Skip to main content

reifydb_transaction/
lib.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2026 ReifyDB
3
4//! Transactional layer over the storage tier: opens a read or read-write transaction, accumulates deltas, validates
5//! conflicts, and commits atomically. The crate offers two flavours - a multi-version path for OLTP traffic that needs
6//! snapshot isolation, and a single-version path for workloads where versioning would only add overhead - and exposes
7//! both behind a uniform `Transaction` handle that the engine threads through every request.
8//!
9//! Every change recorded inside a transaction is captured as a delta, surfaced through the change accumulator, and
10//! published downstream so CDC consumers, replication, and subscriptions observe the same write set the engine just
11//! committed. Interceptors hook the commit boundary and let policy, audit, and consistency checks run with full
12//! visibility into what is about to be written.
13//!
14//! Invariant: a `TransactionId` is unique system-wide and monotonic via Uuid7; downstream consumers (CDC, replication,
15//! subscriptions) order events by transaction id, so reusing or back-dating an id silently breaks consumer
16//! consistency.
17
18#![cfg_attr(not(debug_assertions), deny(clippy::disallowed_methods))]
19#![cfg_attr(debug_assertions, warn(clippy::disallowed_methods))]
20#![cfg_attr(not(debug_assertions), deny(warnings))]
21#![allow(clippy::tabs_in_doc_comments)]
22
23use std::{
24	fmt,
25	fmt::{Display, Formatter},
26	ops::Deref,
27};
28
29use reifydb_core::{
30	interface::version::{ComponentType, HasVersion, SystemVersion},
31	return_internal_error,
32};
33use reifydb_runtime::context::{clock::Clock, rng::Rng};
34use reifydb_value::{error::Error, value::uuid::Uuid7};
35use uuid::{Builder, Uuid};
36
37pub mod change;
38pub mod change_accumulator;
39pub mod delta;
40pub mod error;
41pub mod interceptor;
42pub mod multi;
43pub mod single;
44pub mod transaction;
45
46#[repr(transparent)]
47#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
48pub struct TransactionId(pub(crate) Uuid7);
49
50impl Deref for TransactionId {
51	type Target = Uuid7;
52
53	fn deref(&self) -> &Self::Target {
54		&self.0
55	}
56}
57
58impl TransactionId {
59	pub fn generate(clock: &Clock, rng: &Rng) -> Self {
60		let millis = clock.now_millis();
61		let random_bytes = rng.infra_bytes_10();
62		Self(Uuid7(Builder::from_unix_timestamp_millis(millis, &random_bytes).into_uuid()))
63	}
64}
65
66impl TryFrom<&[u8]> for TransactionId {
67	type Error = Error;
68
69	fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
70		if bytes.len() != 16 {
71			return_internal_error!("Invalid transaction ID length: expected 16 bytes, got {}", bytes.len());
72		}
73		let mut uuid_bytes = [0u8; 16];
74		uuid_bytes.copy_from_slice(bytes);
75		let uuid = Uuid::from_bytes(uuid_bytes);
76		Ok(Self(Uuid7::from(uuid)))
77	}
78}
79
80impl Display for TransactionId {
81	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82		write!(f, "{}", self.0)
83	}
84}
85
86pub struct TransactionVersion;
87
88impl HasVersion for TransactionVersion {
89	fn version(&self) -> SystemVersion {
90		SystemVersion {
91			name: env!("CARGO_PKG_NAME")
92				.strip_prefix("reifydb-")
93				.unwrap_or(env!("CARGO_PKG_NAME"))
94				.to_string(),
95			version: env!("CARGO_PKG_VERSION").to_string(),
96			description: "Transaction management and concurrency control module".to_string(),
97			r#type: ComponentType::Module,
98		}
99	}
100}