Skip to main content

harmos_storage/json/
mod.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10// Bounded JSON Collection Storage And Native File Publication.
11
12/* === Modules === */
13
14mod config;
15mod files;
16mod format;
17mod log;
18mod reads;
19mod sessions;
20
21/* === Re-Exports === */
22
23pub use config::JsonStorageSettings;
24
25/* === Imports === */
26
27// Standard Library Imports
28use std::{
29    num::NonZeroUsize,
30    sync::{Arc, atomic::Ordering},
31};
32
33// Third Party Imports
34use tokio::sync::Mutex;
35
36// Local Imports
37use crate::arrow::RetainedMemory;
38use crate::{
39    query::{FileSelection, ObjectSelection, RecordSelection, SchemaSelection, StreamSelection},
40    *,
41};
42use files::Files;
43use log::{Log, Row};
44
45/* === Constants === */
46
47/* === Macros === */
48
49/* === Type Aliases === */
50
51/* === Structs === */
52
53pub struct JsonStorage {
54    settings: JsonStorageSettings,
55    history: StoreHistory,
56    owner: Arc<Mutex<Option<Log>>>,
57    files: Arc<Files>,
58}
59
60/* === Enums === */
61
62/* === Traits === */
63
64/* === Implementations === */
65
66impl JsonStorage {
67    pub fn new(settings: JsonStorageSettings, history: StoreHistory) -> Self {
68        let files = Files::new(settings.root.clone(), history.clone(), settings.max_entry_bytes.get());
69        Self { settings, history, owner: Arc::new(Mutex::new(None)), files: Arc::new(files) }
70    }
71
72    pub fn into_backends(self, settings: state::StorageStateSettings) -> StorageBackends {
73        let state = state::file::FileBackend::new(self.settings.root.join("state"), self.history.clone(), settings);
74        StorageBackends { primary: Arc::new(self), state: Arc::new(state) }
75    }
76
77    async fn call<T: Send + 'static>(&self, action: impl FnOnce(&mut Log) -> Result<T> + Send + 'static) -> Result<T> {
78        if self.files.closing.load(Ordering::Acquire) {
79            return Err(Error::Closing { domain: "JSON storage" });
80        }
81        let mut owner = tracing::Instrument::instrument(self.owner.clone().lock_owned(), tracing::trace_span!("storage.lock.wait")).await;
82        if self.files.closing.load(Ordering::Acquire) {
83            return Err(Error::Closing { domain: "JSON storage" });
84        }
85        {
86            let context = crate::execution::Context::current();
87            tokio::task::spawn_blocking(move || {
88                context.blocking(|| {
89                    let log = owner.as_mut().ok_or(Error::NotReady { domain: "JSON storage" })?;
90                    action(log)
91                })
92            })
93        }
94        .await
95        .map_err(task)?
96    }
97
98    async fn read<T: Send + 'static>(
99        &self, memory: RetainedMemory, action: impl FnOnce(&mut Log, &RetainedMemory) -> Result<T> + Send + 'static,
100    ) -> Result<T> {
101        let (result, _memory) = self.call(move |log| Ok((action(log, &memory), memory))).await?;
102        result
103    }
104
105    async fn read_selected<T: Send + 'static, S: Clone + MemorySize + Send + Sync + 'static>(
106        &self, snapshot: &BackendSnapshot, selection: &S, memory: RetainedMemory,
107        action: impl FnOnce(&mut Log, &BackendSnapshot, &S, &RetainedMemory, usize) -> Result<T> + Send + 'static,
108    ) -> Result<T> {
109        let base = snapshot
110            .at
111            .memory_size()
112            .saturating_add(snapshot.token.capacity())
113            .saturating_add(std::mem::size_of::<Vec<u8>>())
114            .saturating_add(selection.memory_size())
115            .saturating_add(crate::manager::ENTRY_OVERHEAD);
116        memory.try_grow_to(base)?;
117        let snapshot = snapshot.clone();
118        let selection = selection.clone();
119        self.read(memory, move |log, memory| action(log, &snapshot, &selection, memory, base)).await
120    }
121
122    async fn file_read<T: Send + 'static>(
123        &self, file: StagedFile, memory: RetainedMemory, action: impl FnOnce(&Files, &str) -> Result<T> + Send + 'static,
124    ) -> Result<T> {
125        let (result, _memory) = self.file(file, move |files, key| Ok((action(files, key), memory))).await?;
126        result
127    }
128
129    async fn write<T: Row>(&self, batch: StorageBatch<T>) -> Result<StorageCursor> {
130        self.call(move |log| log.write(batch)).await
131    }
132
133    async fn file<T: Send + 'static>(
134        &self, file: StagedFile, action: impl FnOnce(&Files, &str) -> Result<T> + Send + 'static,
135    ) -> Result<T> {
136        self.history.check(&file.history)?;
137        self.files.call(&file.id, action).await
138    }
139}
140
141impl StorageBackend for JsonStorage {
142    fn history(&self) -> &StoreHistory {
143        &self.history
144    }
145    fn open(&self) -> StorageFuture<'_, StorageCursor> {
146        Box::pin(async move {
147            let context = crate::execution::Context::current();
148            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "open", outcome = "cancelled"));
149            context
150                .result(span, {
151                    async move {
152                        let mut owner =
153                            tracing::Instrument::instrument(self.owner.clone().lock_owned(), tracing::trace_span!("storage.lock.wait"))
154                                .await;
155                        let settings = self.settings.clone();
156                        let history = self.history.clone();
157                        let files = self.files.clone();
158                        {
159                            let context = crate::execution::Context::current();
160                            tokio::task::spawn_blocking(move || {
161                                context.blocking(|| {
162                                    if owner.is_none() {
163                                        *owner = Some(Log::open(&settings, history)?);
164                                    }
165                                    let log = owner.as_ref().expect("opened JSON log");
166                                    *files.lease.lock().map_err(|_| format::corrupt("file lease is poisoned"))? =
167                                        Arc::downgrade(&log.lease);
168                                    files.closing.store(false, Ordering::Release);
169                                    Ok(log.cursor())
170                                })
171                            })
172                        }
173                        .await
174                        .map_err(task)?
175                    }
176                })
177                .await
178        })
179    }
180    fn cursor_saved(&self) -> StorageFuture<'_, StorageCursor> {
181        Box::pin(async move {
182            let context = crate::execution::Context::current();
183            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "cursor_saved", outcome = "cancelled"));
184            context.result(span, self.call(|log| Ok(log.cursor()))).await
185        })
186    }
187    fn close(&self) -> StorageFuture<'_, ()> {
188        Box::pin(async move {
189            let context = crate::execution::Context::current();
190            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "close", outcome = "cancelled"));
191            context
192                .result(span, {
193                    async move {
194                        self.files.closing.store(true, Ordering::Release);
195                        let activity = tracing::Instrument::instrument(
196                            self.files.activity.clone().write_owned(),
197                            tracing::trace_span!("storage.lock.wait"),
198                        )
199                        .await;
200                        let mut owner =
201                            tracing::Instrument::instrument(self.owner.clone().lock_owned(), tracing::trace_span!("storage.lock.wait"))
202                                .await;
203                        {
204                            let context = crate::execution::Context::current();
205                            tokio::task::spawn_blocking(move || {
206                                context.blocking(|| {
207                                    let _activity = activity;
208                                    owner.take();
209                                    Ok(())
210                                })
211                            })
212                        }
213                        .await
214                        .map_err(task)?
215                    }
216                })
217                .await
218        })
219    }
220    fn write_schemas(&self, batch: StorageBatch<StorageSchema>) -> StorageFuture<'_, StorageCursor> {
221        Box::pin(async move {
222            let context = crate::execution::Context::current();
223            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_schemas", outcome = "cancelled"));
224            context.result(span, self.write(batch)).await
225        })
226    }
227    fn write_objects(&self, batch: StorageBatch<ObjectEntry>) -> StorageFuture<'_, StorageCursor> {
228        Box::pin(async move {
229            let context = crate::execution::Context::current();
230            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_objects", outcome = "cancelled"));
231            context.result(span, self.write(batch)).await
232        })
233    }
234    fn write_records(&self, batch: StorageBatch<StoredRecord>) -> StorageFuture<'_, StorageCursor> {
235        Box::pin(async move {
236            let context = crate::execution::Context::current();
237            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_records", outcome = "cancelled"));
238            context.result(span, self.write(batch)).await
239        })
240    }
241    fn write_stream(&self, batch: StorageBatch<StoredRow>) -> StorageFuture<'_, StorageCursor> {
242        Box::pin(async move {
243            let context = crate::execution::Context::current();
244            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_stream", outcome = "cancelled"));
245            context.result(span, self.write(batch)).await
246        })
247    }
248    fn write_files(&self, batch: StorageBatch<FileReference>) -> StorageFuture<'_, StorageCursor> {
249        Box::pin(async move {
250            let context = crate::execution::Context::current();
251            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_files", outcome = "cancelled"));
252            context
253                .result(span, {
254                    let files = self.files.clone();
255                    self.call(move |log| {
256                        for reference in batch.as_slice() {
257                            let production = files.load(&files::key(&reference.metadata.id))?;
258                            if production.phase != FilePhase::Finished || production.finish.as_ref() != Some(reference) {
259                                return Err(format::conflict("file publication precedes durable finish"));
260                            }
261                        }
262                        log.write(batch)
263                    })
264                })
265                .await
266        })
267    }
268    fn open_snapshot<'a>(&'a self, at: &'a StorageCursor, memory: RetainedMemory) -> StorageFuture<'a, BackendSnapshot> {
269        Box::pin(async move {
270            let context = crate::execution::Context::current();
271            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "open_snapshot", outcome = "cancelled"));
272            context
273                .result(span, {
274                    async move {
275                        let base = at
276                            .memory_size()
277                            .saturating_add(std::mem::size_of::<BackendSnapshot>())
278                            .saturating_add(32 + crate::manager::ENTRY_OVERHEAD);
279                        memory.try_grow_to(base)?;
280                        let at = at.clone();
281                        self.read(memory, move |log, _memory| log.snapshot(at)).await
282                    }
283                })
284                .await
285        })
286    }
287    fn release_snapshot<'a>(&'a self, snapshot: &'a BackendSnapshot) -> StorageFuture<'a, ()> {
288        Box::pin(async move {
289            let context = crate::execution::Context::current();
290            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "release_snapshot", outcome = "cancelled"));
291            context
292                .result(span, {
293                    let snapshot = snapshot.clone();
294                    self.call(move |log| {
295                        log.pin(&snapshot)?;
296                        log.snapshots.remove(&snapshot.token);
297                        Ok(())
298                    })
299                })
300                .await
301        })
302    }
303    fn read_schemas<'a>(
304        &'a self, snapshot: &'a BackendSnapshot, selection: &'a SchemaSelection, page: ScanPage, memory: RetainedMemory,
305    ) -> StorageFuture<'a, Vec<StorageSchema>> {
306        Box::pin(async move {
307            let context = crate::execution::Context::current();
308            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_schemas", outcome = "cancelled"));
309            context
310                .result(span, {
311                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
312                        let generation = log.pin(snapshot)?;
313                        log.scan(snapshot.at.sequence, generation, page, memory, base, |row: &StorageSchema| {
314                            Ok(selection.schema.as_ref().is_none_or(|key| key == &row.key))
315                        })
316                    })
317                })
318                .await
319        })
320    }
321    fn read_records<'a>(
322        &'a self, snapshot: &'a BackendSnapshot, selection: &'a RecordSelection, page: ScanPage, memory: RetainedMemory,
323    ) -> StorageFuture<'a, Vec<StoredRecord>> {
324        Box::pin(async move {
325            let context = crate::execution::Context::current();
326            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_records", outcome = "cancelled"));
327            context
328                .result(span, {
329                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
330                        let generation = log.pin(snapshot)?;
331                        log.scan(snapshot.at.sequence, generation, page, memory, base, |row: &StoredRecord| {
332                            Ok(crate::selection::Entry::matches(row, selection))
333                        })
334                    })
335                })
336                .await
337        })
338    }
339    fn read_stream<'a>(
340        &'a self, snapshot: &'a BackendSnapshot, selection: &'a StreamSelection, page: ScanPage, memory: RetainedMemory,
341    ) -> StorageFuture<'a, Vec<StoredRow>> {
342        Box::pin(async move {
343            let context = crate::execution::Context::current();
344            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_stream", outcome = "cancelled"));
345            context
346                .result(span, {
347                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
348                        let generation = log.pin(snapshot)?;
349                        log.scan(snapshot.at.sequence, generation, page, memory, base, |row: &StoredRow| {
350                            Ok(crate::selection::Entry::matches(row, selection))
351                        })
352                    })
353                })
354                .await
355        })
356    }
357    fn read_files<'a>(
358        &'a self, snapshot: &'a BackendSnapshot, selection: &'a FileSelection, page: ScanPage, memory: RetainedMemory,
359    ) -> StorageFuture<'a, Vec<FileReference>> {
360        Box::pin(async move {
361            let context = crate::execution::Context::current();
362            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_files", outcome = "cancelled"));
363            context
364                .result(span, {
365                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
366                        let generation = log.pin(snapshot)?;
367                        log.scan(snapshot.at.sequence, generation, page, memory, base, |row: &FileReference| {
368                            Ok(crate::selection::Entry::matches(row, selection))
369                        })
370                    })
371                })
372                .await
373        })
374    }
375    fn read_objects<'a>(
376        &'a self, snapshot: &'a BackendSnapshot, selection: &'a ObjectSelection, page: ScanPage, memory: RetainedMemory,
377    ) -> StorageFuture<'a, Vec<ObjectEntry>> {
378        Box::pin(async move {
379            let context = crate::execution::Context::current();
380            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_objects", outcome = "cancelled"));
381            context
382                .result(span, {
383                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
384                        log.objects(snapshot, selection, page, memory, base)
385                    })
386                })
387                .await
388        })
389    }
390    fn read_object<'a>(
391        &'a self, snapshot: &'a BackendSnapshot, point: &'a ObjectPoint, memory: RetainedMemory,
392    ) -> StorageFuture<'a, Option<ObjectEntry>> {
393        Box::pin(async move {
394            let context = crate::execution::Context::current();
395            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_object", outcome = "cancelled"));
396            context
397                .result(span, {
398                    self.read_selected(snapshot, point, memory, move |log, snapshot, point, memory, base| {
399                        log.object(snapshot, point, memory, base)
400                    })
401                })
402                .await
403        })
404    }
405    fn read_file<'a>(
406        &'a self, snapshot: &'a BackendSnapshot, file: &'a FileReference, offset: u64, maximum: NonZeroUsize, memory: RetainedMemory,
407    ) -> StorageFuture<'a, Vec<u8>> {
408        Box::pin(async move {
409            let context = crate::execution::Context::current();
410            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_file", outcome = "cancelled"));
411            context
412                .result(span, {
413                    async move {
414                        self.read_selected(snapshot, file, memory.clone(), |log, snapshot, reference, memory, base| {
415                            log.pin(snapshot)?;
416                            if reference.position.sequence <= snapshot.at.sequence
417                                && log.one::<FileReference>(reference.position.sequence, memory, base)?.as_ref() != Some(reference)
418                            {
419                                return Err(format::conflict("file differs from pinned saved view"));
420                            }
421                            Ok(())
422                        })
423                        .await?;
424                        let base = file.memory_size().saturating_mul(2).saturating_add(crate::manager::ENTRY_OVERHEAD);
425                        memory.try_grow_to(base)?;
426                        let reference = file.clone();
427                        let file = StagedFile { history: reference.position.history.clone(), id: reference.metadata.id.clone() };
428                        let scratch = memory.clone();
429                        self.file_read(file, memory, move |files, key| files.read(key, offset, maximum, Some(&reference), &scratch, base))
430                            .await
431                    }
432                })
433                .await
434        })
435    }
436
437    fn release_object_history<'a>(
438        &'a self, schema: &'a SchemaKey, item: &'a ObjectKey, through: &'a StorageCursor,
439    ) -> StorageFuture<'a, ()> {
440        Box::pin(async move {
441            let context = crate::execution::Context::current();
442            let span =
443                context.sync(|| tracing::debug_span!("storage.json.backend", method = "release_object_history", outcome = "cancelled"));
444            context
445                .result(span, {
446                    let schema = schema.clone();
447                    let item = item.clone();
448                    let through = through.clone();
449                    self.call(move |log| log.release(schema, item, through))
450                })
451                .await
452        })
453    }
454    fn prune_objects<'a>(
455        &'a self, through: &'a StorageCursor, maximum: NonZeroUsize, retention: fn(&SchemaKey) -> harmos_domain::Result<ObjectRetention>,
456    ) -> StorageFuture<'a, bool> {
457        Box::pin(async move {
458            let context = crate::execution::Context::current();
459            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "prune_objects", outcome = "cancelled"));
460            context
461                .result(span, {
462                    let through = through.clone();
463                    self.call(move |log| log.prune(&through, maximum, retention))
464                })
465                .await
466        })
467    }
468    fn create_file<'a>(&'a self, metadata: &'a FileMetadata) -> StorageFuture<'a, StagedFile> {
469        Box::pin(async move {
470            let context = crate::execution::Context::current();
471            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "create_file", outcome = "cancelled"));
472            context
473                .result(span, {
474                    let metadata = metadata.clone();
475                    let files = self.files.clone();
476                    let id = metadata.id.clone();
477                    async move { files.call(&id, move |files, key| files.create(key, metadata, false)).await }
478                })
479                .await
480        })
481    }
482    fn write_file<'a>(&'a self, staging: &'a StagedFile, offset: u64, bytes: FileChunk) -> StorageFuture<'a, ()> {
483        Box::pin(async move {
484            let context = crate::execution::Context::current();
485            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "write_file", outcome = "cancelled"));
486            context.result(span, self.file(staging.clone(), move |files, key| files.write(key, offset, bytes.as_slice()))).await
487        })
488    }
489    fn finish_file<'a>(
490        &'a self, staging: &'a StagedFile, position: &'a StorageCursor, length: u64, sha256: [u8; 32],
491    ) -> StorageFuture<'a, FileReference> {
492        Box::pin(async move {
493            let context = crate::execution::Context::current();
494            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "finish_file", outcome = "cancelled"));
495            context
496                .result(span, {
497                    let position = position.clone();
498                    self.file(staging.clone(), move |files, key| files.finish(key, position, length, sha256))
499                })
500                .await
501        })
502    }
503    fn abort_file<'a>(&'a self, staging: &'a StagedFile) -> StorageFuture<'a, ()> {
504        Box::pin(async move {
505            let context = crate::execution::Context::current();
506            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "abort_file", outcome = "cancelled"));
507            context.result(span, self.file(staging.clone(), |files, key| files.abort(key, false, false))).await
508        })
509    }
510    fn supports_file_sessions(&self) -> bool {
511        true
512    }
513    fn create_production<'a>(&'a self, metadata: &'a FileMetadata) -> StorageFuture<'a, StagedFile> {
514        Box::pin(async move {
515            let context = crate::execution::Context::current();
516            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "create_production", outcome = "cancelled"));
517            context
518                .result(span, {
519                    let metadata = metadata.clone();
520                    let files = self.files.clone();
521                    let id = metadata.id.clone();
522                    async move { files.call(&id, move |files, key| files.create(key, metadata, true)).await }
523                })
524                .await
525        })
526    }
527    fn fence_production<'a>(&'a self, file: &'a StagedFile, expected: u64, next: u64) -> StorageFuture<'a, ()> {
528        Box::pin(async move {
529            let context = crate::execution::Context::current();
530            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "fence_production", outcome = "cancelled"));
531            context.result(span, self.file(file.clone(), move |files, key| files.fence(key, expected, next))).await
532        })
533    }
534    fn save_production(&self, checkpoint: StorageBatch<FileCheckpoint>) -> StorageFuture<'_, ()> {
535        Box::pin(async move {
536            let context = crate::execution::Context::current();
537            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "save_production", outcome = "cancelled"));
538            context
539                .result(span, {
540                    async move {
541                        let [value] = checkpoint.as_slice() else {
542                            return Err(format::invalid("checkpoint requires one value"));
543                        };
544                        let file = StagedFile { history: value.session.history.clone(), id: value.session.id.clone() };
545                        self.file(file, move |files, key| files.checkpoint(key, &checkpoint.as_slice()[0])).await
546                    }
547                })
548                .await
549        })
550    }
551    fn recover_productions(&self, limits: ReadLimits, memory: RetainedMemory) -> StorageFuture<'_, Vec<FileCheckpoint>> {
552        Box::pin(async move {
553            let context = crate::execution::Context::current();
554            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "recover_productions", outcome = "cancelled"));
555            context
556                .result(span, {
557                    let files = self.files.clone();
558                    self.read(memory, move |_, memory| files.recover(limits, memory))
559                })
560                .await
561        })
562    }
563    fn file_progress<'a>(&'a self, file: &'a StagedFile, memory: RetainedMemory) -> StorageFuture<'a, FileProgress> {
564        Box::pin(async move {
565            let context = crate::execution::Context::current();
566            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "file_progress", outcome = "cancelled"));
567            context
568                .result(span, {
569                    async move {
570                        let base = file.memory_size().saturating_add(crate::manager::ENTRY_OVERHEAD);
571                        memory.try_grow_to(base)?;
572                        let scratch = memory.clone();
573                        self.file_read(file.clone(), memory, move |files, key| files.progress(key, &scratch, base)).await
574                    }
575                })
576                .await
577        })
578    }
579
580    fn read_production<'a>(
581        &'a self, file: &'a StagedFile, offset: u64, maximum: NonZeroUsize, memory: RetainedMemory,
582    ) -> StorageFuture<'a, Vec<u8>> {
583        Box::pin(async move {
584            let context = crate::execution::Context::current();
585            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_production", outcome = "cancelled"));
586            context
587                .result(span, {
588                    async move {
589                        let base = file.memory_size().saturating_add(crate::manager::ENTRY_OVERHEAD);
590                        memory.try_grow_to(base)?;
591                        let scratch = memory.clone();
592                        self.file_read(file.clone(), memory, move |files, key| files.read(key, offset, maximum, None, &scratch, base)).await
593                    }
594                })
595                .await
596        })
597    }
598
599    fn abandon_file<'a>(&'a self, file: &'a StagedFile) -> StorageFuture<'a, ()> {
600        Box::pin(async move {
601            let context = crate::execution::Context::current();
602            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "abandon_file", outcome = "cancelled"));
603            context.result(span, self.file(file.clone(), |files, key| files.abort(key, true, false))).await
604        })
605    }
606    fn supports_file_deletion(&self) -> bool {
607        true
608    }
609    fn delete_file<'a>(&'a self, file: &'a StagedFile) -> StorageFuture<'a, ()> {
610        Box::pin(async move {
611            let context = crate::execution::Context::current();
612            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "delete_file", outcome = "cancelled"));
613            context.result(span, self.file(file.clone(), |files, key| files.abort(key, false, true))).await
614        })
615    }
616    fn supports_mutation_receipts(&self) -> bool {
617        true
618    }
619    fn save_mutations(&self, receipts: StorageBatch<MutationReceipt>) -> StorageFuture<'_, ()> {
620        Box::pin(async move {
621            let context = crate::execution::Context::current();
622            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "save_mutations", outcome = "cancelled"));
623            context
624                .result(span, {
625                    let files = self.files.clone();
626                    self.call(move |log| {
627                        for receipt in receipts.as_slice() {
628                            files.save_receipt(receipt, log.saved)?;
629                        }
630                        Ok(())
631                    })
632                })
633                .await
634        })
635    }
636    fn recover_mutations(&self, limits: ReadLimits, memory: RetainedMemory) -> StorageFuture<'_, Vec<MutationReceipt>> {
637        Box::pin(async move {
638            let context = crate::execution::Context::current();
639            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "recover_mutations", outcome = "cancelled"));
640            context
641                .result(span, {
642                    let files = self.files.clone();
643                    self.read(memory, move |_, memory| files.receipts(limits, memory))
644                })
645                .await
646        })
647    }
648    fn read_identities<'a>(
649        &'a self, snapshot: &'a BackendSnapshot, selection: &'a IdentitySelection, page: ScanPage, memory: RetainedMemory,
650    ) -> StorageFuture<'a, Vec<StoredIdentity>> {
651        Box::pin(async move {
652            let context = crate::execution::Context::current();
653            let span = context.sync(|| tracing::debug_span!("storage.json.backend", method = "read_identities", outcome = "cancelled"));
654            context
655                .result(span, {
656                    self.read_selected(snapshot, selection, memory, move |log, snapshot, selection, memory, base| {
657                        log.catalog(snapshot, selection, page, memory, base)
658                    })
659                })
660                .await
661        })
662    }
663}
664
665/* === Public Functions === */
666
667/* === Private Functions === */
668
669fn task(error: tokio::task::JoinError) -> Error {
670    Error::backend("JSON storage task", BackendFailure::Permanent, error)
671}
672
673/* === Tests === */