Skip to main content

harmos_runtime/state/
capture.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17use std::collections::HashMap;
18use std::future::Future;
19use std::pin::Pin;
20use std::sync::{
21    Arc,
22    atomic::{AtomicBool, Ordering},
23};
24// Third Party Imports
25use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc, watch};
26// Local Imports
27use crate::{Application, state::Entry, state::Position, state::PreparationError, state::Receipt, state::Recorded, state::RequestKey};
28
29/* === Constants === */
30
31/* === Macros === */
32
33/* === Type Aliases === */
34
35/* === Structs === */
36
37#[derive(Clone, Copy, Debug)]
38pub struct CaptureLimits {
39    pub pending_bytes: u32,
40    pub max_group_bytes: u32,
41}
42
43pub(crate) struct CallbackDeclaration<A: Application> {
44    pub limits: CaptureLimits,
45    pub consumer: Box<dyn Capturing<A>>,
46}
47
48struct Group<A: Application> {
49    entries: Vec<Arc<Entry<Recorded<A>>>>,
50    frontier: Position,
51    _permit: OwnedSemaphorePermit,
52}
53
54pub(crate) struct Callback<A: Application> {
55    sender: mpsc::UnboundedSender<Group<A>>,
56    shared: Arc<Shared>,
57    limits: CaptureLimits,
58    worker: tokio::task::JoinHandle<()>,
59}
60
61struct Shared {
62    failed: Arc<AtomicBool>,
63    signal: watch::Sender<bool>,
64    capacity: Arc<Semaphore>,
65    progress: watch::Sender<Result<Position, CaptureError>>,
66}
67
68struct Failure {
69    shared: Arc<Shared>,
70    clean: bool,
71}
72
73/* === Enums === */
74
75pub(crate) enum Declaration<A: Application> {
76    Callback(CallbackDeclaration<A>),
77    Managed {
78        storage: crate::storage::StorageManager<crate::storage::ErasedCatalog>,
79        receipts: HashMap<RequestKey, Receipt>,
80        projection: Option<crate::storage::capture::Projection>,
81        snapshots: Option<crate::state::snapshot::Snapshots<A>>,
82    },
83}
84
85pub(crate) enum Capture<A: Application> {
86    Callback(Callback<A>),
87    Managed {
88        storage: crate::storage::StorageManager<crate::storage::ErasedCatalog>,
89        receipts: HashMap<RequestKey, Receipt>,
90        projection: Option<crate::storage::capture::Projection>,
91        snapshots: Option<crate::state::snapshot::Snapshots<A>>,
92        failed: Arc<AtomicBool>,
93        progress: watch::Sender<Result<Position, CaptureError>>,
94        signal: watch::Sender<bool>,
95    },
96}
97
98pub(crate) enum Permit {
99    Callback(OwnedSemaphorePermit),
100    Managed(crate::storage::state::Permit),
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
104pub enum CaptureError {
105    #[error("required journal capture failed")]
106    Failed,
107
108    #[error("capture limits must be positive and group bytes must fit pending bytes")]
109    InvalidLimits,
110}
111
112/* === Traits === */
113
114pub trait JournalCapture<A: Application>: Send + 'static {
115    fn capture(
116        &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
117    ) -> impl Future<Output = Result<(), CaptureError>> + Send;
118}
119
120pub(crate) trait Capturing<A: Application>: Send {
121    fn capture(
122        &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
123    ) -> Pin<Box<dyn Future<Output = Result<(), CaptureError>> + Send + '_>>;
124}
125
126/* === Implementations === */
127
128impl<A: Application, C: JournalCapture<A>> Capturing<A> for C {
129    fn capture(
130        &mut self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position,
131    ) -> Pin<Box<dyn Future<Output = Result<(), CaptureError>> + Send + '_>> {
132        Box::pin(JournalCapture::capture(self, entries, frontier))
133    }
134}
135
136impl Shared {
137    fn fail(&self) {
138        self.failed.store(true, Ordering::Release);
139        self.capacity.close();
140        let _ = self.progress.send_replace(Err(CaptureError::Failed));
141        self.signal.send_replace(true);
142    }
143}
144
145impl Drop for Failure {
146    fn drop(&mut self) {
147        if !self.clean {
148            self.shared.fail();
149        }
150    }
151}
152
153impl<A: Application> CallbackDeclaration<A> {
154    pub fn start(self, failed: Arc<AtomicBool>, origin: Position) -> Callback<A> {
155        let (sender, mut receiver) = mpsc::unbounded_channel::<Group<A>>();
156        let (signal, _) = watch::channel(false);
157        let (progress, _) = watch::channel(Ok(origin));
158        let shared = Arc::new(Shared { failed, signal, progress, capacity: Arc::new(Semaphore::new(self.limits.pending_bytes as usize)) });
159        let mut guard = Failure { shared: shared.clone(), clean: false };
160        let mut consumer = self.consumer;
161        let worker = tokio::spawn(async move {
162            while let Some(group) = receiver.recv().await {
163                if consumer.capture(group.entries, group.frontier).await.is_err() {
164                    return;
165                }
166                // Capacity includes the callback until successful acknowledgement.
167                let _ = guard.shared.progress.send_replace(Ok(group.frontier));
168                drop(group._permit);
169            }
170            guard.clean = true;
171            drop(guard);
172        });
173        Callback { sender, shared, limits: self.limits, worker }
174    }
175}
176
177impl<A: Application> Callback<A> {
178    pub async fn reserve(&self, bytes: usize) -> Result<OwnedSemaphorePermit, PreparationError> {
179        self.check()?;
180        if bytes > self.limits.max_group_bytes as usize {
181            return Err(PreparationError::Limit);
182        }
183        self.shared.capacity.clone().acquire_many_owned(bytes as u32).await.map_err(|_| PreparationError::CaptureFailed)
184    }
185
186    pub fn check(&self) -> Result<(), PreparationError> {
187        if self.shared.failed.load(Ordering::Acquire) { Err(PreparationError::CaptureFailed) } else { Ok(()) }
188    }
189
190    pub fn submit(&self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position, permit: OwnedSemaphorePermit) {
191        if self.sender.send(Group { entries, frontier, _permit: permit }).is_err() {
192            self.shared.fail();
193        }
194    }
195
196    pub fn progress(&self) -> watch::Receiver<Result<Position, CaptureError>> {
197        self.shared.progress.subscribe()
198    }
199
200    pub fn failure(&self) -> watch::Receiver<bool> {
201        self.shared.signal.subscribe()
202    }
203
204    pub async fn finish(self) {
205        drop(self.sender);
206        let _ = self.worker.await;
207    }
208}
209
210impl<A: Application> Declaration<A> {
211    pub(crate) fn start(self, failed: Arc<AtomicBool>, origin: Position) -> Capture<A> {
212        match self {
213            Self::Callback(callback) => Capture::Callback(callback.start(failed, origin)),
214            Self::Managed { storage, receipts, projection, snapshots } => Capture::Managed {
215                storage,
216                receipts,
217                projection,
218                snapshots,
219                failed,
220                progress: watch::channel(Ok(origin)).0,
221                signal: watch::channel(false).0,
222            },
223        }
224    }
225}
226
227impl<A: Application> Capture<A> {
228    pub(crate) async fn snapshot(
229        &self, tracked: &super::TrackedState<A>, undos: &super::Undos<A>, receipts: &HashMap<RequestKey, Receipt>, final_save: bool,
230    ) {
231        if let Self::Managed { storage, snapshots: Some(snapshots), .. } = self {
232            snapshots.capture(storage, tracked, undos, receipts, final_save).await;
233        }
234    }
235
236    pub(crate) fn take_receipts(&mut self) -> HashMap<RequestKey, Receipt> {
237        match self {
238            Self::Callback(_) => HashMap::new(),
239            Self::Managed { receipts, .. } => std::mem::take(receipts),
240        }
241    }
242
243    pub(crate) async fn reserve(
244        &self, bytes: usize, group: &crate::state::recovery::Group<&serde_json::Value>,
245    ) -> Result<Permit, PreparationError> {
246        match self {
247            Self::Callback(callback) => callback.reserve(bytes).await.map(Permit::Callback),
248            Self::Managed { storage, projection, .. } => {
249                storage.reserve_group(group, projection.as_ref()).await.map(Permit::Managed).map_err(|error| match error {
250                    crate::Error::InvalidInput { .. } => PreparationError::Limit,
251                    _ => {
252                        self.fail();
253                        PreparationError::CaptureFailed
254                    }
255                })
256            }
257        }
258    }
259
260    pub(crate) fn check(&self) -> Result<(), PreparationError> {
261        match self {
262            Self::Callback(callback) => callback.check(),
263            Self::Managed { storage, .. } => {
264                let status = storage.status();
265                if status.failure.is_some() || status.closing || !status.running {
266                    self.fail();
267                    Err(PreparationError::CaptureFailed)
268                } else {
269                    Ok(())
270                }
271            }
272        }
273    }
274
275    pub(crate) fn submit(&self, entries: Vec<Arc<Entry<Recorded<A>>>>, frontier: Position, permit: Permit) {
276        match (self, permit) {
277            (Self::Callback(callback), Permit::Callback(permit)) => callback.submit(entries, frontier, permit),
278            (Self::Managed { progress, .. }, Permit::Managed(permit)) => {
279                permit.commit(frontier);
280                let _ = progress.send_replace(Ok(frontier));
281            }
282            _ => unreachable!("capture owns its reservation"),
283        }
284    }
285
286    pub(crate) fn progress(&self) -> watch::Receiver<Result<Position, CaptureError>> {
287        match self {
288            Self::Callback(callback) => callback.progress(),
289            Self::Managed { progress, .. } => progress.subscribe(),
290        }
291    }
292
293    pub(crate) fn failure(&self) -> watch::Receiver<bool> {
294        match self {
295            Self::Callback(callback) => callback.failure(),
296            Self::Managed { signal, .. } => signal.subscribe(),
297        }
298    }
299
300    fn fail(&self) {
301        if let Self::Managed { failed, progress, signal, .. } = self {
302            failed.store(true, Ordering::Release);
303            let _ = progress.send_replace(Err(CaptureError::Failed));
304            signal.send_replace(true);
305        }
306    }
307
308    pub(crate) async fn finish(self) {
309        if let Self::Callback(callback) = self {
310            callback.finish().await;
311        }
312    }
313}
314
315/* === Public Functions === */
316
317/* === Private Functions === */
318
319/* === Tests === */