sync_engine/
config.rs

1//! Configuration for the sync engine.
2//!
3//! # Example
4//!
5//! ```
6//! use sync_engine::SyncEngineConfig;
7//!
8//! // Minimal config (uses defaults)
9//! let config = SyncEngineConfig::default();
10//! assert_eq!(config.l1_max_bytes, 256 * 1024 * 1024); // 256 MB
11//!
12//! // Full config
13//! let config = SyncEngineConfig {
14//!     redis_url: Some("redis://localhost:6379".into()),
15//!     sql_url: Some("mysql://user:pass@localhost/db".into()),
16//!     l1_max_bytes: 128 * 1024 * 1024, // 128 MB
17//!     batch_flush_count: 100,
18//!     batch_flush_ms: 50,
19//!     ..Default::default()
20//! };
21//! ```
22
23use serde::Deserialize;
24
25/// Configuration for the sync engine.
26///
27/// All fields have sensible defaults. At minimum, you should configure
28/// `redis_url` and `sql_url` for production use.
29#[derive(Debug, Clone, Deserialize)]
30pub struct SyncEngineConfig {
31    /// Redis connection string (e.g., "redis://localhost:6379")
32    #[serde(default)]
33    pub redis_url: Option<String>,
34    
35    /// Redis key prefix for namespacing (e.g., "myapp:" → keys become "myapp:user.alice")
36    /// Allows sync-engine to coexist with other data in the same Redis instance.
37    #[serde(default)]
38    pub redis_prefix: Option<String>,
39    
40    /// SQL connection string (e.g., "sqlite:sync.db" or "mysql://user:pass@host/db")
41    #[serde(default)]
42    pub sql_url: Option<String>,
43    
44    /// L1 cache max size in bytes (default: 256 MB)
45    #[serde(default = "default_l1_max_bytes")]
46    pub l1_max_bytes: usize,
47    
48    /// Maximum payload size in bytes (default: 16 MB)
49    /// 
50    /// Payloads larger than this will be rejected with an error.
51    /// This prevents a single large item (e.g., 1TB file) from exhausting
52    /// the L1 cache. Set to 0 for unlimited (not recommended).
53    /// 
54    /// **Important**: This is a safety limit. Developers should choose a value
55    /// appropriate for their use case. For binary blobs, consider using
56    /// external object storage (S3, GCS) and storing only references here.
57    #[serde(default = "default_max_payload_bytes")]
58    pub max_payload_bytes: usize,
59    
60    /// Backpressure thresholds
61    #[serde(default = "default_backpressure_warn")]
62    pub backpressure_warn: f64,
63    #[serde(default = "default_backpressure_critical")]
64    pub backpressure_critical: f64,
65    
66    /// Batch flush settings
67    #[serde(default = "default_batch_flush_ms")]
68    pub batch_flush_ms: u64,
69    #[serde(default = "default_batch_flush_count")]
70    pub batch_flush_count: usize,
71    #[serde(default = "default_batch_flush_bytes")]
72    pub batch_flush_bytes: usize,
73    
74    /// Cuckoo filter warmup
75    #[serde(default = "default_cuckoo_warmup_batch_size")]
76    pub cuckoo_warmup_batch_size: usize,
77    
78    /// WAL path (SQLite file for durability during MySQL outages)
79    #[serde(default)]
80    pub wal_path: Option<String>,
81    
82    /// WAL max items before backpressure
83    #[serde(default)]
84    pub wal_max_items: Option<u64>,
85    
86    /// WAL drain batch size
87    #[serde(default = "default_wal_drain_batch_size")]
88    pub wal_drain_batch_size: usize,
89    
90    /// CF snapshot interval in seconds (0 = disabled)
91    #[serde(default = "default_cf_snapshot_interval_secs")]
92    pub cf_snapshot_interval_secs: u64,
93    
94    /// CF snapshot after N inserts (0 = disabled)
95    #[serde(default = "default_cf_snapshot_insert_threshold")]
96    pub cf_snapshot_insert_threshold: u64,
97    
98    /// Redis eviction: enable proactive eviction before Redis LRU kicks in
99    #[serde(default = "default_redis_eviction_enabled")]
100    pub redis_eviction_enabled: bool,
101    
102    /// Redis eviction: pressure threshold to start evicting (0.0-1.0, default: 0.75)
103    #[serde(default = "default_redis_eviction_start")]
104    pub redis_eviction_start: f64,
105    
106    /// Redis eviction: target pressure after eviction (0.0-1.0, default: 0.60)
107    #[serde(default = "default_redis_eviction_target")]
108    pub redis_eviction_target: f64,
109    
110    /// Merkle calculation: enable merkle tree updates on this instance.
111    /// 
112    /// In a multi-instance deployment with shared SQL, only a few nodes need to
113    /// run merkle calculations for resilience. Set to false on most nodes.
114    /// Default: true (single-instance default)
115    #[serde(default = "default_merkle_calc_enabled")]
116    pub merkle_calc_enabled: bool,
117    
118    /// Merkle calculation: jitter range in milliseconds.
119    /// 
120    /// Adds random delay (0 to N ms) before merkle batch calculation to reduce
121    /// contention when multiple instances are calculating. Default: 0 (no jitter)
122    #[serde(default)]
123    pub merkle_calc_jitter_ms: u64,
124}
125
126fn default_l1_max_bytes() -> usize { 256 * 1024 * 1024 } // 256 MB
127fn default_max_payload_bytes() -> usize { 16 * 1024 * 1024 } // 16 MB
128fn default_backpressure_warn() -> f64 { 0.7 }
129fn default_backpressure_critical() -> f64 { 0.9 }
130fn default_batch_flush_ms() -> u64 { 100 }
131fn default_batch_flush_count() -> usize { 1000 }
132fn default_batch_flush_bytes() -> usize { 1024 * 1024 } // 1 MB
133fn default_cuckoo_warmup_batch_size() -> usize { 10000 }
134fn default_wal_drain_batch_size() -> usize { 100 }
135fn default_cf_snapshot_interval_secs() -> u64 { 30 }
136fn default_cf_snapshot_insert_threshold() -> u64 { 10_000 }
137fn default_redis_eviction_enabled() -> bool { true }
138fn default_redis_eviction_start() -> f64 { 0.75 }
139fn default_redis_eviction_target() -> f64 { 0.60 }
140fn default_merkle_calc_enabled() -> bool { true }
141
142impl Default for SyncEngineConfig {
143    fn default() -> Self {
144        Self {
145            redis_url: None,
146            sql_url: None,
147            redis_prefix: None,
148            l1_max_bytes: default_l1_max_bytes(),
149            max_payload_bytes: default_max_payload_bytes(),
150            backpressure_warn: default_backpressure_warn(),
151            backpressure_critical: default_backpressure_critical(),
152            batch_flush_ms: default_batch_flush_ms(),
153            batch_flush_count: default_batch_flush_count(),
154            batch_flush_bytes: default_batch_flush_bytes(),
155            cuckoo_warmup_batch_size: default_cuckoo_warmup_batch_size(),
156            wal_path: None,
157            wal_max_items: None,
158            wal_drain_batch_size: default_wal_drain_batch_size(),
159            cf_snapshot_interval_secs: default_cf_snapshot_interval_secs(),
160            cf_snapshot_insert_threshold: default_cf_snapshot_insert_threshold(),
161            redis_eviction_enabled: default_redis_eviction_enabled(),
162            redis_eviction_start: default_redis_eviction_start(),
163            redis_eviction_target: default_redis_eviction_target(),
164            merkle_calc_enabled: default_merkle_calc_enabled(),
165            merkle_calc_jitter_ms: 0,
166        }
167    }
168}