surrealdb/dbs/
node.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::err::Error;
use crate::err::Error::TimestampOverflow;
use crate::sql::Duration;
use derive::{Key, Store};
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::ops::{Add, Sub};

// NOTE: This is not a statement, but as per layering, keeping it here till we
// have a better structure.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Store)]
#[revisioned(revision = 1)]
pub struct ClusterMembership {
	pub name: String,
	// TiKV = TiKV TSO Timestamp as u64
	// not TiKV = local nanos as u64
	pub heartbeat: Timestamp,
}
// This struct is meant to represent a timestamp that can be used to partially order
// events in a cluster. It should be derived from a timestamp oracle, such as the
// one available in TiKV via the client `TimestampExt` implementation.
#[derive(
	Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Store, Default,
)]
#[revisioned(revision = 1)]
pub struct Timestamp {
	pub value: u64,
}

impl From<u64> for Timestamp {
	fn from(ts: u64) -> Self {
		Timestamp {
			value: ts,
		}
	}
}

// This struct is to be used only when storing keys as the macro currently
// conflicts when you have Store and Key derive macros.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Hash, Key)]
#[revisioned(revision = 1)]
pub struct KeyTimestamp {
	pub value: u64,
}

impl From<&Timestamp> for KeyTimestamp {
	fn from(ts: &Timestamp) -> Self {
		KeyTimestamp {
			value: ts.value,
		}
	}
}

impl Add<&Duration> for &Timestamp {
	type Output = Timestamp;
	fn add(self, rhs: &Duration) -> Timestamp {
		Timestamp {
			value: self.value + rhs.as_millis() as u64,
		}
	}
}

impl Sub<&Duration> for &Timestamp {
	type Output = Result<Timestamp, Error>;
	fn sub(self, rhs: &Duration) -> Self::Output {
		let millis = rhs.as_millis() as u64;
		if self.value <= millis {
			// Removing the duration from this timestamp will cause it to overflow
			return Err(TimestampOverflow(format!(
				"Failed to subtract {} from {}",
				&millis, &self.value
			)));
		}
		Ok(Timestamp {
			value: self.value - millis,
		})
	}
}

#[cfg(test)]
mod test {
	use crate::dbs::node::Timestamp;
	use crate::sql::Duration;
	use chrono::prelude::Utc;
	use chrono::TimeZone;

	#[test]
	fn timestamps_can_be_added_duration() {
		let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
		let ts = Timestamp {
			value: t.timestamp_millis() as u64,
		};

		let hour = Duration(core::time::Duration::from_secs(60 * 60));
		let ts = &ts + &hour;
		let ts = &ts + &hour;
		let ts = &ts + &hour;

		let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
		let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 15, 30, 0).unwrap();
		assert_eq!(end_time, expected_end_time);
	}

	#[test]
	fn timestamps_can_be_subtracted_duration() {
		let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
		let ts = Timestamp {
			value: t.timestamp_millis() as u64,
		};

		let hour = Duration(core::time::Duration::from_secs(60 * 60));
		let ts = (&ts - &hour).unwrap();
		let ts = (&ts - &hour).unwrap();
		let ts = (&ts - &hour).unwrap();

		let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
		let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 9, 30, 0).unwrap();
		assert_eq!(end_time, expected_end_time);
	}
}