1mod admission;
13mod arrow;
14mod encoding;
15mod lifecycle;
16mod recovery;
17mod shutdown;
18mod state;
19mod status;
20mod writes;
21
22pub use encoding::encoded_size;
25pub(crate) use encoding::recovery_encoded_bound;
26
27use std::collections::BTreeMap;
31use std::io::{self, Write};
32use std::marker::PhantomData;
33use std::sync::{Arc, Mutex};
34
35use serde::Serialize;
37use tokio::sync::watch;
38
39use super::admissions::Mutation;
41use super::capacity::{Attempt, Refusal};
42use super::config::{IO_TIMEOUT, MUTATION_RETENTION};
43use super::queue::PendingQueue;
44use super::receipts::{MutationValue, ReceiptState};
45use super::state::{RecoveryEntry, RecoveryPage, RecoverySnapshot, StateBackend};
46use super::worker::Worker;
47use super::{
48 ReadLimits, RecoveryPosition, SchemaKey, StorageBackend, StorageBackends, StorageBatch, StorageCursor, StorageFuture, StorageSchema,
49 StorageSettings, StoredRecord, StoredRow,
50};
51use crate::execution::Execution;
52use crate::{Error, Identifier, Result};
53use harmos_domain::model::storage::{RecordInput, RecordWrite, StreamInput};
54
55const WORKER_NAME: &str = "harmos-storage";
58
59#[derive(Clone, Debug)]
66pub struct StorageMetrics {
67 pub accepted: StorageCursor,
68 pub saved: StorageCursor,
69 pub recovery_accepted: RecoveryPosition,
70 pub recovery_saved: RecoveryPosition,
71 pub pending_bytes: usize,
72
73 pub pending_batches: usize,
74 pub running: bool,
75 pub closing: bool,
76 pub closed: bool,
77 pub failure: Option<Arc<Error>>,
78}
79
80pub struct StorageManager<C = ()> {
81 pub(super) inner: Arc<Inner>,
82 catalog: PhantomData<fn() -> C>,
83}
84
85pub(super) struct Inner {
86 pub(super) execution: Execution,
87 pub(super) measurement_admission: tokio::sync::Mutex<()>,
88 pub(super) control_admission: tokio::sync::Mutex<()>,
89 measurement_waiting: std::sync::atomic::AtomicUsize,
90 control_waiting: std::sync::atomic::AtomicUsize,
91 pub(super) object_writes: tokio::sync::Mutex<()>,
92 pub(super) query_active: Arc<tokio::sync::Semaphore>,
93 pub(super) query_pending: Arc<tokio::sync::Semaphore>,
94 pub(super) settings: StorageSettings,
95 pub(super) primary: Arc<dyn StorageBackend>,
96 pub(super) recovery: Arc<dyn StateBackend>,
97 pub(super) has_objects: bool,
98 pub(super) object_definition: fn(&SchemaKey) -> harmos_domain::Result<harmos_domain::model::storage::catalog::ObjectDefinition>,
99 pub(super) object_retention: fn(&SchemaKey) -> harmos_domain::Result<super::ObjectRetention>,
100 pub(super) state: Mutex<State>,
101 pub(super) changed: watch::Sender<u64>,
102 pub(super) lifecycle: tokio::sync::RwLock<Lifecycle>,
103}
104
105#[derive(Default)]
106pub(super) struct Lifecycle {
107 startup_complete: bool,
108 primary_touched: bool,
109 recovery_touched: bool,
110 primary_open: bool,
111 files_recovered: bool,
112 receipts_recovered: bool,
113 recovery_open: bool,
114 primary_closed: bool,
115 recovery_closed: bool,
116 owner: Option<Box<dyn WorkerOwner>>,
117}
118
119pub(super) struct State {
120 pub(super) execution: Execution,
121 prefer_measurement: bool,
122 pub(super) journal_required_data: StorageCursor,
123 pub(super) reserved: usize,
124 pub(super) reserved_batches: usize,
125 pub(super) journal_limit: usize,
126 pub(super) journal_saved: Option<watch::Sender<harmos_domain::state::Position>>,
127 pub(super) status: StorageMetrics,
128 pub(super) ready: bool,
129 service_owner: Option<Arc<()>>,
130 pub(super) next: u64,
131 pub(super) queue: PendingQueue,
132
133 pub(super) data_attempted: u64,
134 pub(super) objects_pruned: u64,
135 pub(super) objects_prune_owed: bool,
136 pub(super) pruning_objects: bool,
137 pub(super) recovery_written: Target,
138 pub(super) data_accepted: Target,
139 pub(super) recovery_accepted: Target,
140 pub(super) data_flushed: u64,
141 pub(super) recovery_flushed: u64,
142 pub(super) data_request: Option<Target>,
143 pub(super) recovery_request: Option<Target>,
144 pub(super) retry: u64,
145 pub(super) snapshots: BTreeMap<u64, super::snapshot::SnapshotState>,
146 pub(super) followers: BTreeMap<u64, super::follow::Following>,
147 pub(super) files: BTreeMap<u64, super::staging::FileState>,
148 pub(super) receipts: super::receipts::Receipts,
149 pub(super) deletions: BTreeMap<super::MutationId, super::deletion::Deletion>,
150 pub(super) file_cursor: u64,
151 pub(super) finishing: Option<u64>,
152}
153
154#[derive(Clone, Copy, Debug, Default)]
155pub(super) struct Target {
156 pub(super) index: u64,
157 pub(super) sequence: u64,
158}
159
160pub(super) struct Pending {
161 pub(super) context: crate::execution::Context,
162 pub(super) queued: Mutex<Option<tracing::Span>>,
163 pub(super) journal: Option<harmos_domain::state::Position>,
164 pub(super) index: u64,
165 pub(super) bytes: usize,
166 pub(super) operation: Operation,
167}
168
169struct Counter(usize);
170
171struct Admission {
172 inner: Arc<Inner>,
173 active: bool,
174}
175
176struct WriteWaiter {
177 inner: Arc<Inner>,
178 measurement: bool,
179}
180
181enum Admitted {
184 Replayed(u64),
185 Reserved(u64, u64, u64),
186}
187
188pub enum ErasedCatalog {}
189
190#[derive(Clone, Copy, PartialEq, Eq)]
191pub(super) enum Domain {
192 Data,
193 Recovery,
194}
195
196pub(super) enum Operation {
197 Schema(StorageBatch<StorageSchema>),
198 File(StorageBatch<super::FileReference>),
199 Records(StorageBatch<StoredRecord>),
200 ObjectGroup { schemas: StorageBatch<StorageSchema>, objects: StorageBatch<super::ObjectEntry>, bytes: usize },
201 Stream(StorageBatch<StoredRow>),
202 Recovery(StorageBatch<RecoveryEntry>),
203 Snapshot(StorageBatch<RecoverySnapshot>),
204}
205
206pub(crate) trait RecordWriteBytes {
209 fn encoded_bytes(&self, history: &super::StoreHistory) -> Result<usize>;
210}
211
212pub trait WorkerOwner: Send + Sync {
213 fn stop(&self) -> StorageFuture<'_, ()>;
214}
215
216impl Pending {
219 pub(super) fn new(
220 _inner: &Arc<Inner>, journal: Option<harmos_domain::state::Position>, index: u64, bytes: usize, operation: Operation,
221 ) -> Self {
222 Self {
223 context: crate::execution::Context::current(),
224 queued: Mutex::new(Some(tracing::debug_span!("storage.queue"))),
225 journal,
226 index,
227 bytes,
228 operation,
229 }
230 }
231}
232
233impl<C> Clone for StorageManager<C> {
234 fn clone(&self) -> Self {
235 Self {
236 inner: self.inner.clone(),
237 catalog: PhantomData,
238 }
239 }
240}
241
242impl<C> StorageManager<C> {
243 pub fn erased(&self) -> StorageManager<ErasedCatalog> {
244 StorageManager {
245 inner: self.inner.clone(),
246 catalog: PhantomData,
247 }
248 }
249}
250
251impl<C: super::ObjectCatalog> StorageManager<C> {
252 pub fn new(backends: StorageBackends, settings: StorageSettings) -> Result<Self> {
253 Self::with_execution(backends, settings, Execution::default())
254 }
255
256 #[tracing::instrument(name = "storage.manager.new", level = "debug", skip_all)]
257 pub fn with_execution(backends: StorageBackends, settings: StorageSettings, execution: Execution) -> Result<Self> {
258 C::validate()?;
259 settings.validate()?;
260 let StorageBackends { primary, state: state_backend } = backends;
261 primary.history().check(state_backend.history())?;
262 let history = primary.history().clone();
263 let position = StorageCursor { history: history.clone(), sequence: 0 };
264 let recovery_position = RecoveryPosition { history, sequence: 0 };
265 let receipts = super::receipts::Receipts::new(MUTATION_RETENTION, execution)?;
266 Ok(Self {
267 catalog: PhantomData,
268 inner: Arc::new(Inner {
269 execution,
270 measurement_admission: tokio::sync::Mutex::new(()),
271 control_admission: tokio::sync::Mutex::new(()),
272 measurement_waiting: std::sync::atomic::AtomicUsize::new(0),
273 control_waiting: std::sync::atomic::AtomicUsize::new(0),
274 object_writes: tokio::sync::Mutex::new(()),
275 query_active: Arc::new(tokio::sync::Semaphore::new(settings.request.max_active_queries.get())),
276 query_pending: Arc::new(tokio::sync::Semaphore::new(settings.request.max_pending_queries.get())),
277 settings,
278 primary,
279 recovery: state_backend,
280 has_objects: C::definition_count() != 0,
281 object_definition: harmos_domain::model::storage::catalog::definition::<C>,
282 object_retention: |key| Ok(harmos_domain::model::storage::catalog::definition::<C>(key)?.retention()),
283 state: Mutex::new(State {
284 execution,
285 prefer_measurement: false,
286 journal_required_data: position.clone(),
287 reserved: 0,
288 reserved_batches: 0,
289 journal_limit: 0,
290 journal_saved: None,
291 status: StorageMetrics {
292 accepted: position.clone(),
293 saved: position,
294 recovery_accepted: recovery_position.clone(),
295 recovery_saved: recovery_position,
296 pending_bytes: 0,
297
298 pending_batches: 0,
299 running: false,
300 closing: false,
301 closed: false,
302 failure: None,
303 },
304 ready: false,
305 service_owner: None,
306 next: 0,
307 queue: PendingQueue::default(),
308
309 data_attempted: 0,
310 objects_pruned: 0,
311 objects_prune_owed: false,
312 pruning_objects: false,
313 recovery_written: Target::default(),
314 data_accepted: Target::default(),
315 recovery_accepted: Target::default(),
316 data_flushed: 0,
317 recovery_flushed: 0,
318 data_request: None,
319 recovery_request: None,
320 retry: 0,
321 snapshots: BTreeMap::new(),
322 followers: BTreeMap::new(),
323 files: BTreeMap::new(),
324 deletions: BTreeMap::new(),
325 receipts,
326 file_cursor: 0,
327 finishing: None,
328 }),
329 changed: watch::channel(0).0,
330 lifecycle: tokio::sync::RwLock::new(Lifecycle::default()),
331 }),
332 })
333 }
334}
335
336