dpm_store/record.rs
1//! Records the store returns to adapters, available whether or not a SQLite backend is built.
2
3use crate::LineageError;
4use dpm_engine::Operation;
5use dpm_model::{LineageId, WorkspaceId};
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8
9/// A committed operation with the workspace and writable history it was recorded in; mutation
10/// results and history entries return the same object, so a resent operation is answered with
11/// exactly what the first attempt returned.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RecordedOperation {
14 /// Identity, actor, timestamp, revision precondition and semantic command.
15 #[serde(flatten)]
16 pub operation: Operation,
17 /// Workspace the operation changed.
18 pub workspace_id: WorkspaceId,
19 /// Lineage of the store that committed it; a restored store keeps the lineages of the
20 /// operations it copied and records later ones under its own.
21 pub lineage_id: LineageId,
22}
23
24/// One immutable operation addressed by its local append order.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct HistoryEntry {
27 /// Local database cursor, independent of wrapping domain revisions.
28 pub sequence: u64,
29 /// The recorded operation.
30 pub operation: RecordedOperation,
31}
32
33/// A bounded operation page read with a consistent authoritative revision.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct HistoryPage {
36 /// Revision visible in the same SQLite transaction.
37 pub revision: u64,
38 /// Lineage the store continues, within which the revision is meaningful; absent only for a
39 /// page an adapter builds without a store.
40 pub lineage_id: Option<LineageId>,
41 /// Operations in ascending append order.
42 pub entries: Vec<HistoryEntry>,
43 /// Last returned sequence, suitable for the next request; unchanged for an empty page.
44 pub next_after_sequence: u64,
45}
46
47/// The lineage row of one store file.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49pub struct StoreLineage {
50 /// Writable history this file continues.
51 pub lineage_id: LineageId,
52 /// A backup archive, refused for writing until restored into a new store.
53 pub archived: bool,
54}
55
56impl StoreLineage {
57 /// Refuse a write to an archive, or to a lineage other than the one the caller observed.
58 pub fn check_writable(
59 &self,
60 expected: Option<LineageId>,
61 path: &Path,
62 ) -> Result<(), LineageError> {
63 if self.archived {
64 return Err(LineageError::Archived {
65 path: path.to_path_buf(),
66 });
67 }
68 match expected {
69 Some(expected) if expected != self.lineage_id => Err(LineageError::Mismatch {
70 expected,
71 actual: self.lineage_id,
72 }),
73 _ => Ok(()),
74 }
75 }
76}
77
78/// The authoritative revision of a store and the lineage it belongs to, read without decoding the
79/// plan, so clients can poll it for changes.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81pub struct StoreRevision {
82 /// Revision of the committed snapshot.
83 pub revision: u64,
84 /// Lineage within which the revision is meaningful.
85 pub lineage: StoreLineage,
86 /// Local sequence of the newest committed operation, zero for a store with none. It is read in
87 /// the same transaction as `revision`, so it is the exact cursor from which a subscriber to
88 /// the operation history sees every change after that snapshot and no earlier one.
89 pub history_head: u64,
90}