mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
perf: reduce tokio worker thread blocking to improve responsiveness on low-core machines
- Switch memdb durability from Full to Batch(100) with 10s flush worker - Offload BlobManager fjall writes to spawn_blocking - Wrap Tantivy commit operations in block_in_place - Flush memdb WAL on graceful shutdown
This commit is contained in:
@@ -32,12 +32,16 @@ impl DatabaseManager {
|
|||||||
let db_path = &DATA_DIR_MANAGER.memdb_dir;
|
let db_path = &DATA_DIR_MANAGER.memdb_dir;
|
||||||
std::fs::create_dir_all(db_path).expect("Failed to create memdb data directory");
|
std::fs::create_dir_all(db_path).expect("Failed to create memdb data directory");
|
||||||
|
|
||||||
let db = MemDb::open_with(db_path, Durability::Full)
|
let db = MemDb::open_with(db_path, Durability::Batch { max_ops: 100 })
|
||||||
.expect("Failed to open memdb database");
|
.expect("Failed to open memdb database");
|
||||||
|
|
||||||
// Start periodic snapshot worker (every 5 minutes)
|
// Start periodic snapshot worker (every 5 minutes)
|
||||||
db.start_snapshot_worker(Duration::from_secs(300));
|
db.start_snapshot_worker(Duration::from_secs(300));
|
||||||
|
|
||||||
|
// Start periodic flush worker (every 10 seconds) so buffered writes
|
||||||
|
// are flushed regularly and not only at the batch threshold.
|
||||||
|
db.start_flush_worker(Duration::from_secs(10));
|
||||||
|
|
||||||
DatabaseManager { db }
|
DatabaseManager { db }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,4 +49,12 @@ impl DatabaseManager {
|
|||||||
pub fn db(&self) -> &MemDb {
|
pub fn db(&self) -> &MemDb {
|
||||||
&self.db
|
&self.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flush any buffered WAL entries to disk. Must be called before shutdown
|
||||||
|
/// to avoid losing writes that haven't hit the batch threshold yet.
|
||||||
|
pub fn flush(&self) {
|
||||||
|
if let Err(e) = self.db.flush() {
|
||||||
|
eprintln!("[memdb] flush error on shutdown: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,9 +149,18 @@ impl BlobManager {
|
|||||||
res = receiver.recv() => {
|
res = receiver.recv() => {
|
||||||
match res {
|
match res {
|
||||||
Some(eml) => {
|
Some(eml) => {
|
||||||
Self::process_detached_email(eml, &email_ks, &attach_ks);
|
let mut batch = vec![eml];
|
||||||
while let Ok(next_eml) = receiver.try_recv() {
|
while let Ok(next_eml) = receiver.try_recv() {
|
||||||
Self::process_detached_email(next_eml, &email_ks, &attach_ks);
|
batch.push(next_eml);
|
||||||
|
}
|
||||||
|
let email_ks = email_ks.clone();
|
||||||
|
let attach_ks = attach_ks.clone();
|
||||||
|
if let Err(e) = tokio::task::spawn_blocking(move || {
|
||||||
|
for eml in batch {
|
||||||
|
Self::process_detached_email(eml, &email_ks, &attach_ks);
|
||||||
|
}
|
||||||
|
}).await {
|
||||||
|
tracing::error!("BlobManager: spawn_blocking join error: {:#?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -162,16 +171,25 @@ impl BlobManager {
|
|||||||
}
|
}
|
||||||
_ = shutdown.recv() => {
|
_ = shutdown.recv() => {
|
||||||
receiver.close();
|
receiver.close();
|
||||||
let remaining = receiver.len();
|
let mut remaining = Vec::new();
|
||||||
|
while let Some(eml) = receiver.recv().await {
|
||||||
|
remaining.push(eml);
|
||||||
|
}
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"BlobManager: Shutdown signal received. Processing {} remaining tasks...",
|
"BlobManager: Shutdown signal received. Processing {} remaining tasks...",
|
||||||
remaining
|
remaining.len()
|
||||||
);
|
);
|
||||||
|
if !remaining.is_empty() {
|
||||||
while let Some(eml) = receiver.recv().await {
|
let email_ks = email_ks.clone();
|
||||||
Self::process_detached_email(eml, &email_ks, &attach_ks);
|
let attach_ks = attach_ks.clone();
|
||||||
|
if let Err(e) = tokio::task::spawn_blocking(move || {
|
||||||
|
for eml in remaining {
|
||||||
|
Self::process_detached_email(eml, &email_ks, &attach_ks);
|
||||||
|
}
|
||||||
|
}).await {
|
||||||
|
tracing::error!("BlobManager: shutdown spawn_blocking join error: {:#?}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("BlobManager: All remaining tasks processed. Closing Fjall.");
|
tracing::info!("BlobManager: All remaining tasks processed. Closing Fjall.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl IndexManager {
|
|||||||
"Tantivy: Reached threshold ({} docs), committing...",
|
"Tantivy: Reached threshold ({} docs), committing...",
|
||||||
pending_count
|
pending_count
|
||||||
);
|
);
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
pending_count = 0;
|
pending_count = 0;
|
||||||
commit_interval.reset();
|
commit_interval.reset();
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ impl IndexManager {
|
|||||||
tracing::info!("Tantivy: Receiver closed. Finalizing...");
|
tracing::info!("Tantivy: Receiver closed. Finalizing...");
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
@@ -172,7 +172,7 @@ impl IndexManager {
|
|||||||
_ = commit_interval.tick() => {
|
_ = commit_interval.tick() => {
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
pending_count = 0;
|
pending_count = 0;
|
||||||
tracing::debug!("Tantivy: Periodic commit finished.");
|
tracing::debug!("Tantivy: Periodic commit finished.");
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ impl IndexManager {
|
|||||||
tracing::info!("Tantivy: Shutdown signal received. Performing final commit...");
|
tracing::info!("Tantivy: Shutdown signal received. Performing final commit...");
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
}
|
}
|
||||||
tracing::info!("Tantivy: Shutdown cleanup complete.");
|
tracing::info!("Tantivy: Shutdown cleanup complete.");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ impl IndexManager {
|
|||||||
"Tantivy: Reached threshold ({} docs), committing...",
|
"Tantivy: Reached threshold ({} docs), committing...",
|
||||||
pending_count
|
pending_count
|
||||||
);
|
);
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
pending_count = 0;
|
pending_count = 0;
|
||||||
commit_interval.reset();
|
commit_interval.reset();
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ impl IndexManager {
|
|||||||
tracing::info!("Tantivy: Receiver closed. Finalizing...");
|
tracing::info!("Tantivy: Receiver closed. Finalizing...");
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
@@ -187,7 +187,7 @@ impl IndexManager {
|
|||||||
_ = commit_interval.tick() => {
|
_ = commit_interval.tick() => {
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
pending_count = 0;
|
pending_count = 0;
|
||||||
tracing::debug!("Tantivy: Periodic commit finished.");
|
tracing::debug!("Tantivy: Periodic commit finished.");
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ impl IndexManager {
|
|||||||
tracing::info!("Tantivy: Shutdown signal received. Performing final commit...");
|
tracing::info!("Tantivy: Shutdown signal received. Performing final commit...");
|
||||||
if pending_count > 0 {
|
if pending_count > 0 {
|
||||||
let mut writer = writer.lock().await;
|
let mut writer = writer.lock().await;
|
||||||
fatal_commit(&mut writer);
|
tokio::task::block_in_place(|| fatal_commit(&mut writer));
|
||||||
}
|
}
|
||||||
tracing::info!("Tantivy: Shutdown cleanup complete.");
|
tracing::info!("Tantivy: Shutdown cleanup complete.");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use bichon_core::{
|
|||||||
cache::imap::task::SYNC_TASKS,
|
cache::imap::task::SYNC_TASKS,
|
||||||
common::{rustls::BichonTls, signal::SignalManager},
|
common::{rustls::BichonTls, signal::SignalManager},
|
||||||
context::{executors::BichonContext, Initialize},
|
context::{executors::BichonContext, Initialize},
|
||||||
|
database::manager::DB_MANAGER,
|
||||||
error::{code::ErrorCode, BichonResult},
|
error::{code::ErrorCode, BichonResult},
|
||||||
logger,
|
logger,
|
||||||
migrate::check_data_status,
|
migrate::check_data_status,
|
||||||
@@ -116,6 +117,7 @@ pub async fn run() -> BichonResult<()> {
|
|||||||
ENVELOPE_MANAGER.shutdown().await;
|
ENVELOPE_MANAGER.shutdown().await;
|
||||||
ATTACHMENT_MANAGER.shutdown().await;
|
ATTACHMENT_MANAGER.shutdown().await;
|
||||||
BLOB_MANAGER.shutdown().await;
|
BLOB_MANAGER.shutdown().await;
|
||||||
|
DB_MANAGER.flush();
|
||||||
info!("Bichon server stopped.");
|
info!("Bichon server stopped.");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user