1mod config;
15mod files;
16mod format;
17mod log;
18mod reads;
19mod sessions;
20
21pub use config::JsonStorageSettings;
24
25use std::{
29 num::NonZeroUsize,
30 sync::{Arc, atomic::Ordering},
31};
32
33use tokio::sync::Mutex;
35
36use crate::arrow::RetainedMemory;
38use crate::{
39 query::{FileSelection, ObjectSelection, RecordSelection, SchemaSelection, StreamSelection},
40 *,
41};
42use files::Files;
43use log::{Log, Row};
44
45pub struct JsonStorage {
54 settings: JsonStorageSettings,
55 history: StoreHistory,
56 owner: Arc<Mutex<Option<Log>>>,
57 files: Arc<Files>,
58}
59
60impl 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
665fn task(error: tokio::task::JoinError) -> Error {
670 Error::backend("JSON storage task", BackendFailure::Permanent, error)
671}
672
673