surrealdb/dbs/
node.rs

1use crate::err::Error;
2use crate::err::Error::TimestampOverflow;
3use crate::sql::Duration;
4use derive::{Key, Store};
5use revision::revisioned;
6use serde::{Deserialize, Serialize};
7use std::ops::{Add, Sub};
8
9// NOTE: This is not a statement, but as per layering, keeping it here till we
10// have a better structure.
11#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Store)]
12#[revisioned(revision = 1)]
13pub struct ClusterMembership {
14	pub name: String,
15	// TiKV = TiKV TSO Timestamp as u64
16	// not TiKV = local nanos as u64
17	pub heartbeat: Timestamp,
18}
19// This struct is meant to represent a timestamp that can be used to partially order
20// events in a cluster. It should be derived from a timestamp oracle, such as the
21// one available in TiKV via the client `TimestampExt` implementation.
22#[derive(
23	Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Store, Default,
24)]
25#[revisioned(revision = 1)]
26pub struct Timestamp {
27	pub value: u64,
28}
29
30impl From<u64> for Timestamp {
31	fn from(ts: u64) -> Self {
32		Timestamp {
33			value: ts,
34		}
35	}
36}
37
38// This struct is to be used only when storing keys as the macro currently
39// conflicts when you have Store and Key derive macros.
40#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Key)]
41#[revisioned(revision = 1)]
42pub struct KeyTimestamp {
43	pub value: u64,
44}
45
46impl From<&Timestamp> for KeyTimestamp {
47	fn from(ts: &Timestamp) -> Self {
48		KeyTimestamp {
49			value: ts.value,
50		}
51	}
52}
53
54impl Add<&Duration> for &Timestamp {
55	type Output = Timestamp;
56	fn add(self, rhs: &Duration) -> Timestamp {
57		Timestamp {
58			value: self.value + rhs.as_millis() as u64,
59		}
60	}
61}
62
63impl Sub<&Duration> for &Timestamp {
64	type Output = Result<Timestamp, Error>;
65	fn sub(self, rhs: &Duration) -> Self::Output {
66		let millis = rhs.as_millis() as u64;
67		if self.value <= millis {
68			// Removing the duration from this timestamp will cause it to overflow
69			return Err(TimestampOverflow(format!(
70				"Failed to subtract {} from {}",
71				&millis, &self.value
72			)));
73		}
74		Ok(Timestamp {
75			value: self.value - millis,
76		})
77	}
78}
79
80#[cfg(test)]
81mod test {
82	use crate::dbs::node::Timestamp;
83	use crate::sql::Duration;
84	use chrono::prelude::Utc;
85	use chrono::TimeZone;
86
87	#[test]
88	fn timestamps_can_be_added_duration() {
89		let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
90		let ts = Timestamp {
91			value: t.timestamp_millis() as u64,
92		};
93
94		let hour = Duration(core::time::Duration::from_secs(60 * 60));
95		let ts = &ts + &hour;
96		let ts = &ts + &hour;
97		let ts = &ts + &hour;
98
99		let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
100		let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 15, 30, 0).unwrap();
101		assert_eq!(end_time, expected_end_time);
102	}
103
104	#[test]
105	fn timestamps_can_be_subtracted_duration() {
106		let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
107		let ts = Timestamp {
108			value: t.timestamp_millis() as u64,
109		};
110
111		let hour = Duration(core::time::Duration::from_secs(60 * 60));
112		let ts = (&ts - &hour).unwrap();
113		let ts = (&ts - &hour).unwrap();
114		let ts = (&ts - &hour).unwrap();
115
116		let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
117		let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 9, 30, 0).unwrap();
118		assert_eq!(end_time, expected_end_time);
119	}
120}