fix(cache): use point query instead of scan when deleting local cache

This commit is contained in:
rustmailer
2026-08-31 14:44:11 +08:00
parent 7daffa09e0
commit 2250fe995e
6 changed files with 256 additions and 96 deletions
+24 -24
View File
@@ -2,7 +2,7 @@
// Licensed under RustMailer License Agreement v1.0
// Unauthorized copying, modification, or distribution is prohibited.
use std::{collections::HashSet, sync::Arc};
use std::sync::Arc;
use native_db::*;
use native_model::{native_model, Model};
@@ -20,12 +20,13 @@ use crate::{
},
},
database::{
enqueue_delete_secondary_impl, filter_by_secondary_key_impl, manager::DB_MANAGER,
safe_delete::RowFilter,
batch_delete_impl, enqueue_delete_secondary_impl, filter_by_secondary_key_impl,
manager::DB_MANAGER, safe_delete::RowFilter,
},
error::RustMailerResult,
error::{code::ErrorCode, RustMailerResult},
utils::envelope_hash,
},
raise_error,
};
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)]
@@ -102,32 +103,31 @@ impl AddressEntity {
mailbox_id: u64,
to_delete_uid: &[u32],
) -> RustMailerResult<()> {
const BATCH_SIZE: usize = 50;
let to_delete_set: HashSet<u64> = to_delete_uid
let hashes: Vec<u64> = to_delete_uid
.iter()
.map(|uid| envelope_hash(account_id, mailbox_id, *uid))
.collect();
let to_delete_set = Arc::new(to_delete_set);
let filter: RowFilter<AddressEntity> = Arc::new(move |e: &AddressEntity| {
e.account_id == account_id && to_delete_set.contains(&e.envelope_hash)
});
enqueue_delete_secondary_impl(
DB_MANAGER.envelope_db(),
AddressEntityKey::mailbox_id,
mailbox_id,
filter,
BATCH_SIZE,
format!(
"AddressEntity::clean_envelopes account_id={} mailbox_id={}",
account_id, mailbox_id
),
)?;
let deleted = batch_delete_impl(DB_MANAGER.envelope_db(), move |rw| {
let mut to_delete = Vec::new();
for hash in hashes {
let entities: Vec<AddressEntity> = rw
.scan()
.secondary(AddressEntityKey::envelope_hash)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
.start_with(hash)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
.filter_map(Result::ok)
.collect();
to_delete.extend(entities);
}
Ok(to_delete)
})
.await?;
info!(
"Enqueued deletion of address entities for mailbox_id={} account_id={}",
mailbox_id, account_id
"Deleted {} address entities for mailbox_id={} account_id={}",
deleted, mailbox_id, account_id
);
Ok(())
}
+5 -4
View File
@@ -86,6 +86,10 @@ impl EnvelopeFlagsManager {
mailbox_id: u64,
to_delete_uid: &[u32],
) -> RustMailerResult<()> {
EmailEnvelopeV3::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
MinimalEnvelope::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
AddressEntity::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
EmailThread::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
if let Some(mailboxes_map) = FLAGS_STATE_MAP.get(&account_id) {
if let Some(flags_map) = mailboxes_map.get(&mailbox_id) {
for uid in to_delete_uid {
@@ -99,10 +103,7 @@ impl EnvelopeFlagsManager {
FLAGS_STATE_MAP.remove(&account_id);
}
}
EmailEnvelopeV3::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
MinimalEnvelope::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
AddressEntity::clean_envelopes(account_id, mailbox_id, to_delete_uid).await?;
EmailThread::clean_envelopes(account_id, mailbox_id, to_delete_uid).await
Ok(())
}
/// Clean all data associated with a specific mailbox for a given account.
+25 -23
View File
@@ -2,7 +2,7 @@
// Licensed under RustMailer License Agreement v1.0
// Unauthorized copying, modification, or distribution is prohibited.
use std::{collections::HashSet, sync::Arc};
use std::sync::Arc;
use native_db::*;
use native_model::{native_model, Model};
@@ -26,9 +26,9 @@ use crate::{
},
common::Addr,
database::{
enqueue_delete_secondary_impl, filter_by_secondary_key_impl, manager::DB_MANAGER,
paginate_secondary_scan_impl, safe_delete::RowFilter, secondary_find_impl, update_impl,
with_transaction,
batch_delete_impl, enqueue_delete_secondary_impl, filter_by_secondary_key_impl,
manager::DB_MANAGER, paginate_secondary_scan_impl, safe_delete::RowFilter,
secondary_find_impl, update_impl, with_transaction,
},
error::{code::ErrorCode, RustMailerResult},
imap::section::{EmailBodyPart, ImapAttachment},
@@ -475,27 +475,29 @@ impl EmailEnvelopeV3 {
mailbox_id: u64,
to_delete_uid: &[u32],
) -> RustMailerResult<()> {
const BATCH_SIZE: usize = 50;
let to_delete_set: HashSet<u32> = to_delete_uid.iter().copied().collect();
let to_delete_set = Arc::new(to_delete_set);
let filter: RowFilter<EmailEnvelopeV3> = Arc::new(move |e: &EmailEnvelopeV3| {
e.account_id == account_id && to_delete_set.contains(&e.uid)
});
enqueue_delete_secondary_impl(
DB_MANAGER.envelope_db(),
EmailEnvelopeV3Key::mailbox_id,
mailbox_id,
filter,
BATCH_SIZE,
format!(
"EmailEnvelopeV3::clean_envelopes account_id={} mailbox_id={}",
account_id, mailbox_id
),
)?;
let hashes: Vec<u64> = to_delete_uid
.iter()
.map(|uid| envelope_hash(account_id, mailbox_id, *uid))
.collect();
let deleted = batch_delete_impl(DB_MANAGER.envelope_db(), move |rw| {
let mut to_delete = Vec::with_capacity(hashes.len());
for hash in hashes {
if let Some(envelope) = rw
.get()
.secondary::<EmailEnvelopeV3>(EmailEnvelopeV3Key::create_envelope_id, hash)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
{
to_delete.push(envelope);
}
}
Ok(to_delete)
})
.await?;
info!(
"Enqueued deletion of envelopes for account_id={} mailbox_id={}",
account_id, mailbox_id
"Deleted {} envelopes for account_id={} mailbox_id={}",
deleted, account_id, mailbox_id
);
Ok(())
}
+24 -22
View File
@@ -2,7 +2,7 @@
// Licensed under RustMailer License Agreement v1.0
// Unauthorized copying, modification, or distribution is prohibited.
use std::{collections::HashSet, sync::Arc};
use std::sync::Arc;
use native_db::*;
use native_model::{native_model, Model};
@@ -13,8 +13,8 @@ use crate::{
modules::{
cache::imap::{manager::EnvelopeFlagsManager, migration::EmailEnvelopeV3},
database::{
batch_insert_impl, enqueue_delete_secondary_impl, filter_by_secondary_key_impl,
manager::DB_MANAGER, safe_delete::RowFilter, update_impl,
batch_delete_impl, batch_insert_impl, enqueue_delete_secondary_impl,
filter_by_secondary_key_impl, manager::DB_MANAGER, safe_delete::RowFilter, update_impl,
},
error::{code::ErrorCode, RustMailerResult},
utils::envelope_hash,
@@ -95,27 +95,29 @@ impl MinimalEnvelope {
mailbox_id: u64,
to_delete_uid: &[u32],
) -> RustMailerResult<()> {
const BATCH_SIZE: usize = 50;
let to_delete_set: HashSet<u32> = to_delete_uid.iter().copied().collect();
let to_delete_set = Arc::new(to_delete_set);
let filter: RowFilter<MinimalEnvelope> = Arc::new(move |e: &MinimalEnvelope| {
e.account_id == account_id && to_delete_set.contains(&e.uid)
});
enqueue_delete_secondary_impl(
DB_MANAGER.envelope_db(),
MinimalEnvelopeKey::mailbox_id,
mailbox_id,
filter,
BATCH_SIZE,
format!(
"MinimalEnvelope::clean_envelopes account_id={} mailbox_id={}",
account_id, mailbox_id
),
)?;
let hashes: Vec<u64> = to_delete_uid
.iter()
.map(|uid| envelope_hash(account_id, mailbox_id, *uid))
.collect();
let deleted = batch_delete_impl(DB_MANAGER.envelope_db(), move |rw| {
let mut to_delete = Vec::with_capacity(hashes.len());
for hash in hashes {
if let Some(envelope) = rw
.get()
.primary::<MinimalEnvelope>(hash)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
{
to_delete.push(envelope);
}
}
Ok(to_delete)
})
.await?;
info!(
"Enqueued deletion of minimal envelopes for account_id={} mailbox_id={}",
account_id, mailbox_id
"Deleted {} minimal envelopes for account_id={} mailbox_id={}",
deleted, account_id, mailbox_id
);
Ok(())
}
+20 -23
View File
@@ -2,7 +2,7 @@
// Licensed under RustMailer License Agreement v1.0
// Unauthorized copying, modification, or distribution is prohibited.
use std::{collections::HashSet, sync::Arc};
use std::sync::Arc;
use futures::future::join_all;
use native_db::*;
@@ -23,8 +23,8 @@ use crate::{
},
},
database::{
enqueue_delete_secondary_impl, manager::DB_MANAGER, paginate_secondary_scan_impl,
safe_delete::RowFilter,
batch_delete_impl, enqueue_delete_secondary_impl, manager::DB_MANAGER,
paginate_secondary_scan_impl, safe_delete::RowFilter,
},
error::{code::ErrorCode, RustMailerResult},
rest::response::DataPage,
@@ -112,32 +112,29 @@ impl EmailThread {
mailbox_id: u64,
to_delete_uid: &[u32],
) -> RustMailerResult<()> {
const BATCH_SIZE: usize = 50;
let to_delete_set: HashSet<u64> = to_delete_uid
let hashes: Vec<u64> = to_delete_uid
.iter()
.map(|uid| envelope_hash(account_id, mailbox_id, *uid))
.collect();
let to_delete_set = Arc::new(to_delete_set);
let filter: RowFilter<EmailThread> = Arc::new(move |e: &EmailThread| {
e.account_id == account_id && to_delete_set.contains(&e.envelope_id)
});
enqueue_delete_secondary_impl(
DB_MANAGER.envelope_db(),
EmailThreadKey::mailbox_id,
mailbox_id,
filter,
BATCH_SIZE,
format!(
"EmailThread::clean_envelopes account_id={} mailbox_id={}",
account_id, mailbox_id
),
)?;
let deleted = batch_delete_impl(DB_MANAGER.envelope_db(), move |rw| {
let mut to_delete = Vec::with_capacity(hashes.len());
for hash in hashes {
if let Some(thread) = rw
.get()
.secondary::<EmailThread>(EmailThreadKey::envelope_id, hash)
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?
{
to_delete.push(thread);
}
}
Ok(to_delete)
})
.await?;
info!(
"Enqueued deletion of thread entities for mailbox_id={} account_id={}",
mailbox_id, account_id
"Deleted {} thread entities for account_id={} mailbox_id={}",
deleted, account_id, mailbox_id
);
Ok(())
}