Database schema optimization - part 13
This commit is contained in:
@@ -11,13 +11,8 @@ use email::{
|
||||
mailbox::INBOX_ID,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use store::{
|
||||
IterateParams, U32_LEN, ValueKey,
|
||||
ahash::AHashMap,
|
||||
write::{IndexPropertyClass, ValueClass, key::DeserializeBigEndian},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{collection::Collection, field::EmailField, special_use::SpecialUse};
|
||||
use types::special_use::SpecialUse;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mailbox {
|
||||
@@ -53,44 +48,6 @@ impl<T: SessionStream> Session<T> {
|
||||
.map(|x| x.uid_validity)
|
||||
.unwrap_or_default();
|
||||
|
||||
// Obtain message sizes
|
||||
let mut message_sizes = AHashMap::new();
|
||||
self.server
|
||||
.store()
|
||||
.iterate(
|
||||
IterateParams::new(
|
||||
ValueKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: 0,
|
||||
class: ValueClass::IndexProperty(IndexPropertyClass::Integer {
|
||||
property: EmailField::ReceivedToSize.into(),
|
||||
value: 0,
|
||||
}),
|
||||
},
|
||||
ValueKey {
|
||||
account_id,
|
||||
collection: Collection::Email.into(),
|
||||
document_id: u32::MAX,
|
||||
class: ValueClass::IndexProperty(IndexPropertyClass::Integer {
|
||||
property: EmailField::ReceivedToSize.into(),
|
||||
value: u64::MAX,
|
||||
}),
|
||||
},
|
||||
)
|
||||
.ascending(),
|
||||
|key, value| {
|
||||
message_sizes.insert(
|
||||
key.deserialize_be_u32(key.len() - U32_LEN)?,
|
||||
value.deserialize_be_u32(0)?,
|
||||
);
|
||||
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
// Sort by UID
|
||||
let message_map = cache
|
||||
.emails
|
||||
@@ -101,9 +58,9 @@ impl<T: SessionStream> Session<T> {
|
||||
.mailboxes
|
||||
.iter()
|
||||
.find(|m| m.mailbox_id == INBOX_ID)
|
||||
.map(|m| (m.uid, message.document_id))
|
||||
.map(|m| (m.uid, (message.document_id, message.size)))
|
||||
})
|
||||
.collect::<BTreeMap<u32, u32>>();
|
||||
.collect::<BTreeMap<u32, (u32, u32)>>();
|
||||
|
||||
// Create mailbox
|
||||
let mut mailbox = Mailbox {
|
||||
@@ -112,17 +69,15 @@ impl<T: SessionStream> Session<T> {
|
||||
account_id,
|
||||
..Default::default()
|
||||
};
|
||||
for (uid, id) in message_map {
|
||||
if let Some(size) = message_sizes.get(&id) {
|
||||
mailbox.messages.push(Message {
|
||||
id,
|
||||
uid,
|
||||
size: *size,
|
||||
deleted: false,
|
||||
});
|
||||
mailbox.total += 1;
|
||||
mailbox.size += *size;
|
||||
}
|
||||
for (uid, (id, size)) in message_map {
|
||||
mailbox.messages.push(Message {
|
||||
id,
|
||||
uid,
|
||||
size,
|
||||
deleted: false,
|
||||
});
|
||||
mailbox.total += 1;
|
||||
mailbox.size += size;
|
||||
}
|
||||
|
||||
Ok(mailbox)
|
||||
|
||||
@@ -89,7 +89,12 @@ impl<T: SessionStream> Session<T> {
|
||||
let mut batch = BatchBuilder::new();
|
||||
let not_deleted = self
|
||||
.server
|
||||
.emails_delete(mailbox.account_id, &mut batch, deleted)
|
||||
.emails_delete(
|
||||
mailbox.account_id,
|
||||
self.state.access_token().tenant_id(),
|
||||
&mut batch,
|
||||
deleted,
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use email::message::metadata::MessageMetadata;
|
||||
use std::time::Instant;
|
||||
use trc::AddContext;
|
||||
use types::{collection::Collection, field::EmailField};
|
||||
use utils::chained_bytes::ChainedBytes;
|
||||
|
||||
impl<T: SessionStream> Session<T> {
|
||||
pub async fn handle_fetch(&mut self, msg: u32, lines: Option<u32>) -> trc::Result<()> {
|
||||
@@ -50,6 +51,14 @@ impl<T: SessionStream> Session<T> {
|
||||
Elapsed = op_start.elapsed()
|
||||
);
|
||||
|
||||
let bytes = ChainedBytes::new(metadata.raw_headers.as_ref())
|
||||
.with_last(
|
||||
bytes
|
||||
.get(metadata.blob_body_offset.to_native() as usize..)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.get_full_range();
|
||||
|
||||
self.write_bytes(
|
||||
Response::Message::<u32> {
|
||||
bytes,
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
|
||||
use super::Mechanism;
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
use utils::chained_bytes::SliceRange;
|
||||
|
||||
pub enum Response<T> {
|
||||
pub enum Response<'x, T> {
|
||||
Ok(Cow<'static, str>),
|
||||
Err(Cow<'static, str>),
|
||||
List(Vec<T>),
|
||||
Message {
|
||||
bytes: Vec<u8>,
|
||||
bytes: SliceRange<'x>,
|
||||
lines: u32,
|
||||
},
|
||||
Capability {
|
||||
@@ -22,7 +22,7 @@ pub enum Response<T> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: Display> Response<T> {
|
||||
impl<'x, T: Display> Response<'x, T> {
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Response::Ok(message) => {
|
||||
@@ -61,7 +61,7 @@ impl<T: Display> Response<T> {
|
||||
let mut last_byte = 0;
|
||||
|
||||
// Transparency procedure
|
||||
for &byte in bytes {
|
||||
for &byte in bytes.into_iter() {
|
||||
// POP3 requires that lines end with CRLF, do this check to ensure that
|
||||
if byte == b'\n' && last_byte != b'\r' {
|
||||
buf.push(b'\r');
|
||||
@@ -165,10 +165,9 @@ impl SerializeResponse for trc::Error {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::protocol::Mechanism;
|
||||
|
||||
use super::Response;
|
||||
use crate::protocol::Mechanism;
|
||||
use utils::chained_bytes::SliceRange;
|
||||
|
||||
#[test]
|
||||
fn serialize_response() {
|
||||
@@ -206,9 +205,7 @@ mod tests {
|
||||
),
|
||||
(
|
||||
Response::Message {
|
||||
bytes: "Subject: test\r\n\r\n.\r\ntest.\r\n.test\r\na"
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
bytes: SliceRange::Split(b"Subject: test\r\n\r\n.\r\n", b"test.\r\n.test\r\na"),
|
||||
lines: 0,
|
||||
},
|
||||
"+OK 35 octets\r\nSubject: test\r\n\r\n..\r\ntest.\r\n..test\r\na\r\n.\r\n",
|
||||
|
||||
Reference in New Issue
Block a user