use std::sync::Arc;
use crate::mvcc::{
SafeSnapshot, SerializableGraph, SerializableParticipant, SerializablePredicate,
SerializableTransactionId, VersionError, VersionResult, VersionedPersistence,
};
use crate::read_control::StorageReadControl;
use crate::StorageBackendResult;
#[derive(Clone, Copy, Debug, Default)]
pub struct SerializableSnapshotOptions {
pub read_only: bool,
pub deferrable: bool,
}
pub type SerializableSnapshotCapture<'a> =
dyn FnMut(&mut dyn FnMut() -> VersionResult<()>) -> VersionResult<()> + 'a;
pub trait SerializableSession: Send + Sync {
fn establish_serializable_snapshot(&self) -> StorageBackendResult<SerializableReadContext>;
fn establish_serializable_snapshot_with(
&self,
options: SerializableSnapshotOptions,
capture: &mut SerializableSnapshotCapture<'_>,
) -> StorageBackendResult<SerializableReadContext>;
fn serializable_read_context(&self) -> StorageBackendResult<Option<SerializableReadContext>>;
fn observe_serializable_write(
&self,
predicate: SerializablePredicate<'_>,
) -> StorageBackendResult<()>;
}
impl SerializableSession for super::VersionedKeyValueStore {
fn establish_serializable_snapshot(&self) -> StorageBackendResult<SerializableReadContext> {
Self::establish_serializable_snapshot(self)
}
fn establish_serializable_snapshot_with(
&self,
options: SerializableSnapshotOptions,
capture: &mut SerializableSnapshotCapture<'_>,
) -> StorageBackendResult<SerializableReadContext> {
self.require_mutable_session()?;
self.active
.lock()
.as_mut()
.ok_or_else(super::no_transaction)?
.establish_serializable_with(&self.persistence, options, capture, &self.write_control())
.map_err(VersionError::into_storage_error)
}
fn serializable_read_context(&self) -> StorageBackendResult<Option<SerializableReadContext>> {
if let Some(view) = &self.retained {
return Ok(view.serializable().cloned());
}
let active = self.active.lock();
let Some(transaction) = active.as_ref() else {
return Ok(None);
};
transaction
.unsealed()
.map_err(VersionError::into_storage_error)?;
Ok(transaction.serializable_context().cloned())
}
fn observe_serializable_write(
&self,
predicate: SerializablePredicate<'_>,
) -> StorageBackendResult<()> {
self.require_mutable_session()?;
Self::observe_serializable_write(self, predicate)
}
}
#[derive(Clone)]
pub struct SerializableReadContext {
pub(super) persistence: Arc<dyn VersionedPersistence>,
pub(super) participant: SerializableParticipant,
pub(super) memory: uqa_core::memory::MemoryBudget,
pub(super) safe: bool,
}
impl SerializableReadContext {
pub fn id(&self) -> SerializableTransactionId {
self.participant.id()
}
pub fn read_control(&self, cancellation: &uqa_core::CancellationToken) -> StorageReadControl {
StorageReadControl::new(&self.memory, cancellation)
}
pub fn observe_read(
&self,
predicate: SerializablePredicate<'_>,
control: &StorageReadControl,
) -> VersionResult<()> {
self.with_graph(control, |graph| {
if self.safe {
graph.check_active(self.id())
} else {
graph.observe_read(self.id(), predicate, control)
}
})
}
pub fn safe_snapshot(&self, control: &StorageReadControl) -> VersionResult<SafeSnapshot> {
self.with_graph(control, |graph| graph.safe_snapshot(self.id(), control))
}
pub(super) fn with_graph<T>(
&self,
control: &StorageReadControl,
operation: impl FnOnce(&mut SerializableGraph) -> VersionResult<T>,
) -> VersionResult<T> {
let coordinator =
self.persistence
.serializable_coordinator()
.ok_or(VersionError::InvalidEncoding(
"serializable session lost its coordinator",
))?;
let mut operation = Some(operation);
let mut result = None;
coordinator.with_serializable_admission(control, &mut |graph, leases| {
let operation = operation.take().ok_or(VersionError::InvalidEncoding(
"serializable session operation was replayed",
))?;
crate::mvcc::serializable::recover(coordinator, graph, leases, control)?;
result = Some(operation(graph)?);
Ok(())
})?;
result.ok_or(VersionError::InvalidEncoding(
"serializable session operation was not invoked",
))
}
}