harmos_storage/manager/
status.rs1use super::*;
22
23impl<C> StorageManager<C> {
38 #[tracing::instrument(name = "storage.manager.status.wait_saved_after", level = "debug", skip_all, fields(after = cursor.sequence, saved = tracing::field::Empty, outcome = "cancelled"))]
44 pub async fn wait_saved_after(&self, cursor: &StorageCursor) -> Result<StorageCursor> {
45 let result = async {
46 self.inner.primary.history().check(&cursor.history)?;
47 self.inner
48 .wait(None, |state| {
49 if state.status.saved.sequence > cursor.sequence {
50 return crate::capacity::Attempt::Ready(state.status.saved.clone());
51 }
52 let error = if state.status.closing || state.status.closed {
53 Some(Error::Closing { domain: "storage" })
54 } else if let Some(source) = &state.status.failure {
55 Some(Error::Persistence { source: Arc::clone(source) })
56 } else if !state.ready || !state.status.running {
57 Some(Error::NotReady { domain: "storage" })
58 } else {
59 None
60 };
61 match error {
62 Some(error) => crate::capacity::Attempt::Refuse(Refusal::Failed(error)),
63 None => crate::capacity::Attempt::Wait,
64 }
65 })
66 .await
67 .map_err(Refusal::into_error)
68 }
69 .await;
70 if let Ok(saved) = &result {
71 tracing::Span::current().record("saved", saved.sequence);
72 }
73 tracing::Span::current().record("outcome", if result.is_ok() { "completed" } else { "failed" });
74 result
75 }
76
77 pub fn check_state_dependency(&self, required: &StorageCursor) -> Result<()> {
78 if !self.cursor_saved().covers(required)? {
79 return Err(Error::InvalidInput { field: "state evidence dependency", reason: "required primary evidence is not durable" });
80 }
81 Ok(())
82 }
83
84 #[tracing::instrument(name = "storage.manager.status.retry_transient_persistence", level = "trace", skip_all)]
85 pub fn retry_transient_persistence(&self) -> bool {
86 let mut state = self.inner.state.lock().unwrap();
87 let Some(failure) = &state.status.failure else {
88 return false;
89 };
90 let mut cause = failure.as_ref();
91 while let Error::Persistence { source } = cause {
92 cause = source.as_ref();
93 }
94 if !matches!(cause, Error::Backend { kind: crate::BackendFailure::Transient, .. }) {
95 return false;
96 }
97 state.status.failure = None;
98 state.retry = state.retry.wrapping_add(1);
99 drop(state);
100 self.inner.notify();
101 true
102 }
103
104 #[tracing::instrument(name = "storage.manager.status.reserve_arrow_memory", level = "trace", skip_all, fields(bytes = bytes, outcome = "cancelled"))]
105 pub fn reserve_arrow_memory(&self, bytes: usize) -> Result<RetainedMemory> {
106 let result: Result<RetainedMemory> =
107 Reservation::try_new(&self.inner, Pool::Read, &Held::default(), bytes).map(Reservation::retain).map_err(Refusal::into_error);
108 tracing::Span::current().record("outcome", if result.is_ok() { "completed" } else { "failed" });
109 result
110 }
111
112 pub fn write_receipt_capacity(&self) -> usize {
113 self.inner.settings.request.max_pending_write_batches.get()
114 }
115
116 #[tracing::instrument(name = "storage.manager.status.await_durable", level = "debug", skip_all)]
117 pub async fn await_durable(&self, cursor: &StorageCursor) -> Result<()> {
118 self.inner.primary.history().check(&cursor.history)?;
119 self.flush_domain(Domain::Data, Some(cursor)).await
120 }
121
122 #[tracing::instrument(name = "storage.manager.status.release_object_history", level = "debug", skip_all)]
123 pub async fn release_object_history(&self, schema: &SchemaKey, item: &crate::ObjectKey, through: &StorageCursor) -> Result<()> {
124 self.inner.primary.history().check(&through.history)?;
125 if !self.status().saved.covers(through)? {
126 return Err(Error::InvalidInput { field: "object retention", reason: "release exceeds durable frontier" });
127 }
128 self.inner.bounded("primary.release_object_history", self.inner.primary.release_object_history(schema, item, through)).await?;
129 self.inner.state.lock().unwrap().objects_pruned = 0;
130 self.inner.notify();
131 Ok(())
132 }
133}
134
135#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::test_backend::{Data, Recovery, history, storage_settings};
145
146 fn storage() -> StorageManager {
147 StorageManager::new(
148 crate::StorageBackends { primary: Arc::new(Data::<()>::new(history())), state: Arc::new(Recovery::new(history())) },
149 storage_settings(),
150 )
151 .unwrap()
152 }
153
154 async fn pending<F: std::future::Future + ?Sized>(mut future: std::pin::Pin<&mut F>) -> bool {
155 std::future::poll_fn(|cx| std::task::Poll::Ready(future.as_mut().poll(cx).is_pending())).await
156 }
157
158 #[tokio::test]
159 async fn saved_wait_cancels() {
160 let storage = storage();
161 {
162 let mut state = storage.inner.state.lock().unwrap();
163 state.ready = true;
164 state.status.running = true;
165 state.retry = 41;
166 }
167 let before = storage.cursor_saved();
168 let future = StorageCursor { sequence: 42, ..before.clone() };
169 let revision = *storage.inner.changed.borrow();
170 let subscribers = storage.inner.changed.receiver_count();
171 let mut waiting = Box::pin(storage.wait_saved_after(&future));
172 assert!(pending(waiting.as_mut()).await);
173 assert_eq!(storage.inner.changed.receiver_count(), subscribers + 1);
174 drop(waiting);
175 assert_eq!(storage.inner.changed.receiver_count(), subscribers);
176 assert_eq!(*storage.inner.changed.borrow(), revision);
177 {
178 let state = storage.inner.state.lock().unwrap();
179 assert_eq!(state.retry, 41);
180 assert!(state.data_request.is_none());
181 assert!(state.recovery_request.is_none());
182 assert_eq!(state.queue.len(), 0);
183 assert_eq!(state.status.saved, before);
184 assert_eq!(state.status.accepted, before);
185 }
186 let mut waiting = Box::pin(storage.wait_saved_after(&future));
187 assert!(pending(waiting.as_mut()).await);
188 storage.inner.state.lock().unwrap().status.closed = true;
189 storage.inner.notify();
190 assert!(matches!(waiting.await, Err(Error::Closing { .. })));
191 }
192 #[tokio::test]
193 async fn saved_wait_coalesces() {
194 let storage = storage();
195 {
196 let mut state = storage.inner.state.lock().unwrap();
197 state.ready = true;
198 state.status.running = true;
199 state.status.accepted.sequence = 12;
200 }
201 let before = storage.cursor_saved();
202 let mut waiting = Box::pin(storage.wait_saved_after(&before));
203 assert!(pending(waiting.as_mut()).await);
204 for sequence in [3, 9] {
205 storage.inner.state.lock().unwrap().status.saved.sequence = sequence;
206 storage.inner.notify();
207 }
208 assert_eq!(waiting.await.unwrap().sequence, 9);
209 let cursor = storage.position(9);
211 let waiting = storage.wait_saved_after(&cursor);
212 storage.inner.state.lock().unwrap().status.saved.sequence = 12;
213 storage.inner.notify();
214 assert_eq!(waiting.await.unwrap().sequence, 12);
215 }
216
217 #[tokio::test]
218 async fn saved_wait_errors() {
219 let storage = storage();
220 let cursor = storage.cursor_saved();
221 assert!(matches!(storage.wait_saved_after(&cursor).await, Err(Error::NotReady { .. })));
222 let failure = Arc::new(Error::NotReady { domain: "injected persistence" });
223 {
224 let mut state = storage.inner.state.lock().unwrap();
225 state.status.failure = Some(Arc::clone(&failure));
226 state.retry = 41;
227 }
228 let result = storage.wait_saved_after(&cursor).await;
229 assert!(matches!(result, Err(Error::Persistence { source }) if Arc::ptr_eq(&source, &failure)));
230 {
231 let mut state = storage.inner.state.lock().unwrap();
232 assert!(Arc::ptr_eq(state.status.failure.as_ref().unwrap(), &failure));
233 assert_eq!(state.retry, 41);
234 assert!(state.data_request.is_none());
235 state.status.closing = true;
236 }
237 assert!(matches!(storage.wait_saved_after(&cursor).await, Err(Error::Closing { .. })));
238 {
239 let mut state = storage.inner.state.lock().unwrap();
240 state.status.accepted.sequence = 7;
241 state.status.saved.sequence = 7;
242 }
243 assert_eq!(storage.wait_saved_after(&cursor).await.unwrap().sequence, 7);
244 let mut foreign = cursor;
245 foreign.history.store = crate::Identifier::new("foreign").unwrap();
246 assert!(matches!(storage.wait_saved_after(&foreign).await, Err(Error::Domain(harmos_domain::Error::DifferentHistory))));
247 }
248}