Skip to main content

uqa_storage/mvcc/session/
mod.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Logical Key/Value sessions retain private changes rather than a physical writer.
8
9mod batch;
10mod evaluation;
11mod notifications;
12mod observed;
13mod read;
14pub use read::RecordRead;
15mod sequence_values;
16pub use sequence_values::SequencePublicationLease;
17mod serializable;
18pub use serializable::{
19    SerializableReadContext, SerializableSession, SerializableSnapshotCapture,
20    SerializableSnapshotOptions,
21};
22mod transaction;
23mod vector_fields;
24pub use vector_fields::VectorFieldGuardMaintenance;
25
26use std::sync::Arc;
27
28use parking_lot::{Mutex, MutexGuard};
29
30use crate::read_control::{KeyValueReadVisitor, StorageReadControl, ValueReadVisitor};
31use crate::{
32    KeyValueBatch, KeyValueStore, PersistentStorageIdentity, StorageBackendError,
33    StorageBackendResult, StorageSessionAffinity,
34};
35
36use super::{
37    CommitFailure, MergedRecordSnapshot, StorageTransactionId, VersionError, VersionResult,
38    VersionedPersistence,
39};
40use transaction::Transaction;
41
42/// Retention allowance for one logical session, including private history, savepoints, batches and retained views. Once more than half of it is used, the private changes of a transaction move into sorted runs in encrypted temporary files, so the size of a transaction is bounded by disk; a single value or batch that does not fit in it is still an error.
43#[derive(Debug, Clone, Copy)]
44pub struct VersionedSessionOptions {
45    pub retained_bytes: usize,
46}
47
48impl Default for VersionedSessionOptions {
49    fn default() -> Self {
50        Self {
51            retained_bytes: 64 * 1024 * 1024,
52        }
53    }
54}
55
56/// A provider-independent session with pinned reads and conditional atomic publication. Common storage resolves typed index changes before physical admission; SQL isolation and tuple locks remain above this byte-record contract.
57pub struct VersionedKeyValueStore {
58    affinity: StorageSessionAffinity,
59    persistence: Arc<dyn VersionedPersistence>,
60    identity: Option<PersistentStorageIdentity>,
61    options: VersionedSessionOptions,
62    control: StorageReadControl,
63    write_cancellation: uqa_core::CancellationToken,
64    retained: Option<MergedRecordSnapshot>,
65    active: Mutex<Option<Transaction>>,
66    observed: Mutex<observed::ObservedWatermarks>,
67    /// The commit monitor value and the committed sequence of this session's last capture. While the monitor returns that value nothing was committed since, so the sequence is still the latest and a caller that asks only for it needs no capture.
68    latest: Mutex<Option<(u64, super::CommitSequence)>>,
69    completed: Mutex<Option<super::CommitReceipt>>,
70    /// Retained readers attribute sequence use to the original completion owner. Savepoints do not undo nontransactional sequence effects.
71    sequence_durability: Arc<sequence_values::SequenceDurability>,
72    sequence_generation: Option<u64>,
73}
74
75impl VersionedKeyValueStore {
76    fn evaluate_mutation(
77        &self,
78        operation: &mut crate::key_value::KeyValueMutation<'_>,
79        prefixes: Option<&[&[u8]]>,
80    ) -> StorageBackendResult<Option<crate::key_value::KeyValueReadRevision>> {
81        self.control.check()?;
82        let ((identity, sequence), receipt) = self.write_with_receipt(|transaction| {
83            let view = transaction.view()?;
84            let read = read::RecordRead {
85                view: &view,
86                database: self.persistence.database_id(),
87                control: &self.control,
88            };
89            let mut batch = batch::Batch::new(
90                self,
91                transaction
92                    .serializable_context()
93                    .map(SerializableReadContext::id),
94            );
95            operation(&read, &mut batch)?;
96            self.control.check()?;
97            batch.apply(transaction)?;
98            // Cache certification is optional and never changes the mutation outcome. The selected root is captured while this transaction is still exclusively borrowed.
99            let identity = prefixes.and_then(|prefixes| {
100                let staged = transaction.view().ok()?;
101                crate::key_value::KeyValueRead::revision(
102                    &read::RecordRead {
103                        view: &staged,
104                        database: self.persistence.database_id(),
105                        control: &self.control,
106                    },
107                    prefixes,
108                )
109                .ok()
110            });
111            Ok((identity, view.sequence()))
112        })?;
113        Ok(match (identity, receipt) {
114            // The immediately following commit cannot have rebased over a peer. This receipt was captured under the same session mutex, never from a later read.
115            (Some(_), Some(receipt)) if sequence.successor().ok() == Some(receipt.sequence) => {
116                Some(crate::key_value::KeyValueReadRevision::records(
117                    self.persistence.database_id(),
118                    receipt.sequence,
119                    None,
120                ))
121            }
122            (identity, _) => identity,
123        })
124    }
125    /// Evaluate one provider mutation against the same fixed native record view as its batch. The callback must not reenter this session or complete its batch. Common MVCC owns autocommit, failed-evaluation cleanup and receipt retry; callbacks are never replayed.
126    pub fn with_versioned_record_mutation<T>(
127        &self,
128        operation: impl FnOnce(
129            super::StorageMutationOrigin,
130            MergedRecordSnapshot,
131            &mut dyn KeyValueBatch,
132        ) -> StorageBackendResult<T>,
133    ) -> StorageBackendResult<T> {
134        self.control.check()?;
135        self.write(|transaction| {
136            let origin = transaction.mutation_origin(&*self.persistence, &self.write_control())?;
137            let view = transaction.view()?;
138            let mut batch = batch::Batch::new(
139                self,
140                transaction
141                    .serializable_context()
142                    .map(SerializableReadContext::id),
143            );
144            let result = operation(origin, view, &mut batch)?;
145            self.control.check()?;
146            batch.apply(transaction)?;
147            Ok(result)
148        })
149    }
150
151    /// Reclaim committed history without changing an active transaction or its retained readers. The persistence owner supplies atomic snapshot admission and physical deletion.
152    pub fn reclaim_versions(&self) -> StorageBackendResult<u64> {
153        let control = self.write_control();
154        VectorFieldGuardMaintenance::run(self, &control)?;
155        let removed = self
156            .persistence
157            .reclaim_versions(&control)
158            .map_err(VersionError::into_storage_error)?;
159        self.persistence
160            .reclaim_diskann_tombstones(&control)
161            .map_err(VersionError::into_storage_error)?;
162        Ok(removed)
163    }
164
165    /// Autonomous watermark visibility follows physical reservations, including reservations made after this session pinned its record snapshot. No write capability is required and no logical transaction is started or finished.
166    pub fn identifier_watermark(&self, namespace: &[u8]) -> StorageBackendResult<Option<u64>> {
167        self.persistence
168            .identifier_watermark(namespace, &self.control)
169            .map_err(VersionError::into_storage_error)
170    }
171
172    /// Reserve identities without publishing this session's private records. Read-only sessions and unresolved sealed commits cannot allocate; rollback never reclaims a successful reservation.
173    pub fn allocate_identifiers(
174        &self,
175        namespace: &[u8],
176        request: super::IdentifierRequest,
177    ) -> StorageBackendResult<super::IdentifierAllocation> {
178        let _active = self.admit_identifier_allocation()?;
179        self.allocate_admitted_identifiers(namespace, request)
180    }
181
182    /// Raise the watermark of `namespace` to at least `value` without reporting it. An allocation reports the current watermark and so is always physical; this answers from a watermark the session has already read when that covers `value`. The admission of an allocation applies either way.
183    pub fn observe_identifier(
184        &self,
185        namespace: &[u8],
186        value: u64,
187    ) -> StorageBackendResult<super::ObservedIdentifier> {
188        let _active = self.admit_identifier_allocation()?;
189        if self.observed.lock().covers(namespace, value) {
190            return Ok(super::ObservedIdentifier::Covered);
191        }
192        self.allocate_admitted_identifiers(namespace, super::IdentifierRequest::Observe(value))
193            .map(|allocation| super::ObservedIdentifier::Observed {
194                previous: allocation.previous(),
195            })
196    }
197
198    /// Admit an identifier allocation: the session can write and its transaction, when it has one, has not been sealed. The guard keeps the transaction from changing while the allocation runs.
199    fn admit_identifier_allocation(
200        &self,
201    ) -> StorageBackendResult<MutexGuard<'_, Option<Transaction>>> {
202        self.require_mutable_session()?;
203        let active = self.active.lock();
204        if let Some(transaction) = active.as_ref() {
205            transaction
206                .writable()
207                .map_err(VersionError::into_storage_error)?;
208        }
209        Ok(active)
210    }
211
212    fn allocate_admitted_identifiers(
213        &self,
214        namespace: &[u8],
215        request: super::IdentifierRequest,
216    ) -> StorageBackendResult<super::IdentifierAllocation> {
217        let allocation = self
218            .persistence
219            .allocate_identifiers(namespace, request, &self.write_control())
220            .map_err(VersionError::into_storage_error)?;
221        // A row written with a reserved identity observes it again; the reservation already covers that.
222        self.observed
223            .lock()
224            .record(namespace, allocation.watermark());
225        Ok(allocation)
226    }
227
228    pub fn new(
229        persistence: Arc<dyn VersionedPersistence>,
230        identity: Option<PersistentStorageIdentity>,
231        options: VersionedSessionOptions,
232    ) -> Self {
233        Self::new_with_cancellation(
234            persistence,
235            identity,
236            options,
237            uqa_core::CancellationToken::new(),
238        )
239    }
240
241    /// Share the invoking execution's cancellation during autonomous allocation and publication. Retained reads and rollback cleanup keep their independent control, so cancelling a statement cannot prevent its undo.
242    pub fn new_with_cancellation(
243        persistence: Arc<dyn VersionedPersistence>,
244        identity: Option<PersistentStorageIdentity>,
245        options: VersionedSessionOptions,
246        write_cancellation: uqa_core::CancellationToken,
247    ) -> Self {
248        Self {
249            affinity: StorageSessionAffinity::new(),
250            persistence,
251            identity,
252            options,
253            control: StorageReadControl::with_limit(options.retained_bytes),
254            write_cancellation,
255            retained: None,
256            active: Mutex::new(None),
257            observed: Mutex::default(),
258            latest: Mutex::new(None),
259            completed: Mutex::new(None),
260            sequence_durability: Arc::default(),
261            sequence_generation: None,
262        }
263    }
264
265    /// Create an independent logical session with the same persistence and retention limit.
266    pub fn new_session(&self) -> Self {
267        Self::new(
268            Arc::clone(&self.persistence),
269            self.identity.clone(),
270            self.options,
271        )
272    }
273
274    /// Keep a separate transaction context and retention budget while inheriting the invoking writer's cancellation.
275    pub fn new_session_with_cancellation(
276        &self,
277        cancellation: &uqa_core::CancellationToken,
278    ) -> Self {
279        Self::new_with_cancellation(
280            Arc::clone(&self.persistence),
281            self.identity.clone(),
282            self.options,
283            cancellation.clone(),
284        )
285    }
286
287    /// Keep an independent writer while sharing the invoking operation's memory allowance. Read/rollback cancellation stays independent so cleanup remains possible after write cancellation.
288    pub fn new_controlled_session(&self, control: &StorageReadControl) -> Self {
289        let mut session = self.new_session_with_cancellation(control.cancellation());
290        session.control = StorageReadControl::new(control.memory(), session.control.cancellation());
291        session
292    }
293
294    /// Open an independently owned read-only session at this exact committed/private view. Its original participant, snapshot leases and retention allowance remain shared, while read cancellation stays independent. Completing this reader never completes the originating transaction.
295    pub fn new_retained_read_session(
296        &self,
297        cancellation: &uqa_core::CancellationToken,
298    ) -> StorageBackendResult<Self> {
299        cancellation.check()?;
300        let active = self.active.lock();
301        let retained = self
302            .view_locked(active.as_ref())
303            .map_err(VersionError::into_storage_error)?;
304        let mut session = self.new_session_with_cancellation(cancellation);
305        session.control =
306            StorageReadControl::new(self.control.memory(), session.control.cancellation());
307        session.retained = Some(retained);
308        session.sequence_durability = Arc::clone(&self.sequence_durability);
309        session.sequence_generation = Some(
310            self.sequence_generation
311                .unwrap_or_else(|| self.sequence_durability.generation()),
312        );
313        Ok(session)
314    }
315
316    fn require_mutable_session(&self) -> StorageBackendResult<()> {
317        if self.retained.is_some() {
318            return Err(StorageBackendError::Other(
319                "cannot write through a retained read-only session".into(),
320            ));
321        }
322        Ok(())
323    }
324
325    fn write_control(&self) -> StorageReadControl {
326        StorageReadControl::new(self.control.memory(), &self.write_cancellation)
327    }
328
329    /// Native providers certify definition preservation using effects already observed during the physical commit.
330    pub fn committed_data_revision(&self) -> Option<crate::CommittedDataRevision> {
331        let receipt = self.completed_commit()?;
332        self.persistence
333            .commit_preserves_catalog_definitions(receipt)
334            .then(|| crate::CommittedDataRevision::from_receipt(receipt))
335    }
336
337    pub fn options(&self) -> VersionedSessionOptions {
338        self.options
339    }
340
341    /// Retain the current committed and private boundaries for a compound provider read. Later session mutations, rollback and commit do not advance this view.
342    pub fn record_snapshot(&self) -> VersionResult<MergedRecordSnapshot> {
343        self.view()
344    }
345
346    /// Advance an active transaction to the current committed boundary while retaining and rebasing its evaluated private changes. Retained readers keep their previous boundary; savepoint undo restores its matching private changes and committed base. SQL chooses when its isolation level allows this transition. A sealed commit cannot advance, and failed preparation leaves the active view unchanged.
347    pub fn refresh_transaction_snapshot(
348        &self,
349        cancellation: &uqa_core::CancellationToken,
350    ) -> StorageBackendResult<()> {
351        cancellation.check()?;
352        let control = StorageReadControl::new(self.control.memory(), cancellation);
353        let mut active = self.active.lock();
354        if self.retained.is_some() {
355            active.as_ref().ok_or_else(no_transaction)?;
356            return Ok(());
357        }
358        active
359            .as_mut()
360            .ok_or_else(no_transaction)?
361            .refresh(&*self.persistence, &control)
362            .map_err(VersionError::into_storage_error)
363    }
364
365    /// Share this session's retention allowance with provider codecs and retained record readers. Creating a control clone does not create another allowance.
366    pub fn retention_control(&self) -> StorageReadControl {
367        self.control.clone()
368    }
369
370    pub fn session_affinity(&self) -> StorageSessionAffinity {
371        self.affinity.clone()
372    }
373
374    /// Identify a sealed attempt after a failed commit. Retrying `commit_transaction` resubmits only the identical evaluated batch, and never reevaluates application code.
375    pub fn pending_commit(&self) -> Option<StorageTransactionId> {
376        self.active
377            .lock()
378            .as_ref()
379            .and_then(Transaction::pending_commit)
380    }
381
382    /// Identify a retained physical attempt or uncertain logical completion, including an empty/read-only SSI transaction without a write receipt. Completion retries never replay evaluated application work.
383    pub fn pending_transaction_completion(&self) -> Option<super::TransactionOutcomeId> {
384        self.active
385            .lock()
386            .as_ref()
387            .and_then(Transaction::pending_completion)
388    }
389
390    /// Establish the original SSI participant and fixed record boundary before this transaction's first logical access. SQL must call this at its first snapshot-bearing statement, then supply logical observations for every participating access path. Capability presence alone does not enable SQL SSI or fence legacy publishers.
391    pub fn establish_serializable_snapshot(&self) -> StorageBackendResult<SerializableReadContext> {
392        self.require_mutable_session()?;
393        self.active
394            .lock()
395            .as_mut()
396            .ok_or_else(no_transaction)?
397            .establish_serializable(&self.persistence, &self.write_control())
398            .map_err(VersionError::into_storage_error)
399    }
400
401    /// Register a logical write before staging its records. SQL statement/savepoint ownership must encompass both this observation and the evaluated mutation. Retained record readers expose only read observation capability.
402    pub fn observe_serializable_write(
403        &self,
404        predicate: super::SerializablePredicate<'_>,
405    ) -> StorageBackendResult<()> {
406        let mut active = self.active.lock();
407        let transaction = active.as_mut().ok_or_else(no_transaction)?;
408        transaction
409            .writable()
410            .map_err(VersionError::into_storage_error)?;
411        let context = transaction.serializable_context().ok_or_else(|| {
412            StorageBackendError::Other("no serializable participant in this session".into())
413        })?;
414        let control = self.write_control();
415        context
416            .with_graph(&control, |graph| {
417                graph.observe_write(context.id(), predicate, &control)
418            })
419            .map_err(VersionError::into_storage_error)
420    }
421
422    fn begin(&self, read_only: bool) -> StorageBackendResult<()> {
423        if !read_only {
424            self.require_mutable_session()?;
425        }
426        let mut active = self.active.lock();
427        if active.is_some() {
428            return Err(StorageBackendError::Other(
429                "a KeyValue transaction is already active".into(),
430            ));
431        }
432        *self.completed.lock() = None;
433        let transaction = match self.retained.as_ref() {
434            Some(view) => Transaction::at_snapshot(view.retain_committed(), true, &self.control),
435            None => self
436                .capture(read_only)
437                .map_err(VersionError::into_storage_error)?,
438        };
439        if self.retained.is_none() {
440            self.sequence_durability.begin()?;
441        }
442        *active = Some(transaction);
443        Ok(())
444    }
445
446    /// Begin a transaction at the latest commit, and remember what the capture found for [`Self::latest_sequence`].
447    fn capture(&self, read_only: bool) -> VersionResult<Transaction> {
448        let transaction = Transaction::new(&*self.persistence, read_only, &self.control)?;
449        *self.latest.lock() = transaction.captured();
450        Ok(transaction)
451    }
452
453    /// The sequence of the latest commit, as a capture would read it. A capture is a physical read with a lease registration; the monitor is one counter read.
454    fn latest_sequence(&self) -> VersionResult<super::CommitSequence> {
455        if let Some(monitor) = self.persistence.commit_monitor_version()? {
456            if let Some((_, sequence)) = self
457                .latest
458                .lock()
459                .filter(|(captured_at, _)| *captured_at == monitor)
460            {
461                return Ok(sequence);
462            }
463        }
464        Ok(self.capture(true)?.view()?.sequence())
465    }
466
467    fn view(&self) -> VersionResult<MergedRecordSnapshot> {
468        self.view_locked(self.active.lock().as_ref())
469    }
470
471    fn view_locked(&self, active: Option<&Transaction>) -> VersionResult<MergedRecordSnapshot> {
472        if let Some(view) = self.retained.as_ref() {
473            return view.try_clone();
474        }
475        if let Some(transaction) = active {
476            transaction.view()
477        } else {
478            self.capture(true)?.view()
479        }
480    }
481
482    fn write<T>(
483        &self,
484        operation: impl FnOnce(&mut Transaction) -> VersionResult<T>,
485    ) -> StorageBackendResult<T> {
486        self.write_with_receipt(operation).map(|(value, _)| value)
487    }
488
489    fn write_with_receipt<T>(
490        &self,
491        operation: impl FnOnce(&mut Transaction) -> VersionResult<T>,
492    ) -> StorageBackendResult<(T, Option<super::CommitReceipt>)> {
493        self.require_mutable_session()?;
494        let mut active = self.active.lock();
495        if let Some(transaction) = active.as_mut() {
496            return transaction
497                .atomic(operation)
498                .map(|value| (value, None))
499                .map_err(VersionError::into_storage_error);
500        }
501        *self.completed.lock() = None;
502        let transaction = Transaction::new(&*self.persistence, false, &self.control)
503            .map_err(VersionError::into_storage_error)?;
504        let result = self.evaluate_autocommit(&mut active, transaction, operation)?;
505        active
506            .as_mut()
507            .expect("retained attempt")
508            .commit(&*self.persistence, &self.write_control())?;
509        active
510            .as_ref()
511            .expect("retained attempt")
512            .acknowledge_completion(&*self.persistence, &self.control)?;
513        let receipt = active.as_ref().and_then(Transaction::committed_receipt);
514        *self.completed.lock() = receipt;
515        *active = None;
516        Ok((result, receipt))
517    }
518
519    fn savepoint_action(
520        &self,
521        operation: impl FnOnce(&mut Transaction) -> VersionResult<()>,
522    ) -> StorageBackendResult<()> {
523        let mut active = self.active.lock();
524        let transaction = active.as_mut().ok_or_else(no_transaction)?;
525        transaction
526            .unsealed()
527            .map_err(VersionError::into_storage_error)?;
528        operation(transaction).map_err(VersionError::into_storage_error)
529    }
530}
531
532impl super::IdentifierAllocator for VersionedKeyValueStore {
533    fn identifier_watermark(&self, namespace: &[u8]) -> StorageBackendResult<Option<u64>> {
534        Self::identifier_watermark(self, namespace)
535    }
536
537    fn allocate_identifiers(
538        &self,
539        namespace: &[u8],
540        request: super::IdentifierRequest,
541    ) -> StorageBackendResult<super::IdentifierAllocation> {
542        Self::allocate_identifiers(self, namespace, request)
543    }
544
545    fn observe_identifier(
546        &self,
547        namespace: &[u8],
548        value: u64,
549    ) -> StorageBackendResult<super::ObservedIdentifier> {
550        Self::observe_identifier(self, namespace, value)
551    }
552}
553
554impl KeyValueStore for VersionedKeyValueStore {
555    fn resource_leases(&self) -> Option<&dyn super::ResourceLeaseProvider> {
556        self.persistence.resource_leases()
557    }
558
559    fn retention_control(&self) -> Option<StorageReadControl> {
560        Some(Self::retention_control(self))
561    }
562
563    fn notification_publications(
564        &self,
565    ) -> Option<&dyn crate::notifications::NotificationPublicationStore> {
566        Some(self)
567    }
568
569    fn serializable_session(&self) -> Option<&dyn SerializableSession> {
570        self.persistence
571            .serializable_coordinator()
572            .map(|_| self as &dyn SerializableSession)
573    }
574
575    fn reclaim_obsolete(&self) -> StorageBackendResult<()> {
576        let active = self.active.lock();
577        if active.is_some() {
578            return Err(StorageBackendError::Other(
579                "vacuum requires an inactive logical session".into(),
580            ));
581        }
582        let control = self.write_control();
583        let source: Arc<dyn KeyValueStore> =
584            Arc::new(self.new_session_with_cancellation(control.cancellation()));
585        crate::key_value::KeyValueDiskANNMaintenance::run(&source, &control)?;
586        self.reclaim_versions().map(|_| ())
587    }
588
589    fn write_cancellation(&self) -> Option<uqa_core::CancellationToken> {
590        Some(self.write_cancellation.clone())
591    }
592
593    fn open_controlled_session(
594        &self,
595        control: &StorageReadControl,
596    ) -> StorageBackendResult<Arc<dyn KeyValueStore>> {
597        control.check()?;
598        Ok(Arc::new(self.new_controlled_session(control)))
599    }
600
601    fn open_session_with_cancellation(
602        &self,
603        cancellation: &uqa_core::CancellationToken,
604    ) -> StorageBackendResult<Arc<dyn KeyValueStore>> {
605        cancellation.check()?;
606        Ok(Arc::new(self.new_session_with_cancellation(cancellation)))
607    }
608
609    fn open_retained_read_session(
610        &self,
611        cancellation: &uqa_core::CancellationToken,
612    ) -> StorageBackendResult<Arc<dyn KeyValueStore>> {
613        Ok(Arc::new(self.new_retained_read_session(cancellation)?))
614    }
615
616    fn transaction_model(&self) -> crate::StorageTransactionModel {
617        crate::StorageTransactionModel::VersionedConcurrent {
618            database: self.persistence.database_id(),
619        }
620    }
621
622    fn refresh_transaction_snapshot(
623        &self,
624        cancellation: &uqa_core::CancellationToken,
625    ) -> StorageBackendResult<()> {
626        Self::refresh_transaction_snapshot(self, cancellation)
627    }
628
629    fn identifier_allocator(&self) -> Option<&dyn super::IdentifierAllocator> {
630        Some(self)
631    }
632
633    fn with_read_view(
634        &self,
635        operation: &mut crate::key_value::KeyValueReadScope<'_>,
636    ) -> StorageBackendResult<()> {
637        self.control.check()?;
638        let view = self.view().map_err(VersionError::into_storage_error)?;
639        operation(&read::RecordRead {
640            view: &view,
641            database: self.persistence.database_id(),
642            control: &self.control,
643        })?;
644        self.control.check()
645    }
646
647    fn with_mutation(
648        &self,
649        operation: &mut crate::key_value::KeyValueMutation<'_>,
650    ) -> StorageBackendResult<()> {
651        self.evaluate_mutation(operation, None).map(|_| ())
652    }
653
654    fn with_mutation_revision(
655        &self,
656        prefixes: &[&[u8]],
657        operation: &mut crate::key_value::KeyValueMutation<'_>,
658    ) -> StorageBackendResult<Option<crate::key_value::KeyValueReadRevision>> {
659        self.evaluate_mutation(operation, Some(prefixes))
660    }
661
662    fn with_versioned_mutation(
663        &self,
664        operation: &mut crate::key_value::KeyValueVersionedMutation<'_>,
665    ) -> StorageBackendResult<()> {
666        self.with_versioned_record_mutation(|origin, view, batch| {
667            operation(
668                origin,
669                &read::RecordRead {
670                    view: &view,
671                    database: self.persistence.database_id(),
672                    control: &self.control,
673                },
674                batch,
675            )
676        })
677    }
678
679    fn transaction_affinity(&self) -> Option<StorageSessionAffinity> {
680        Some(self.session_affinity())
681    }
682
683    fn storage_identity(&self) -> StorageBackendResult<Option<PersistentStorageIdentity>> {
684        Ok(self.identity.clone())
685    }
686
687    fn open_session(&self) -> StorageBackendResult<Arc<dyn KeyValueStore>> {
688        Ok(Arc::new(self.new_session()))
689    }
690
691    fn get(&self, key: &[u8]) -> StorageBackendResult<Option<Vec<u8>>> {
692        let mut value = None;
693        self.visit_value(key, &self.control, &mut |bytes| {
694            value = bytes.map(<[u8]>::to_vec);
695            Ok(())
696        })?;
697        Ok(value)
698    }
699
700    fn contains_key(&self, key: &[u8]) -> StorageBackendResult<bool> {
701        Ok(self
702            .view()
703            .and_then(|view| view.metadata(key, &self.control))
704            .map_err(VersionError::into_storage_error)?
705            .is_some_and(|record| record.live))
706    }
707
708    fn contains_prefix_budgeted(
709        &self,
710        prefix: &[u8],
711        control: &StorageReadControl,
712    ) -> StorageBackendResult<bool> {
713        control.check()?;
714        let mut found = false;
715        self.view()
716            .map_err(VersionError::into_storage_error)?
717            .visit_keys(prefix, None, usize::MAX, control, &mut |_, record| {
718                found = record.live;
719                Ok(!found)
720            })
721            .map_err(VersionError::into_storage_error)?;
722        Ok(found)
723    }
724
725    fn visit_value(
726        &self,
727        key: &[u8],
728        control: &StorageReadControl,
729        visit: &mut ValueReadVisitor<'_>,
730    ) -> StorageBackendResult<()> {
731        control.check()?;
732        self.view()
733            .map_err(VersionError::into_storage_error)?
734            .visit_value(key, control, &mut |record| {
735                visit(record.and_then(|record| record.value)).map_err(VersionError::from)
736            })
737            .map_err(VersionError::into_storage_error)
738    }
739
740    fn visit_value_bounded(
741        &self,
742        key: &[u8],
743        max_bytes: usize,
744        control: &StorageReadControl,
745        visit: &mut ValueReadVisitor<'_>,
746    ) -> StorageBackendResult<()> {
747        control.check()?;
748        self.view()
749            .map_err(VersionError::into_storage_error)?
750            .visit_value_bounded(key, max_bytes, control, &mut |record| {
751                visit(record.and_then(|record| record.value)).map_err(VersionError::from)
752            })
753            .map_err(VersionError::into_storage_error)
754    }
755
756    fn visit_prefix_after(
757        &self,
758        prefix: &[u8],
759        after: Option<&[u8]>,
760        limit: usize,
761        control: &StorageReadControl,
762        visit: &mut KeyValueReadVisitor<'_>,
763    ) -> StorageBackendResult<()> {
764        control.check()?;
765        if limit == 0 {
766            return Ok(());
767        }
768        let view = self.view().map_err(VersionError::into_storage_error)?;
769        read::visit_live(&view, prefix, after, limit, control, visit)
770            .map_err(VersionError::into_storage_error)
771    }
772
773    fn put(&self, key: &[u8], value: &[u8]) -> StorageBackendResult<()> {
774        self.write(|transaction| transaction.replace(key, Some(value), &self.control))
775    }
776    fn delete(&self, key: &[u8]) -> StorageBackendResult<()> {
777        self.write(|transaction| transaction.replace(key, None, &self.control))
778    }
779
780    fn scan_prefix(&self, prefix: &[u8]) -> StorageBackendResult<Vec<(Vec<u8>, Vec<u8>)>> {
781        self.scan_prefix_after(prefix, None, usize::MAX)
782    }
783
784    fn scan_prefix_after(
785        &self,
786        prefix: &[u8],
787        after: Option<&[u8]>,
788        limit: usize,
789    ) -> StorageBackendResult<Vec<(Vec<u8>, Vec<u8>)>> {
790        let mut rows = Vec::new();
791        self.visit_prefix_after(prefix, after, limit, &self.control, &mut |key, value| {
792            rows.push((key.to_vec(), value.to_vec()));
793            Ok(())
794        })?;
795        Ok(rows)
796    }
797
798    fn scan_prefix_keys_after(
799        &self,
800        prefix: &[u8],
801        after: Option<&[u8]>,
802        limit: usize,
803    ) -> StorageBackendResult<Vec<Vec<u8>>> {
804        let mut keys = Vec::new();
805        if limit != 0 {
806            self.view()
807                .map_err(VersionError::into_storage_error)?
808                .visit_keys(
809                    prefix,
810                    after,
811                    usize::MAX,
812                    &self.control,
813                    &mut |key, record| {
814                        if record.live {
815                            keys.push(key.to_vec());
816                        }
817                        Ok(keys.len() < limit)
818                    },
819                )
820                .map_err(VersionError::into_storage_error)?;
821        }
822        Ok(keys)
823    }
824
825    fn first_prefix_after(
826        &self,
827        prefix: &[u8],
828        after: Option<&[u8]>,
829    ) -> StorageBackendResult<Option<(Vec<u8>, Vec<u8>)>> {
830        Ok(self.scan_prefix_after(prefix, after, 1)?.pop())
831    }
832    fn delete_prefix(&self, prefix: &[u8]) -> StorageBackendResult<usize> {
833        self.write(|transaction| transaction.delete_prefix(prefix, &self.control))
834    }
835    fn batch(&self) -> Box<dyn KeyValueBatch + '_> {
836        let participant = self
837            .active
838            .lock()
839            .as_ref()
840            .and_then(Transaction::serializable_context)
841            .map(SerializableReadContext::id);
842        Box::new(batch::Batch::new(self, participant))
843    }
844    fn begin_transaction(&self) -> StorageBackendResult<()> {
845        self.begin(false)
846    }
847    fn begin_read_transaction(&self) -> StorageBackendResult<()> {
848        self.begin(true)
849    }
850    fn begin_upgradeable_transaction(&self) -> StorageBackendResult<()> {
851        self.begin(self.retained.is_some())
852    }
853    fn in_transaction(&self) -> bool {
854        self.active.lock().is_some()
855    }
856    fn transaction_has_written(&self) -> StorageBackendResult<bool> {
857        Ok(self.active.lock().as_ref().is_some_and(|transaction| {
858            transaction.changes.has_written() || transaction.has_derived_changes()
859        }))
860    }
861    fn change_version(&self) -> StorageBackendResult<Option<u64>> {
862        let pinned = match self.retained.as_ref() {
863            Some(view) => Some(view.sequence()),
864            None => self
865                .active
866                .lock()
867                .as_ref()
868                .map(Transaction::view)
869                .transpose()
870                .map_err(VersionError::into_storage_error)?
871                .map(|view| view.sequence()),
872        };
873        let sequence = match pinned {
874            Some(sequence) => sequence,
875            None => self
876                .latest_sequence()
877                .map_err(VersionError::into_storage_error)?,
878        };
879        Ok(Some(sequence.as_u64()))
880    }
881
882    fn read_view_revision(
883        &self,
884    ) -> StorageBackendResult<Option<crate::key_value::KeyValueReadRevision>> {
885        let view = self.view().map_err(VersionError::into_storage_error)?;
886        Ok(Some(crate::key_value::KeyValueReadRevision::records(
887            self.persistence.database_id(),
888            view.sequence(),
889            view.private_revision(),
890        )))
891    }
892
893    fn completed_commit(&self) -> Option<super::CommitReceipt> {
894        let active = self.active.lock();
895        if active.is_some() {
896            None
897        } else {
898            *self.completed.lock()
899        }
900    }
901
902    fn commit_transaction(&self) -> StorageBackendResult<()> {
903        let mut active = self.active.lock();
904        let sequence_completion = self
905            .retained
906            .is_none()
907            .then(|| self.sequence_durability.close());
908        let transaction = active.as_mut().ok_or_else(no_transaction)?;
909        // A sequence-only reader must synchronize before its logical SSI outcome becomes committed. Ordinary record publication can cover the same prefix itself.
910        let synchronized_first = !transaction.has_record_publication();
911        if synchronized_first {
912            self.synchronize_sequence_values(&self.write_control())
913                .map_err(|error| transaction.completion_error(error))?;
914        }
915        transaction.commit(&*self.persistence, &self.write_control())?;
916        if !synchronized_first {
917            self.synchronize_sequence_values(&self.control)
918                .map_err(|error| transaction.completion_error(error))?;
919        }
920        active
921            .as_ref()
922            .expect("completed attempt")
923            .acknowledge_completion(&*self.persistence, &self.control)?;
924        *self.completed.lock() = active.as_ref().and_then(Transaction::committed_receipt);
925        *active = None;
926        if sequence_completion.is_some() {
927            self.sequence_durability.clear();
928        }
929        Ok(())
930    }
931
932    fn rollback_transaction(&self) -> StorageBackendResult<()> {
933        let mut active = self.active.lock();
934        let sequence_completion = self
935            .retained
936            .is_none()
937            .then(|| self.sequence_durability.close());
938        active
939            .as_mut()
940            .ok_or_else(no_transaction)?
941            .abort(&*self.persistence, &self.control)?;
942        self.synchronize_sequence_values(&self.control)
943            .map_err(|error| {
944                active
945                    .as_ref()
946                    .expect("completed attempt")
947                    .completion_error(error)
948            })?;
949        active
950            .as_ref()
951            .expect("completed attempt")
952            .acknowledge_completion(&*self.persistence, &self.control)?;
953        *self.completed.lock() = None;
954        *active = None;
955        if sequence_completion.is_some() {
956            self.sequence_durability.clear();
957        }
958        Ok(())
959    }
960
961    fn savepoint(&self, name: &str) -> StorageBackendResult<()> {
962        self.savepoint_action(|transaction| transaction.savepoint(name, &self.control))
963    }
964    fn release_savepoint(&self, name: &str) -> StorageBackendResult<()> {
965        self.savepoint_action(|transaction| transaction.release(name))
966    }
967    fn rollback_to_savepoint(&self, name: &str) -> StorageBackendResult<()> {
968        self.savepoint_action(|transaction| transaction.rollback_to(name, &self.control))
969    }
970}
971
972fn no_transaction() -> StorageBackendError {
973    StorageBackendError::Other("no active KeyValue transaction".into())
974}
975
976fn commit_error(error: CommitFailure) -> StorageBackendError {
977    match error {
978        CommitFailure::Rejected(error) => error.into_storage_error(),
979        error @ CommitFailure::Indeterminate { .. } => StorageBackendError::backend("MVCC", error),
980    }
981}