use std::sync::atomic::{AtomicI64, Ordering};
use radixdb_core::time_compat::{system_time_now, UNIX_EPOCH};
static LAST_TIMESTAMP: AtomicI64 = AtomicI64::new(0);
const MAX_DURABLE_TIMESTAMP: i64 = i64::MAX - (1i64 << 60);
#[inline]
fn persisted_timestamp_is_admissible(timestamp: i64) -> bool {
timestamp <= MAX_DURABLE_TIMESTAMP
}
pub fn observe_persisted_timestamp(timestamp: i64) -> bool {
if timestamp <= 0 {
return true;
}
if !persisted_timestamp_is_admissible(timestamp) {
return false;
}
LAST_TIMESTAMP.fetch_max(timestamp, Ordering::AcqRel);
true
}
pub fn timestamp_high_water() -> i64 {
LAST_TIMESTAMP.load(Ordering::Acquire)
}
pub fn get_fast_timestamp() -> i64 {
let now_nano = system_time_now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as i64)
.unwrap_or(1);
loop {
let last_ts = LAST_TIMESTAMP.load(Ordering::Acquire);
let next_ts = if now_nano > last_ts {
now_nano
} else {
last_ts
.checked_add(1)
.expect("MVCC timestamp domain exhausted")
};
if LAST_TIMESTAMP
.compare_exchange(last_ts, next_ts, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
return next_ts;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustc_hash::FxHashSet;
use std::thread;
#[test]
fn r2_l05_a_persisted_timestamp_seeds_process_high_water() {
let durable_timestamp = get_fast_timestamp() + 1_000_000_000;
assert!(observe_persisted_timestamp(durable_timestamp));
assert!(
get_fast_timestamp() > durable_timestamp,
"new process timestamps must continue the durable high-water"
);
}
#[test]
fn v2_r2_durable_timestamp_reserves_allocator_headroom() {
assert!(persisted_timestamp_is_admissible(MAX_DURABLE_TIMESTAMP));
assert!(!persisted_timestamp_is_admissible(
MAX_DURABLE_TIMESTAMP + 1
));
assert!(!persisted_timestamp_is_admissible(i64::MAX - 1));
assert!(!persisted_timestamp_is_admissible(i64::MAX));
}
#[test]
fn test_timestamp_monotonic() {
let mut prev = get_fast_timestamp();
for _ in 0..1000 {
let ts = get_fast_timestamp();
assert!(
ts > prev,
"Timestamp not strictly increasing: {} <= {}",
ts,
prev
);
prev = ts;
}
}
#[test]
fn test_timestamp_unique() {
let mut timestamps = FxHashSet::default();
for _ in 0..10000 {
let ts = get_fast_timestamp();
assert!(
timestamps.insert(ts),
"Duplicate timestamp detected: {}",
ts
);
}
}
#[test]
fn test_timestamp_concurrent() {
let handles: Vec<_> = (0..4)
.map(|_| {
thread::spawn(|| {
let mut timestamps = Vec::with_capacity(1000);
for _ in 0..1000 {
timestamps.push(get_fast_timestamp());
}
timestamps
})
})
.collect();
let mut all_timestamps: FxHashSet<i64> = FxHashSet::default();
for handle in handles {
let timestamps = handle.join().unwrap();
for ts in timestamps {
all_timestamps.insert(ts);
}
}
assert_eq!(
all_timestamps.len(),
4000,
"Expected all 4000 timestamps to be unique, got {}",
all_timestamps.len()
);
}
#[test]
fn test_timestamp_positive() {
for _ in 0..100 {
let ts = get_fast_timestamp();
assert!(ts > 0, "Timestamp should be positive: {}", ts);
}
}
}