fix
This commit is contained in:
@@ -248,6 +248,12 @@ pub struct StorageConfig {
|
||||
/// quota fresh for all mutations without recomputing on the request path.
|
||||
/// Default: 600 (10 min). Env: `OXICLOUD_STORAGE_USAGE_RECONCILE_SECS`.
|
||||
pub usage_reconcile_secs: u64,
|
||||
/// Interval (milliseconds) of the background job that drains
|
||||
/// `storage.tree_etag_dirty` and bumps folder `tree_modified_at`
|
||||
/// (collection ETags). Write paths only enqueue — this is the upper
|
||||
/// bound on how stale an ancestor folder's ETag can be after a change.
|
||||
/// Default: 500. Env: `OXICLOUD_TREE_ETAG_FLUSH_MS`.
|
||||
pub tree_etag_flush_ms: u64,
|
||||
/// Which blob storage backend to use (`local`, `s3`, or `azure`).
|
||||
pub backend: StorageBackendType,
|
||||
/// S3-compatible backend configuration (used when `backend == S3`).
|
||||
@@ -390,6 +396,7 @@ impl Default for StorageConfig {
|
||||
upload_temp_dir: None,
|
||||
chunk_dir: None,
|
||||
usage_reconcile_secs: 600, // 10 minutes
|
||||
tree_etag_flush_ms: 500,
|
||||
backend: StorageBackendType::Local,
|
||||
s3: None,
|
||||
azure: None,
|
||||
@@ -1291,6 +1298,13 @@ impl AppConfig {
|
||||
config.storage.usage_reconcile_secs = val;
|
||||
}
|
||||
|
||||
// Tree-ETag dirty-queue flush cadence
|
||||
if let Ok(ms) = env::var("OXICLOUD_TREE_ETAG_FLUSH_MS").map(|v| v.parse::<u64>())
|
||||
&& let Ok(val) = ms
|
||||
{
|
||||
config.storage.tree_etag_flush_ms = val;
|
||||
}
|
||||
|
||||
// Storage backend selection
|
||||
if let Ok(backend) = env::var("OXICLOUD_STORAGE_BACKEND") {
|
||||
match backend.to_lowercase().as_str() {
|
||||
|
||||
@@ -657,6 +657,25 @@ impl AppServiceFactory {
|
||||
service
|
||||
}
|
||||
|
||||
/// Starts the tree-ETag flush job (requires database).
|
||||
///
|
||||
/// The statement triggers on `storage.files`/`storage.folders` only
|
||||
/// enqueue bump requests into `storage.tree_etag_dirty` (so user-facing
|
||||
/// writes take no folder-row locks); this job is the single drainer that
|
||||
/// turns them into `tree_modified_at` updates. It must run whenever the
|
||||
/// database is up — the triggers are always installed, and an undrained
|
||||
/// queue grows unboundedly while folder ETags freeze. Fire-and-forget on
|
||||
/// the maintenance pool, like the trash cleanup job.
|
||||
fn start_tree_etag_flush_job(&self, maintenance_pool: &Arc<PgPool>) {
|
||||
let service =
|
||||
crate::infrastructure::services::tree_etag_flush_service::TreeEtagFlushService::new(
|
||||
maintenance_pool.clone(),
|
||||
self.config.storage.tree_etag_flush_ms,
|
||||
);
|
||||
service.start_flush_job();
|
||||
tracing::info!("Tree-ETag flush service initialized");
|
||||
}
|
||||
|
||||
/// Builds the complete AppState using all factory services.
|
||||
///
|
||||
/// This is the main entry point that replaces all manual logic in `main.rs`.
|
||||
@@ -745,6 +764,8 @@ impl AppServiceFactory {
|
||||
storage_usage_service =
|
||||
Some(self.create_storage_usage_service(&repos, &pool, &maintenance_pool));
|
||||
|
||||
self.start_tree_etag_flush_job(&maintenance_pool);
|
||||
|
||||
// User-lifecycle dispatcher. Hook order is registration order;
|
||||
// document dependencies inline if/when any arise. Today:
|
||||
// 1. AuditLifecycleHook — fires first so the
|
||||
|
||||
Reference in New Issue
Block a user