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 crate::{Application, state::Entry, state::Position, state::PreparationError, state::Receipt, state::Recorded, state::RequestKey};
28
29#[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
73pub(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
112pub 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
126impl<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 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