1use std::collections::HashMap;
18use std::future::Future;
19use std::pin::Pin;
20use std::sync::{
21 Arc,
22 atomic::{AtomicBool, Ordering},
23};
24use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, watch};
26use tracing::Instrument;
27use crate::supervision::executor::InContext;
29use crate::{Application, state::Entry, state::Position, state::PreparationError, state::Receipt, state::Recorded, state::RequestKey};
30
31#[derive(Clone, Copy, Debug)]
40pub struct CaptureLimits {
41 pub pending_bytes: u32,
42 pub max_group_bytes: u32,
43}
44
45pub(crate) struct CallbackDeclaration<A: Application> {
46 pub limits: CaptureLimits,
47 pub consumer: Box<dyn Capturing<A>>,
48}
49
50struct Group<A: Application> {
51 trace: crate::supervision::executor::SpanContext,
52 entries: Vec<Arc<Entry<Recorded<A>>>>,
53 frontier: Position,
54 _permit: OwnedSemaphorePermit,
55}
56
57pub(crate) struct Callback<A: Application> {
58 sender: mpsc::UnboundedSender<Group<A>>,
59 shared: Arc<Shared>,
60 limits: CaptureLimits,
61 worker: tokio::task::JoinHandle<()>,
62}
63
64struct Shared {
65 failed: Arc<AtomicBool>,
66 signal: watch::Sender<bool>,
67 capacity: Arc<Semaphore>,
68 progress: watch::Sender<Result<Position, CaptureError>>,
69}
70
71struct Failure {
72 shared: Arc<Shared>,
73 clean: bool,
74}
75
76pub(crate) enum Declaration<A: Application> {
79 Callback(CallbackDeclaration<A>),
80 Managed {
81 storage: crate::storage::StorageManager<crate::storage::ErasedCatalog>,
82 receipts: HashMap<RequestKey, Receipt>,
83 projection: Option<crate::storage::capture::Projection>,
84 snapshots: Option<crate::state::snapshot::Snapshots<A>>,
85 },
86}
87
88pub(crate) enum Capture<A: Application> {
89 Callback(Callback<A>),
90 Managed {
91 storage: crate::storage::StorageManager<crate::storage::ErasedCatalog>,
92 receipts: HashMap<RequestKey, Receipt>,
93 projection: Option<crate::storage::capture::Projection>,
94 snapshots: Option<crate::state::snapshot::Snapshots<A>>,
95 failed: Arc<AtomicBool>,
96 progress: watch::Sender<Result<Position, CaptureError>>,
97 signal: watch::Sender<bool>,
98 },
99}
100
101pub(crate) enum Permit {
102 Callback(OwnedSemaphorePermit),
103 Managed(Box<crate::storage::state::Permit>),
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
107pub enum CaptureError {
108 #[error("required journal capture failed")]
109 Failed,
110
111 #[error("capture limits must be positive and group bytes must fit pending bytes")]
112 InvalidLimits,
113}
114
115pub trait JournalCapture<A: Application>: Send + 'static {
118 fn capture(
119 &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
120 ) -> impl Future<Output = Result<(), CaptureError>> + Send;
121}
122
123pub(crate) trait Capturing<A: Application>: Send {
124 fn capture(
125 &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
126 ) -> Pin<Box<dyn Future<Output = Result<(), CaptureError>> + Send + '_>>;
127}
128
129impl<A: Application, C: JournalCapture<A>> Capturing<A> for C {
132 fn capture(
133 &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
134 ) -> Pin<Box<dyn Future<Output = Result<(), CaptureError>> + Send + '_>> {
135 Box::pin(JournalCapture::capture(self, entries, frontier))
136 }
137}
138
139impl Shared {
140 fn fail(&self) {
141 self.failed.store(true, Ordering::Release);
142 self.capacity.close();
143 let _ = self.progress.send_replace(Err(CaptureError::Failed));
144 self.signal.send_replace(true);
145 }
146}
147
148impl Drop for Failure {
149 fn drop(&mut self) {
150 if !self.clean {
151 self.shared.fail();
152 }
153 }
154}
155
156impl<A: Application> CallbackDeclaration<A> {
157 pub fn start(self, failed: Arc<AtomicBool>, origin: Position) -> Callback<A> {
158 let (sender, mut receiver) = mpsc::unbounded_channel::<Group<A>>();
159 let (signal, _) = watch::channel(false);
160 let (progress, _) = watch::channel(Ok(origin));
161 let shared = Arc::new(Shared {
162 failed,
163 signal,
164 progress,
165 capacity: Arc::new(Semaphore::new(self.limits.pending_bytes as usize)),
166 });
167 let mut guard = Failure { shared: shared.clone(), clean: false };
168 let mut consumer = self.consumer;
169 let worker = tokio::spawn(async move {
170 while let Some(mut group) = receiver.recv().await {
171 group.trace.span.follows_from(group.trace.predecessor());
172 group.trace.dequeued();
173 if consumer
174 .capture(group.entries, group.frontier)
175 .instrument(group.trace.span.clone())
176 .in_context(group.trace.dispatcher.clone())
177 .await
178 .is_err()
179 {
180 return;
181 }
182 let _ = guard.shared.progress.send_replace(Ok(group.frontier));
184 drop(group._permit);
185 }
186 guard.clean = true;
187 drop(guard);
188 });
189 Callback {
190 sender,
191 shared,
192 limits: self.limits,
193 worker,
194 }
195 }
196}
197
198impl<A: Application> Callback<A> {
199 #[tracing::instrument(target = "harmos::state", name = "state.reserve", level = "debug", skip_all)]
200 pub async fn reserve(&self, bytes: usize) -> Result<OwnedSemaphorePermit, PreparationError> {
201 self.check()?;
202 if bytes > self.limits.max_group_bytes as usize {
203 return Err(PreparationError::Limit);
204 }
205 self.shared.capacity.clone().acquire_many_owned(bytes as u32).await.map_err(|_| PreparationError::CaptureFailed)
206 }
207
208 pub fn check(&self) -> Result<(), PreparationError> {
209 if self.shared.failed.load(Ordering::Acquire) { Err(PreparationError::CaptureFailed) } else { Ok(()) }
210 }
211
212 pub fn submit(&self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position, permit: OwnedSemaphorePermit) {
213 if self
214 .sender
215 .send(Group {
216 entries,
217 frontier,
218 _permit: permit,
219 trace: crate::supervision::executor::SpanContext::new(
220 tracing::debug_span!(target: "harmos::state", "state.capture_group", frontier = frontier.0),
221 tracing::debug_span!(target: "harmos::state", "state.capture_queue"),
222 ),
223 })
224 .is_err()
225 {
226 self.shared.fail();
227 }
228 }
229
230 pub fn progress(&self) -> watch::Receiver<Result<Position, CaptureError>> {
231 self.shared.progress.subscribe()
232 }
233
234 pub fn failure(&self) -> watch::Receiver<bool> {
235 self.shared.signal.subscribe()
236 }
237
238 #[tracing::instrument(target = "harmos::state", name = "state.finish", level = "debug", skip_all)]
239 pub async fn finish(self) {
240 drop(self.sender);
241 let _ = self.worker.await;
242 }
243}
244
245impl<A: Application> Declaration<A> {
246 pub(crate) fn start(self, failed: Arc<AtomicBool>, origin: Position) -> Capture<A> {
247 match self {
248 Self::Callback(callback) => Capture::Callback(callback.start(failed, origin)),
249 Self::Managed { storage, receipts, projection, snapshots } => Capture::Managed {
250 storage,
251 receipts,
252 projection,
253 snapshots,
254 failed,
255 progress: watch::channel(Ok(origin)).0,
256 signal: watch::channel(false).0,
257 },
258 }
259 }
260}
261
262impl<A: Application> Capture<A> {
263 #[tracing::instrument(target = "harmos::state", name = "state.snapshot", level = "debug", skip_all)]
264 pub(crate) async fn snapshot(
265 &self, tracked: &super::TrackedState<A>, undos: &super::Undos<A>, receipts: &HashMap<RequestKey, Receipt>, final_save: bool,
266 ) {
267 if let Self::Managed { storage, snapshots: Some(snapshots), .. } = self {
268 snapshots.capture(storage, tracked, undos, receipts, final_save).await;
269 }
270 }
271
272 pub(crate) fn take_receipts(&mut self) -> HashMap<RequestKey, Receipt> {
273 match self {
274 Self::Callback(_) => HashMap::new(),
275 Self::Managed { receipts, .. } => std::mem::take(receipts),
276 }
277 }
278
279 #[tracing::instrument(target = "harmos::state", name = "state.reserve", level = "debug", skip_all)]
280 pub(crate) async fn reserve(
281 &self, bytes: usize, group: &crate::state::recovery::Group<&serde_json::Value>,
282 ) -> Result<Permit, PreparationError> {
283 match self {
284 Self::Callback(callback) => callback.reserve(bytes).await.map(Permit::Callback),
285 Self::Managed { storage, projection, .. } => {
286 storage.reserve_group(group, projection.as_ref()).await.map(|permit| Permit::Managed(Box::new(permit))).map_err(|error| {
287 match error {
288 crate::Error::InvalidInput { .. } => PreparationError::Limit,
289 _ => {
290 self.fail();
291 PreparationError::CaptureFailed
292 }
293 }
294 })
295 }
296 }
297 }
298
299 pub(crate) fn check(&self) -> Result<(), PreparationError> {
300 match self {
301 Self::Callback(callback) => callback.check(),
302 Self::Managed { storage, .. } => {
303 let status = storage.status();
304 if status.failure.is_some() || status.closing || !status.running {
305 self.fail();
306 Err(PreparationError::CaptureFailed)
307 } else {
308 Ok(())
309 }
310 }
311 }
312 }
313
314 pub(crate) fn submit(&self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position, permit: Permit) {
315 match (self, permit) {
316 (Self::Callback(callback), Permit::Callback(permit)) => callback.submit(entries, frontier, permit),
317 (Self::Managed { progress, .. }, Permit::Managed(permit)) => {
318 (*permit).commit(frontier);
319 let _ = progress.send_replace(Ok(frontier));
320 }
321 _ => unreachable!("capture owns its reservation"),
322 }
323 }
324
325 pub(crate) fn progress(&self) -> watch::Receiver<Result<Position, CaptureError>> {
326 match self {
327 Self::Callback(callback) => callback.progress(),
328 Self::Managed { progress, .. } => progress.subscribe(),
329 }
330 }
331
332 pub(crate) fn failure(&self) -> watch::Receiver<bool> {
333 match self {
334 Self::Callback(callback) => callback.failure(),
335 Self::Managed { signal, .. } => signal.subscribe(),
336 }
337 }
338
339 fn fail(&self) {
340 if let Self::Managed { failed, progress, signal, .. } = self {
341 failed.store(true, Ordering::Release);
342 let _ = progress.send_replace(Err(CaptureError::Failed));
343 signal.send_replace(true);
344 }
345 }
346
347 #[tracing::instrument(target = "harmos::state", name = "state.finish", level = "debug", skip_all)]
348 pub(crate) async fn finish(self) {
349 if let Self::Callback(callback) = self {
350 callback.finish().await;
351 }
352 }
353}
354
355