risingwave_common/util/
epoch.rs1use std::sync::LazyLock;
16use std::time::{Duration, SystemTime};
17
18use easy_ext::ext;
19use parse_display::Display;
20
21use crate::types::{ScalarImpl, Timestamptz};
22
23static UNIX_RISINGWAVE_DATE_SEC: u64 = 1_617_235_200;
24
25pub static UNIX_RISINGWAVE_DATE_EPOCH: LazyLock<SystemTime> =
28 LazyLock::new(|| SystemTime::UNIX_EPOCH + Duration::from_secs(UNIX_RISINGWAVE_DATE_SEC));
29
30#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct Epoch(pub u64);
32
33pub const INVALID_EPOCH: u64 = 0;
35
36const EPOCH_PHYSICAL_SHIFT_BITS: u8 = 16;
37
38const MIN_EPOCH_PHYSICAL_TIME_DELTA: u64 = 2;
44
45impl Epoch {
46 pub fn now() -> Self {
47 Self(Self::physical_now() << EPOCH_PHYSICAL_SHIFT_BITS)
48 }
49
50 #[must_use]
51 pub fn next(self) -> Self {
52 let mut physical_now = Epoch::physical_now();
53 let prev_physical_time = self.physical_time();
54 let min_physical_time = prev_physical_time
55 .checked_add(MIN_EPOCH_PHYSICAL_TIME_DELTA)
56 .expect("next epoch physical time should not overflow");
57
58 loop {
59 if physical_now >= min_physical_time {
60 break;
61 }
62 physical_now = Epoch::physical_now();
63
64 #[cfg(madsim)]
65 tokio::time::advance(std::time::Duration::from_micros(10));
66 #[cfg(not(madsim))]
67 std::hint::spin_loop();
68 }
69 let next_epoch = Self::from_physical_time(physical_now);
72
73 assert!(next_epoch.0 > self.0);
74 next_epoch
75 }
76
77 pub fn physical_time(&self) -> u64 {
79 self.0 >> EPOCH_PHYSICAL_SHIFT_BITS
80 }
81
82 pub fn from_physical_time(time: u64) -> Self {
83 Epoch(time << EPOCH_PHYSICAL_SHIFT_BITS)
84 }
85
86 pub fn from_unix_millis(mi: u64) -> Self {
87 Epoch((mi - UNIX_RISINGWAVE_DATE_SEC * 1000) << EPOCH_PHYSICAL_SHIFT_BITS)
88 }
89
90 pub fn from_unix_millis_or_earliest(mi: u64) -> Self {
91 Epoch((mi.saturating_sub(UNIX_RISINGWAVE_DATE_SEC * 1000)) << EPOCH_PHYSICAL_SHIFT_BITS)
92 }
93
94 pub fn physical_now() -> u64 {
95 UNIX_RISINGWAVE_DATE_EPOCH
96 .elapsed()
97 .expect("system clock set earlier than risingwave date!")
98 .as_millis() as u64
99 }
100
101 pub fn as_unix_millis(&self) -> u64 {
102 UNIX_RISINGWAVE_DATE_SEC * 1000 + self.physical_time()
103 }
104
105 pub fn as_unix_secs(&self) -> u64 {
106 UNIX_RISINGWAVE_DATE_SEC + self.physical_time() / 1000
107 }
108
109 pub fn as_timestamptz(&self) -> Timestamptz {
111 Timestamptz::from_millis(self.as_unix_millis() as i64).expect("epoch is out of range")
112 }
113
114 pub fn as_scalar(&self) -> ScalarImpl {
116 self.as_timestamptz().into()
117 }
118
119 pub fn as_system_time(&self) -> SystemTime {
121 *UNIX_RISINGWAVE_DATE_EPOCH + Duration::from_millis(self.physical_time())
122 }
123
124 pub fn subtract_ms(&self, relative_time_ms: u64) -> Self {
127 let physical_time = self.physical_time();
128
129 if physical_time < relative_time_ms {
130 Epoch(INVALID_EPOCH)
131 } else {
132 Epoch((physical_time - relative_time_ms) << EPOCH_PHYSICAL_SHIFT_BITS)
133 }
134 }
135}
136
137pub const EPOCH_AVAILABLE_BITS: u64 = 16;
138pub const MAX_SPILL_TIMES: u16 = ((1 << EPOCH_AVAILABLE_BITS) - 1) as u16;
139pub const EPOCH_SPILL_TIME_MASK: u64 = (1 << EPOCH_AVAILABLE_BITS) - 1;
141pub const MAX_EPOCH: u64 = !((1 << EPOCH_AVAILABLE_BITS) - 1);
143
144const EPOCH_INC_MIN_STEP_FOR_TEST: u64 = test_epoch(1);
148
149pub fn is_max_epoch(epoch: u64) -> bool {
150 epoch >= MAX_EPOCH
154}
155pub fn is_compatibility_max_epoch(epoch: u64) -> bool {
156 epoch == MAX_EPOCH
158}
159impl From<u64> for Epoch {
160 fn from(epoch: u64) -> Self {
161 Self(epoch)
162 }
163}
164
165#[derive(Debug, Clone, Copy, PartialEq)]
166pub struct EpochPair {
167 pub curr: u64,
168 pub prev: u64,
169}
170
171impl EpochPair {
172 pub fn new(curr: u64, prev: u64) -> Self {
173 assert!(curr > prev);
174 Self { curr, prev }
175 }
176
177 pub fn inc_for_test(&mut self) {
178 self.prev = self.curr;
179 self.curr += EPOCH_INC_MIN_STEP_FOR_TEST;
180 }
181
182 pub fn new_test_epoch(curr: u64) -> Self {
183 if !is_max_epoch(curr) {
184 assert!(curr >= EPOCH_INC_MIN_STEP_FOR_TEST);
185 assert!((curr & EPOCH_SPILL_TIME_MASK) == 0);
186 }
187 Self::new(curr, curr - EPOCH_INC_MIN_STEP_FOR_TEST)
188 }
189}
190
191pub const fn test_epoch(value_millis: u64) -> u64 {
194 value_millis << EPOCH_AVAILABLE_BITS
195}
196
197#[ext(EpochExt)]
200pub impl u64 {
201 fn inc_epoch(&mut self) {
202 *self += EPOCH_INC_MIN_STEP_FOR_TEST;
203 }
204
205 fn dec_epoch(&mut self) {
206 *self -= EPOCH_INC_MIN_STEP_FOR_TEST;
207 }
208
209 fn next_epoch(self) -> u64 {
210 self + EPOCH_INC_MIN_STEP_FOR_TEST
211 }
212
213 fn prev_epoch(self) -> u64 {
214 self - EPOCH_INC_MIN_STEP_FOR_TEST
215 }
216}
217
218pub mod task_local {
220 use futures::Future;
221 use tokio::task_local;
222
223 use super::{Epoch, EpochPair};
224
225 task_local! {
226 static TASK_LOCAL_EPOCH_PAIR: EpochPair;
227 }
228
229 pub fn curr_epoch() -> Option<Epoch> {
234 TASK_LOCAL_EPOCH_PAIR.try_with(|e| Epoch(e.curr)).ok()
235 }
236
237 pub fn prev_epoch() -> Option<Epoch> {
242 TASK_LOCAL_EPOCH_PAIR.try_with(|e| Epoch(e.prev)).ok()
243 }
244
245 pub fn epoch() -> Option<EpochPair> {
250 TASK_LOCAL_EPOCH_PAIR.try_with(|e| *e).ok()
251 }
252
253 pub async fn scope<F>(epoch: EpochPair, f: F) -> F::Output
255 where
256 F: Future,
257 {
258 TASK_LOCAL_EPOCH_PAIR.scope(epoch, f).await
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use chrono::{Local, TimeZone, Utc};
265
266 use super::*;
267
268 #[test]
269 fn test_risingwave_system_time() {
270 let utc = Utc.with_ymd_and_hms(2021, 4, 1, 0, 0, 0).unwrap();
271 let risingwave_dt = Local.from_utc_datetime(&utc.naive_utc());
272 let risingwave_st = SystemTime::from(risingwave_dt);
273 assert_eq!(risingwave_st, *UNIX_RISINGWAVE_DATE_EPOCH);
274 }
275
276 #[tokio::test]
277 async fn test_epoch_generate() {
278 let mut prev_epoch = Epoch::now();
279 for _ in 0..1000 {
280 let epoch = prev_epoch.next();
281 assert!(epoch > prev_epoch);
282 assert!(
283 epoch.physical_time() >= prev_epoch.physical_time() + MIN_EPOCH_PHYSICAL_TIME_DELTA
284 );
285 prev_epoch = epoch;
286 }
287 }
288
289 #[test]
290 fn test_subtract_ms() {
291 {
292 let epoch = Epoch(10);
293 assert_eq!(0, epoch.physical_time());
294 assert_eq!(0, epoch.subtract_ms(20).0);
295 }
296
297 {
298 let epoch = Epoch::now();
299 let physical_time = epoch.physical_time();
300 let interval = 10;
301
302 assert_ne!(0, physical_time);
303 assert_eq!(
304 physical_time - interval,
305 epoch.subtract_ms(interval).physical_time()
306 );
307 }
308 }
309}