taskchampion_sync_server_core/
storage.rs

1use chrono::{DateTime, Utc};
2use uuid::Uuid;
3
4/// A representation of stored metadata about a client.
5#[derive(Clone, PartialEq, Eq, Debug)]
6pub struct Client {
7    /// The latest version for this client (may be the nil version)
8    pub latest_version_id: Uuid,
9    /// Data about the latest snapshot for this client
10    pub snapshot: Option<Snapshot>,
11}
12
13/// Metadata about a snapshot, not including the snapshot data itself.
14#[derive(Clone, PartialEq, Eq, Debug)]
15pub struct Snapshot {
16    /// ID of the version at which this snapshot was made
17    pub version_id: Uuid,
18
19    /// Timestamp at which this snapshot was set
20    pub timestamp: DateTime<Utc>,
21
22    /// Number of versions since this snapshot was made
23    pub versions_since: u32,
24}
25
26#[derive(Clone, PartialEq, Eq, Debug)]
27pub struct Version {
28    /// The uuid identifying this version.
29    pub version_id: Uuid,
30    /// The uuid identifying this version's parent.
31    pub parent_version_id: Uuid,
32    /// The data carried in this version.
33    pub history_segment: Vec<u8>,
34}
35
36/// A transaction in the storage backend.
37///
38/// Transactions must be sequentially consistent. That is, the results of transactions performed
39/// in storage must be as if each were executed sequentially in some order. In particular,
40/// un-committed changes must not be read by another transaction, but committed changes must
41/// be visible to subequent transations. Together, this guarantees that `add_version` reliably
42/// constructs a linear sequence of versions.
43///
44/// Transactions with different client IDs cannot share any data, so it is safe to handle them
45/// concurrently.
46///
47/// Changes in a transaction that is dropped without calling `commit` must not appear in any other
48/// transaction.
49#[async_trait::async_trait(?Send)]
50pub trait StorageTxn {
51    /// Get information about the client for this transaction
52    async fn get_client(&mut self) -> anyhow::Result<Option<Client>>;
53
54    /// Create the client for this transaction, with the given latest_version_id. The client must
55    /// not already exist.
56    async fn new_client(&mut self, latest_version_id: Uuid) -> anyhow::Result<()>;
57
58    /// Set the client's most recent snapshot.
59    async fn set_snapshot(&mut self, snapshot: Snapshot, data: Vec<u8>) -> anyhow::Result<()>;
60
61    /// Get the data for the most recent snapshot.  The version_id
62    /// is used to verify that the snapshot is for the correct version.
63    async fn get_snapshot_data(&mut self, version_id: Uuid) -> anyhow::Result<Option<Vec<u8>>>;
64
65    /// Get a version, indexed by parent version id
66    async fn get_version_by_parent(
67        &mut self,
68        parent_version_id: Uuid,
69    ) -> anyhow::Result<Option<Version>>;
70
71    /// Get a version, indexed by its own version id
72    async fn get_version(&mut self, version_id: Uuid) -> anyhow::Result<Option<Version>>;
73
74    /// Add a version (that must not already exist), and
75    ///  - update latest_version_id from parent_version_id to version_id
76    ///  - increment snapshot.versions_since
77    /// Fails if the existing `latest_version_id` is not equal to `parent_version_id`. Check
78    /// this by calling `get_client` earlier in the same transaction.
79    async fn add_version(
80        &mut self,
81        version_id: Uuid,
82        parent_version_id: Uuid,
83        history_segment: Vec<u8>,
84    ) -> anyhow::Result<()>;
85
86    /// Commit any changes made in the transaction.  It is an error to call this more than
87    /// once.  It is safe to skip this call for read-only operations.
88    async fn commit(&mut self) -> anyhow::Result<()>;
89}
90
91/// A trait for objects able to act as storage.  Most of the interesting behavior is in the
92/// [`crate::storage::StorageTxn`] trait.
93#[async_trait::async_trait]
94pub trait Storage: Send + Sync {
95    /// Begin a transaction for the given client ID.
96    async fn txn(&self, client_id: Uuid) -> anyhow::Result<Box<dyn StorageTxn + '_>>;
97}