690dd55a86
Co-authored-by: Gerard Guillemas Martos <gerard.guillemas@surrealdb.com>
140 lines
3.1 KiB
Rust
140 lines
3.1 KiB
Rust
use crate::dbs::node::Timestamp;
|
|
use crate::sql;
|
|
use sql::Duration;
|
|
use std::sync::atomic::AtomicU64;
|
|
use std::sync::atomic::Ordering;
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
#[cfg(target_arch = "wasm32")]
|
|
use wasmtimer::std::{SystemTime, UNIX_EPOCH};
|
|
|
|
// Traits cannot have async and we need sized structs for Clone + Send + Sync
|
|
#[allow(dead_code)]
|
|
#[derive(Clone)]
|
|
pub enum SizedClock {
|
|
System(SystemClock),
|
|
#[cfg(test)]
|
|
Fake(FakeClock),
|
|
#[cfg(test)]
|
|
Inc(IncFakeClock),
|
|
}
|
|
|
|
impl SizedClock {
|
|
pub async fn now(&self) -> Timestamp {
|
|
match self {
|
|
SizedClock::System(c) => c.now(),
|
|
#[cfg(test)]
|
|
SizedClock::Fake(c) => c.now().await,
|
|
#[cfg(test)]
|
|
SizedClock::Inc(c) => c.now().await,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// FakeClock is a clock that is fully controlled externally.
|
|
/// Use this clock for when you are testing timestamps.
|
|
|
|
pub struct FakeClock {
|
|
// Locks necessary for Send
|
|
now: AtomicU64,
|
|
}
|
|
|
|
impl Clone for FakeClock {
|
|
fn clone(&self) -> Self {
|
|
FakeClock {
|
|
now: AtomicU64::new(self.now.load(Ordering::SeqCst)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl FakeClock {
|
|
pub fn new(now: Timestamp) -> Self {
|
|
FakeClock {
|
|
now: AtomicU64::new(now.value),
|
|
}
|
|
}
|
|
|
|
pub async fn now(&self) -> Timestamp {
|
|
Timestamp {
|
|
value: self.now.load(Ordering::SeqCst),
|
|
}
|
|
}
|
|
|
|
pub async fn set(&self, timestamp: Timestamp) {
|
|
self.now.store(timestamp.value, Ordering::SeqCst);
|
|
}
|
|
}
|
|
|
|
/// IncFakeClock increments a local clock every time the clock is accessed, similar to a real clock.
|
|
/// This is useful when you need unique and partially deterministic timestamps for tests.
|
|
/// Partially deterministic, because you do not have direct control over how many times a clock
|
|
/// is accessed, and due to the nature of async - you neither have order guarantee.
|
|
pub struct IncFakeClock {
|
|
now: AtomicU64,
|
|
increment: Duration,
|
|
}
|
|
|
|
impl Clone for IncFakeClock {
|
|
fn clone(&self) -> Self {
|
|
IncFakeClock {
|
|
now: AtomicU64::new(self.now.load(Ordering::SeqCst)),
|
|
increment: self.increment,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl IncFakeClock {
|
|
pub fn new(now: Timestamp, increment: Duration) -> Self {
|
|
IncFakeClock {
|
|
now: AtomicU64::new(now.value),
|
|
increment,
|
|
}
|
|
}
|
|
|
|
pub async fn now(&self) -> Timestamp {
|
|
self.now.fetch_add(self.increment.as_millis() as u64, Ordering::SeqCst);
|
|
Timestamp {
|
|
value: self.now.load(Ordering::SeqCst),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// SystemClock is a clock that uses the system time.
|
|
/// Use this when there are no other alternatives.
|
|
#[derive(Clone, Copy)]
|
|
pub struct SystemClock;
|
|
|
|
impl SystemClock {
|
|
pub fn new() -> Self {
|
|
SystemClock
|
|
}
|
|
pub fn now(&self) -> Timestamp {
|
|
// Use a timestamp oracle if available
|
|
let now: u128 = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
|
Ok(duration) => duration.as_millis(),
|
|
Err(error) => panic!("Clock may have gone backwards: {:?}", error.duration()),
|
|
};
|
|
Timestamp {
|
|
value: now as u64,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for SystemClock {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::kvs::clock::SystemClock;
|
|
|
|
#[test]
|
|
fn get_clock_now() {
|
|
let clock = SystemClock::new();
|
|
let _ = clock.now();
|
|
}
|
|
}
|