Skip to main content

nodedb_types/
calvin_passive.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Identity of one row a passive Calvin participant reads.
4//!
5//! A dependent-read transaction names the rows its passive vShards read. Each
6//! passive vShard reads them at its lock grant and broadcasts one value per
7//! row through the data-group log of every active vShard. The active vShard
8//! matches each value to its row by [`PassiveReadKeyId`].
9//!
10//! The identity is exact: a key-value row keeps its key bytes, so two keys
11//! never share an identity.
12
13use serde::{Deserialize, Serialize};
14
15use crate::QualifiedCollection;
16
17/// The row within a collection a passive read names.
18#[derive(
19    Debug,
20    Clone,
21    PartialEq,
22    Eq,
23    PartialOrd,
24    Ord,
25    Hash,
26    Serialize,
27    Deserialize,
28    zerompk::ToMessagePack,
29    zerompk::FromMessagePack,
30)]
31pub enum PassiveKey {
32    /// A document row, by its surrogate.
33    Surrogate { surrogate: u32 },
34    /// A key-value row, by its key bytes.
35    Kv { key: Vec<u8> },
36}
37
38/// Identity of one row a passive participant reads.
39///
40/// The map key of every passive read value. `Ord` is derived, collection
41/// first, so every replica iterates a map of them in one order.
42#[derive(
43    Debug,
44    Clone,
45    PartialEq,
46    Eq,
47    PartialOrd,
48    Ord,
49    Hash,
50    Serialize,
51    Deserialize,
52    zerompk::ToMessagePack,
53    zerompk::FromMessagePack,
54)]
55pub struct PassiveReadKeyId {
56    /// The database-qualified collection of the row.
57    pub collection: QualifiedCollection,
58    /// The row within the collection.
59    pub key: PassiveKey,
60}
61
62impl PassiveReadKeyId {
63    /// The identity of document row `surrogate` of `collection`.
64    pub fn surrogate(collection: QualifiedCollection, surrogate: u32) -> Self {
65        Self {
66            collection,
67            key: PassiveKey::Surrogate { surrogate },
68        }
69    }
70
71    /// The identity of key-value row `key` of `collection`.
72    pub fn kv(collection: QualifiedCollection, key: Vec<u8>) -> Self {
73        Self {
74            collection,
75            key: PassiveKey::Kv { key },
76        }
77    }
78
79    /// Bytes this identity adds to a replicated entry, estimated.
80    pub fn serialized_size_hint(&self) -> usize {
81        let key = match &self.key {
82            PassiveKey::Surrogate { .. } => 4,
83            PassiveKey::Kv { key } => key.len(),
84        };
85        self.collection.as_str().len() + key
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    /// Two key-value rows keep two identities, whatever their keys hash to.
94    #[test]
95    fn kv_rows_keep_distinct_identities() {
96        let collection = QualifiedCollection::from_stored("items".to_owned());
97        let a = PassiveReadKeyId::kv(collection.clone(), b"alice:sword".to_vec());
98        let b = PassiveReadKeyId::kv(collection, b"alice:shield".to_vec());
99        assert_ne!(a, b);
100    }
101
102    /// An identity survives a msgpack round trip.
103    #[test]
104    fn identity_round_trips_through_msgpack() {
105        let id = PassiveReadKeyId::surrogate(QualifiedCollection::from_stored("o".to_owned()), 9);
106        let bytes = zerompk::to_msgpack_vec(&id).expect("encode");
107        let back: PassiveReadKeyId = zerompk::from_msgpack(&bytes).expect("decode");
108        assert_eq!(back, id);
109    }
110}