1use std::num::NonZeroUsize;
20use std::time::Duration;
21
22use serde::{Deserialize, Serialize};
24
25pub(super) const FLUSH_INTERVAL: Duration = Duration::from_millis(100);
31pub(super) const MAX_BATCH_BYTES: usize = 16 * 1024 * 1024;
32pub(super) const TARGET_BATCH_BYTES: usize = 1024 * 1024;
33pub(super) const OBJECT_PRUNE_ENTRIES: NonZeroUsize = NonZeroUsize::new(256).unwrap();
34pub(super) const SNAPSHOT_BYTES: usize = 1024 * 1024;
35
36pub(super) const IO_TIMEOUT: Duration = Duration::from_secs(30);
38pub(super) const RECOVERY_TIMEOUT: Duration = Duration::from_secs(300);
39pub(super) const MAX_RETRIES: usize = 3;
40pub(super) const RETRY_BASE: Duration = Duration::from_millis(10);
41pub(super) const RETRY_CAP: Duration = Duration::from_secs(1);
42
43pub(super) const MAX_FILES: NonZeroUsize = NonZeroUsize::new(256).unwrap();
45pub(super) const FILE_SESSION_TIMEOUT: Duration = Duration::from_secs(60);
46pub(super) const MAX_MUTATIONS: NonZeroUsize = NonZeroUsize::new(1024).unwrap();
47pub(super) const MUTATION_RETENTION: Duration = Duration::from_secs(300);
48pub(super) const SCAN_ROWS: NonZeroUsize = NonZeroUsize::new(16_384).unwrap();
53pub(super) const SCAN_BYTES: usize = 8 * 1024 * 1024;
54
55#[derive(Clone, Debug, Serialize, Deserialize)]
62#[serde(deny_unknown_fields)]
63pub struct StorageSettings {
64 pub request: StorageRequestSettings,
65 pub response: StorageResponseSettings,
66 pub filesystem: StorageFilesystemSettings,
67}
68
69#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
70#[serde(deny_unknown_fields)]
71pub struct StorageFilesystemSettings {
72 pub poll_interval: Duration,
73 pub max_entries: NonZeroUsize,
74 pub event_capacity: NonZeroUsize,
75}
76
77#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
78#[serde(deny_unknown_fields)]
79pub struct StorageRequestSettings {
80 pub max_pending_queries: NonZeroUsize,
81 pub max_active_queries: NonZeroUsize,
82 pub max_pending_write_batches: NonZeroUsize,
83 pub max_active_file_operations: NonZeroUsize,
84 pub max_pending_write_bytes: NonZeroUsize,
85}
86
87#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
88#[serde(deny_unknown_fields)]
89pub struct StorageResponseSettings {
90 pub batch_rows: NonZeroUsize,
91 pub batch_bytes: NonZeroUsize,
92}
93
94impl StorageSettings {
101 pub fn validate(&self) -> crate::Result<()> {
102 if self.filesystem.poll_interval.is_zero() || self.filesystem.event_capacity.get() > tokio::sync::Semaphore::MAX_PERMITS {
103 return Err(crate::Error::InvalidInput {
104 field: "storage filesystem",
105 reason: "poll interval must be positive and event capacity bounded",
106 });
107 }
108
109 if self.request.max_active_queries.get() > tokio::sync::Semaphore::MAX_PERMITS
110 || self.request.max_pending_queries.get() > tokio::sync::Semaphore::MAX_PERMITS
111 {
112 return Err(crate::Error::InvalidInput {
113 field: "storage query admission",
114 reason: "query counts exceed semaphore capacity",
115 });
116 }
117 Ok(())
118 }
119
120 pub fn max_batch_bytes(&self) -> NonZeroUsize {
122 self.request.max_pending_write_bytes.min(NonZeroUsize::new(MAX_BATCH_BYTES).unwrap())
123 }
124
125 pub(super) fn target_batch_bytes(&self) -> usize {
126 TARGET_BATCH_BYTES.min(self.max_batch_bytes().get())
127 }
128
129 pub(super) fn scan_bytes(&self) -> NonZeroUsize {
130 NonZeroUsize::new(SCAN_BYTES).unwrap()
131 }
132}
133
134#[cfg(test)]
141mod tests {
142 use super::{StorageRequestSettings, StorageResponseSettings, StorageSettings};
143 use std::num::NonZeroUsize;
144
145 fn settings() -> StorageSettings {
146 StorageSettings {
147 filesystem: crate::config::StorageFilesystemSettings {
148 poll_interval: std::time::Duration::from_millis(100),
149 max_entries: std::num::NonZeroUsize::new(100_000).unwrap(),
150 event_capacity: std::num::NonZeroUsize::new(1024).unwrap(),
151 },
152
153 request: StorageRequestSettings {
154 max_pending_queries: NonZeroUsize::new(64).unwrap(),
155 max_active_queries: NonZeroUsize::new(4).unwrap(),
156 max_pending_write_batches: NonZeroUsize::new(256).unwrap(),
157 max_active_file_operations: NonZeroUsize::MIN,
158 max_pending_write_bytes: NonZeroUsize::new(16 * 1024 * 1024).unwrap(),
159 },
160 response: StorageResponseSettings {
161 batch_rows: NonZeroUsize::new(64).unwrap(),
162 batch_bytes: NonZeroUsize::new(64 * 1024).unwrap(),
163 },
164 }
165 }
166
167 #[test]
168 fn query_counts_bounded() {
169 let mut settings = settings();
170 settings.request.max_active_queries = NonZeroUsize::new(tokio::sync::Semaphore::MAX_PERMITS + 1).unwrap();
171 assert!(settings.validate().is_err());
172
173 settings.request.max_active_queries = NonZeroUsize::MIN;
174 settings.request.max_pending_queries = NonZeroUsize::MIN;
175 settings.validate().unwrap();
176 }
177}