Skip to main content

rushdb_types/
command.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use crate::ids::OwnerKind;
5use crate::ids::TenantId;
6use crate::model::{Cardinality, Nullability, TypeState};
7use crate::occurrence::{EmbeddingBinding, EmbeddingSpaceInfo, OccurrenceRow};
8
9/// Current deterministic command format. Only this version is accepted by
10/// the state-machine expander; it is independent of the platform wire protocol.
11/// Reset to 1 for the first release (no earlier format was ever shipped);
12/// bump it whenever a command type changes.
13pub const COMMAND_VERSION: u32 = 1;
14
15/// Deterministic mutation set applied atomically by the replicated state
16/// machine. Contains allocated IDs and encoded values; apply never reads a
17/// clock, RNG, network or parser (plan documents 05/07).
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct CommitBatch {
20    /// Command format version used to interpret the batch.
21    pub command_version: u32,
22    /// Tenant whose keyspace the batch mutates.
23    pub tenant: TenantId,
24    /// Client-visible transaction identifier.
25    pub transaction_id: String,
26    /// Caller-chosen idempotency key; replays discover the stored outcome.
27    pub commit_key: Uuid,
28    /// Commit index the batch is expected to be applied at.
29    pub start_index: u64,
30    /// Isolation level under which the batch was planned.
31    pub isolation: IsolationMode,
32    /// Conditions that must hold before any mutation is applied.
33    pub preconditions: Vec<Precondition>,
34    /// Ordered mutations applied atomically.
35    pub mutations: Vec<Mutation>,
36}
37
38/// Isolation level used when planning a batch.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40pub enum IsolationMode {
41    /// Observe reads committed before the batch was planned.
42    ReadCommitted,
43    /// Read a consistent snapshot taken at planning time.
44    Snapshot,
45    /// Fully serializable execution.
46    Serializable,
47}
48
49/// A single key condition guarding a batch.
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub struct Precondition {
52    /// Key the condition inspects.
53    pub key: Vec<u8>,
54    /// Condition evaluated against the key.
55    pub kind: PreconditionKind,
56}
57
58/// The two supported precondition checks.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60pub enum PreconditionKind {
61    /// The key must exist.
62    Exists,
63    /// The key must be absent.
64    Absent,
65}
66
67impl Precondition {
68    /// Require `key` to exist.
69    #[must_use]
70    pub fn exists(key: Vec<u8>) -> Self {
71        Precondition {
72            key,
73            kind: PreconditionKind::Exists,
74        }
75    }
76
77    /// Require `key` to be absent.
78    #[must_use]
79    pub fn absent(key: Vec<u8>) -> Self {
80        Precondition {
81            key,
82            kind: PreconditionKind::Absent,
83        }
84    }
85}
86
87/// A single deterministic mutation applied by the state machine.
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub enum Mutation {
90    /// Publish a tenant policy, using the applying CI as its generation.
91    SetSecurityPolicy {
92        /// Policy payload to publish.
93        policy: crate::security::DataAccessPolicy,
94        /// Enforcement strength for the policy.
95        mode: crate::security::AuthorizationMode,
96    },
97    /// Create a record header with the given id, ordinal and label.
98    CreateRecordHeader {
99        /// Public id of the new record.
100        record_id: [u8; 16],
101        /// Dense ordinal assigned to the record.
102        ordinal: u64,
103        /// Primary label of the record.
104        label: String,
105    },
106    /// Delete a record header by id, ordinal and label.
107    DeleteRecordHeader {
108        /// Dense ordinal of the record.
109        ordinal: u64,
110        /// Public id of the record.
111        record_id: [u8; 16],
112        /// Primary label of the record.
113        label: String,
114    },
115    /// Move a record between its single primary-label membership sets while
116    /// preserving its stable public id, ordinal, fields, and relationships.
117    RetypeRecordHeader {
118        /// Public id of the record.
119        record_id: [u8; 16],
120        /// Dense ordinal of the record.
121        ordinal: u64,
122        /// Label the record is leaving.
123        from_label: String,
124        /// Label the record is moving to.
125        to_label: String,
126    },
127    /// Create a relationship header between two records.
128    CreateRelationshipHeader {
129        /// Public id of the new relationship.
130        relationship_id: [u8; 16],
131        /// Dense ordinal assigned to the relationship.
132        ordinal: u64,
133        /// Relationship type.
134        rel_type: String,
135        /// Ordinal of the source record.
136        source_ord: u64,
137        /// Ordinal of the target record.
138        target_ord: u64,
139        /// Public id of the source record.
140        source_id: [u8; 16],
141        /// Public id of the target record.
142        target_id: [u8; 16],
143    },
144    /// Delete a relationship header by id, ordinal and type.
145    DeleteRelationshipHeader {
146        /// Public id of the relationship.
147        relationship_id: [u8; 16],
148        /// Dense ordinal of the relationship.
149        ordinal: u64,
150        /// Relationship type.
151        rel_type: String,
152        /// Ordinal of the source record.
153        source_ord: u64,
154        /// Ordinal of the target record.
155        target_ord: u64,
156    },
157    /// Create a property family if it does not already exist.
158    EnsureFamily {
159        /// Dense id of the family.
160        family_id: u64,
161        /// Namespace of the property.
162        namespace: String,
163        /// Whether the family is owned by records or relationships.
164        owner_scope: OwnerKind,
165        /// Normalized name used for lookup.
166        normalized_name: String,
167        /// Human-readable display name.
168        display_name: String,
169    },
170    /// Create a property definition within a family.
171    EnsureDefinition {
172        /// Dense id of the property.
173        property_id: u64,
174        /// Family the property belongs to.
175        family_id: u64,
176        /// Current type resolution state.
177        type_state: TypeState,
178        /// Allowed value cardinality.
179        cardinality: Cardinality,
180        /// Nullability constraint.
181        nullability: Nullability,
182    },
183    /// Atomically remove a property family: every occurrence of the family
184    /// (records and relationships), the family row/signature, and all property
185    /// definitions. Used by the protocol `deleteProperty` operation.
186    DeleteFamily {
187        /// Dense id of the family.
188        family_id: u64,
189        /// Namespace of the property.
190        namespace: String,
191        /// Whether the family is owned by records or relationships.
192        owner_scope: OwnerKind,
193        /// Normalized name used for lookup.
194        normalized_name: String,
195        /// Human-readable display name.
196        display_name: String,
197    },
198    /// Insert or update one property occurrence.
199    PutOccurrence {
200        /// Occurrence row to persist.
201        row: OccurrenceRow,
202        /// Canonical encoded payload, or `None` for payload-free states.
203        canonical_value_bytes: Option<Vec<u8>>,
204    },
205    /// Delete one property occurrence.
206    DeleteOccurrence {
207        /// Occurrence row to remove.
208        row: OccurrenceRow,
209    },
210    /// Create or update an embedding space.
211    EnsureEmbeddingSpace {
212        /// Alias the space is reachable under.
213        alias: String,
214        /// Space definition to ensure.
215        info: EmbeddingSpaceInfo,
216        /// Whether this creates a new space.
217        new: bool,
218    },
219    /// Insert or update an embedding binding.
220    UpsertEmbeddingBinding {
221        /// Binding to persist.
222        binding: EmbeddingBinding,
223        /// Vector payload to store, if any.
224        vector: Option<Vec<f32>>,
225        /// When set, statuses are recomputed against this expected live hash.
226        expect_live_hash: bool,
227    },
228    /// v3: resolve and remove canonical binary vector keys at apply time.
229    /// The alias must still identify this space; counters and the immutable
230    /// space definition remain so a recreated alias receives a fresh space ID.
231    ClearEmbeddingSpace {
232        /// Alias of the space to clear.
233        alias: String,
234        /// Dense id of the space.
235        space_id: u64,
236    },
237    /// Replicated meta bookkeeping (projection watermarks and similar);
238    /// value semantics are opaque to the state machine.
239    SetMetaValue {
240        /// Logical meta key.
241        key: String,
242        /// New integer value.
243        value: u64,
244    },
245    /// Replicated opaque blob (policies, configuration); replayed verbatim
246    /// by the deterministic state machine like any other mutation.
247    SetMetaBlob {
248        /// Logical meta key.
249        key: String,
250        /// New blob value.
251        value: Vec<u8>,
252    },
253    /// Deterministic deletion of a replicated meta blob (index registry rows).
254    DeleteMetaBlob {
255        /// Logical meta key.
256        key: String,
257    },
258    /// Delete one tenant-scoped raw key during an explicit maintenance repair.
259    /// The maintenance caller must validate the key belongs to its tenant.
260    DeleteRawKey {
261        /// Canonical tenant-scoped key to remove.
262        key: Vec<u8>,
263    },
264    /// Put one tenant-scoped raw key during an explicit maintenance fixture
265    /// or rebuild operation.
266    PutRawKey {
267        /// Canonical tenant-scoped key to write.
268        key: Vec<u8>,
269        /// Value bytes to store.
270        value: Vec<u8>,
271    },
272    /// One bounded, resumable purge step: delete up to `max_keys` of the
273    /// smallest remaining purgeable keys (tenant keyspace excluding the
274    /// security subrange, then vector/ANN metadata), without retaining MVCC
275    /// history for erased keys. The driver loops commits until nothing
276    /// remains; expansion is a pure function of (state, batch), so replay
277    /// reproduces the same sequence.
278    PurgeTenantChunk {
279        /// Maximum number of keys this step may delete.
280        max_keys: u64,
281    },
282    /// Register a tenant scope in the directory: assign the next
283    /// ordinal unless the scope already has an active one. Expansion reads
284    /// and bumps the directory sequence, so replay reproduces the ordinal.
285    RegisterTenant {
286        /// SHA-256 digest of the scope value (the scope string is never
287        /// stored).
288        scope: crate::ids::TenantScope,
289    },
290    /// Retire the batch tenant's ordinal after its keyspace is purged: both
291    /// directory rows move to `Purged`, and the ordinal is never reassigned.
292    RetireTenant,
293    /// Reclaim embedding vector slots whose superseding binding version is at
294    /// or below `through_ci` (the GC watermark). A re-binding allocates a fresh
295    /// slot and records the old one in the reclamation index; the old slot's
296    /// bytes stay readable by pinned views until the history floor passes it.
297    /// Expansion deletes a bounded batch of the oldest reclaimable slots plus
298    /// their reclamation rows; the driver loops commits until none remain.
299    ReclaimEmbeddingSlots {
300        /// Highest superseding commit index safe to reclaim (inclusive).
301        through_ci: u64,
302    },
303    /// Declare an index: allocate the next id and store the
304    /// definition as `Building { cursor: 0 }`. From this commit on, the
305    /// deriver maintains entries of covered records.
306    DefineIndex {
307        /// Tenant-unique name.
308        name: String,
309        /// Record label.
310        label: String,
311        /// Property name (normalized at expansion).
312        property: String,
313        /// Index kind.
314        kind: crate::index::IndexKind,
315    },
316    /// Advance a building index's cursor to `through` (the deriver backfills
317    /// the records it newly covers from their current values), or mark it
318    /// `Ready` when `through` is `None`.
319    IndexBackfill {
320        /// Index id.
321        id: u32,
322        /// New cursor, or `None` for `Ready`.
323        through: Option<u64>,
324    },
325    /// Start dropping an index by name: `Dropping { cursor: 0 }`.
326    DropIndex {
327        /// Index name.
328        name: String,
329    },
330    /// Advance a dropping index's cursor to `through` (the deriver removes
331    /// the entries it no longer covers), or remove the definition when
332    /// `through` is `None`.
333    IndexDrop {
334        /// Index id.
335        id: u32,
336        /// New cursor, or `None` to remove the definition.
337        through: Option<u64>,
338    },
339    /// An index applier (text or structural) processed `owners` of index
340    /// `index` as of commit `upto`: remove each owner's pending row
341    /// unless a later commit re-marked it.
342    IndexApplied {
343        /// Index id.
344        index: u32,
345        /// Snapshot the applier read.
346        upto: u64,
347        /// Owners whose current text was applied.
348        owners: Vec<u64>,
349    },
350    /// [`Mutation::SetSecurityPolicy`] with a small-count threshold.
351    /// Appended last so earlier logs keep their variant indexes; written only
352    /// when the threshold is set, so a log without one stays readable by
353    /// binaries that predate it.
354    SetSecurityPolicyThreshold {
355        /// Policy payload to publish.
356        policy: crate::security::DataAccessPolicy,
357        /// Enforcement strength for the policy.
358        mode: crate::security::AuthorizationMode,
359        /// Smallest group a catalog read may report.
360        min_count: u64,
361    },
362}