Database schema optimization - part 13

This commit is contained in:
mdecimus
2025-11-22 10:55:54 +02:00
parent dbe40829da
commit c7fc16d9a2
60 changed files with 2118 additions and 1249 deletions

View File

@@ -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)

View File

@@ -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!())?;

View File

@@ -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,

View File

@@ -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",