limiteron 0.3.0-rc.7

Unified flow control framework for Rust
Documentation
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
// Copyright (c) 2026 Kirky.X🌠
// SPDX-License-Identifier: MIT
//! 可控时钟抽象模块
//!
//! 提供时钟 trait 和实现,支持时间注入用于测试。

#[cfg(any(test, feature = "test-clock"))]
use std::sync::Arc;
#[cfg(any(test, feature = "test-clock"))]
use std::time::Duration;
use std::time::{Instant, SystemTime, UNIX_EPOCH};

use chrono::{DateTime, Utc};

/// 时钟 trait
///
/// 提供时间获取接口,支持注入实现用于测试。
///
/// # 示例
///
/// ```rust
/// use limiteron::{Clock, SystemClock};
///
/// let clock = SystemClock;
/// let now = clock.now();
/// let timestamp = clock.unix_timestamp();
/// ```
pub trait Clock: Send + Sync {
    /// 获取当前 `Instant` 时间
    fn now(&self) -> Instant;

    /// 获取当前 UNIX 时间戳(秒)
    fn unix_timestamp(&self) -> u64;

    /// 获取当前 UNIX 时间戳(纳秒)
    fn unix_timestamp_nanos(&self) -> u64;

    /// 获取当前 UTC 墙上时间(配额窗口/告警去重等 `DateTime` 语义的时间读取口)
    ///
    /// 默认实现从 [`Clock::unix_timestamp_nanos`] 派生,第三方实现者无需改动
    /// 即可获得合法语义;`SystemClock` 覆写为直读以避免精度中转。
    fn now_datetime(&self) -> DateTime<Utc> {
        DateTime::from_timestamp_nanos(self.unix_timestamp_nanos() as i64)
    }
}

/// 系统时钟实现
///
/// 使用真实的系统时间,生产环境默认使用此实现。
#[derive(Debug, Clone, Copy)]
pub struct SystemClock;

impl Clock for SystemClock {
    fn now(&self) -> Instant {
        Instant::now()
    }

    fn unix_timestamp(&self) -> u64 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0)
    }

    fn unix_timestamp_nanos(&self) -> u64 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_nanos() as u64)
            .unwrap_or(0)
    }

    fn now_datetime(&self) -> DateTime<Utc> {
        Utc::now()
    }
}

/// 模拟时钟实现
///
/// 用于测试,可以手动控制时间。
///
/// # 示例
///
/// ```rust
/// use limiteron::{Clock, MockClock};
/// use std::time::{Duration, Instant};
///
/// let clock = MockClock::new();
/// let start = clock.now();
///
/// // 前进 10 秒
/// clock.advance(Duration::from_secs(10));
///
/// assert_eq!(clock.now().duration_since(start), Duration::from_secs(10));
/// ```
#[cfg(any(test, feature = "test-clock"))]
pub struct MockClock {
    current_time: parking_lot::RwLock<Instant>,
    /// UNIX 时间(纳秒,u128 防溢出)。单一时钟源:
    /// `unix_timestamp` 取整秒、`unix_timestamp_nanos` 携带亚秒,
    /// 二者与 Instant 永远同步推进。
    ///
    /// 历史教训:曾用「秒级 RwLock + advance 只加 `as_secs()`」,
    /// 亚秒前进被丢弃而 Instant 却完整前移——两个内部时钟在亚秒
    /// 操作后互相发散,且 `unix_timestamp_nanos` 恒为秒×1e9,
    /// 无法测试令牌桶 1ms 门限等亚秒行为。
    unix_nanos: parking_lot::RwLock<u128>,
}

#[cfg(any(test, feature = "test-clock"))]
impl Clone for MockClock {
    fn clone(&self) -> Self {
        Self {
            current_time: parking_lot::RwLock::new(*self.current_time.read()),
            unix_nanos: parking_lot::RwLock::new(*self.unix_nanos.read()),
        }
    }
}

#[cfg(any(test, feature = "test-clock"))]
impl MockClock {
    /// 创建新的模拟时钟,初始时间为当前系统时间
    pub fn new() -> Self {
        Self {
            current_time: parking_lot::RwLock::new(Instant::now()),
            unix_nanos: parking_lot::RwLock::new(
                SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .map(|d| d.as_nanos())
                    .unwrap_or(0),
            ),
        }
    }

    /// 创建新的模拟时钟,指定初始时间
    pub fn with_instant(instant: Instant, unix_ts: u64) -> Self {
        Self {
            current_time: parking_lot::RwLock::new(instant),
            unix_nanos: parking_lot::RwLock::new((unix_ts as u128) * 1_000_000_000),
        }
    }

    /// 将时间前进指定时长(亚秒精度同步推进 Instant 与 UNIX 时钟)
    pub fn advance(&self, duration: Duration) {
        {
            let mut time = self.current_time.write();
            *time = time.checked_add(duration).unwrap_or(*time);
        }
        let mut nanos = self.unix_nanos.write();
        *nanos = nanos.saturating_add(duration.as_nanos());
    }

    /// 设置当前时间
    pub fn set_time(&self, instant: Instant, unix_ts: u64) {
        {
            let mut time = self.current_time.write();
            *time = instant;
        }
        let mut nanos = self.unix_nanos.write();
        *nanos = (unix_ts as u128) * 1_000_000_000;
    }

    /// 获取包装为 Arc 的时钟实例
    pub fn as_arc(self) -> Arc<dyn Clock> {
        Arc::new(self)
    }
}

#[cfg(any(test, feature = "test-clock"))]
impl Default for MockClock {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(any(test, feature = "test-clock"))]
impl Clock for MockClock {
    fn now(&self) -> Instant {
        *self.current_time.read()
    }

    fn unix_timestamp(&self) -> u64 {
        (*self.unix_nanos.read() / 1_000_000_000) as u64
    }

    fn unix_timestamp_nanos(&self) -> u64 {
        // 与 SystemClock 同口径:u64 截断(饱和),纳秒精度
        u64::try_from(*self.unix_nanos.read()).unwrap_or(u64::MAX)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// 创建系统时钟的 Arc 包装
    fn system_clock() -> Arc<dyn Clock> {
        Arc::new(SystemClock)
    }

    #[test]
    fn test_system_clock_now() {
        let clock = SystemClock;
        let now = clock.now();

        // Instant::now() 应该返回有效时间
        let later = clock.now();
        assert!(later >= now);
    }

    #[test]
    fn test_system_clock_unix_timestamp() {
        let clock = SystemClock;
        let ts = clock.unix_timestamp();

        // 应该是合理的时间戳(2024-2030)
        assert!(ts > 1_700_000_000);
        assert!(ts < 2_000_000_000);
    }

    #[test]
    fn test_system_clock_unix_timestamp_nanos() {
        let clock = SystemClock;
        let ts_nanos = clock.unix_timestamp_nanos();
        let ts_secs = clock.unix_timestamp();

        // 纳秒时间戳应该是秒时间戳的 10^9 倍
        let expected_nanos = ts_secs * 1_000_000_000;
        // 允许 1 秒误差(因为两次调用有时间差)
        assert!(ts_nanos >= expected_nanos);
        assert!(ts_nanos < expected_nanos + 2_000_000_000);
    }

    #[test]
    fn test_mock_clock_advance() {
        let clock = MockClock::new();
        let start = clock.now();

        clock.advance(Duration::from_secs(10));
        let elapsed = clock.now().duration_since(start);

        assert_eq!(elapsed, Duration::from_secs(10));
    }

    #[test]
    fn test_mock_clock_set_time() {
        let clock = MockClock::new();
        let custom_instant = Instant::now() + Duration::from_secs(100);
        let custom_unix_ts = 1_700_000_000;

        clock.set_time(custom_instant, custom_unix_ts);

        assert_eq!(clock.now(), custom_instant);
        assert_eq!(clock.unix_timestamp(), custom_unix_ts);
    }

    #[test]
    fn test_mock_clock_unix_timestamp_nanos() {
        // 整秒基座 + 整秒前进:纳秒与秒级时间戳必须精确一致
        //(亚秒语义由 test_mock_clock_advance_subsecond_consistency 覆盖)
        let clock = MockClock::with_instant(Instant::now(), 1_700_000_000);
        clock.advance(Duration::from_secs(5));

        let ts_secs = clock.unix_timestamp();
        let ts_nanos = clock.unix_timestamp_nanos();

        assert_eq!(ts_secs, 1_700_000_005);
        assert_eq!(ts_nanos, 1_700_000_005_000_000_000);
    }

    #[test]
    fn test_mock_clock_thread_safety() {
        let clock = Arc::new(MockClock::new());

        let mut handles = vec![];
        for _ in 0..10 {
            let clock_clone = Arc::clone(&clock);
            handles.push(std::thread::spawn(move || {
                clock_clone.advance(Duration::from_secs(1));
                clock_clone.now()
            }));
        }

        for handle in handles {
            handle.join().unwrap();
        }

        // 所有线程完成后,时间应该至少前进了 10 秒
        // 但由于并发,实际值可能更大
        let elapsed = clock.now().duration_since(Instant::now());
        // 这里无法精确验证,因为初始时间未知,但至少验证不会 panic
        let _ = elapsed;
    }

    #[test]
    fn test_system_clock_arc() {
        let clock = system_clock();
        let ts = clock.unix_timestamp();
        assert!(ts > 1_700_000_000);
    }

    #[test]
    fn test_mock_clock_with_instant() {
        let instant = Instant::now();
        let unix_ts = 1_700_000_000;
        let clock = MockClock::with_instant(instant, unix_ts);

        assert_eq!(clock.now(), instant);
        assert_eq!(clock.unix_timestamp(), unix_ts);
    }

    #[test]
    fn test_mock_clock_default() {
        let clock = MockClock::default();
        let ts = clock.unix_timestamp();
        assert!(ts > 1_700_000_000);
    }

    #[test]
    fn test_mock_clock_clone() {
        let clock = MockClock::new();
        clock.advance(Duration::from_secs(42));
        let cloned = clock.clone();
        assert_eq!(cloned.unix_timestamp(), clock.unix_timestamp());
        assert_eq!(clock.now(), cloned.now());
    }

    #[test]
    fn test_mock_clock_as_arc() {
        let clock = MockClock::new();
        let arc_clock: Arc<dyn Clock> = clock.as_arc();
        let ts = arc_clock.unix_timestamp();
        assert!(ts > 1_700_000_000);
        let now = arc_clock.now();
        assert!(now.elapsed() < Duration::from_secs(5));
    }

    #[test]
    fn test_mock_clock_clone_independent() {
        let clock = MockClock::new();
        clock.advance(Duration::from_secs(10));
        let cloned = clock.clone();
        clock.advance(Duration::from_secs(20));
        assert_eq!(clock.unix_timestamp(), cloned.unix_timestamp() + 20);
    }

    #[test]
    fn test_system_clock_copy_and_clone() {
        let clock1 = SystemClock;
        let clock2 = clock1;
        let ts1 = clock1.unix_timestamp();
        let ts2 = clock2.unix_timestamp();
        assert!(ts1 <= ts2);
    }

    #[test]
    fn test_mock_clock_advance_then_nanos() {
        let clock = MockClock::with_instant(Instant::now(), 100);
        clock.advance(Duration::from_secs(5));
        let nanos = clock.unix_timestamp_nanos();
        assert_eq!(nanos, 105 * 1_000_000_000);
    }

    #[test]
    fn test_mock_clock_advance_subsecond_consistency() {
        // 亚秒一致性回归:advance 的亚秒部分曾只作用于 Instant,
        // UNIX 时间戳丢弃亚秒(as_secs())且 unix_timestamp_nanos 恒为秒×1e9
        // ——两钟发散、亚秒行为不可测。
        let clock = MockClock::with_instant(Instant::now(), 1_000);
        let start = clock.now();

        clock.advance(Duration::from_millis(900));
        assert_eq!(
            clock.unix_timestamp(),
            1_000,
            "a sub-second advance must not carry into seconds"
        );
        assert_eq!(
            clock.unix_timestamp_nanos(),
            1_000 * 1_000_000_000 + 900_000_000,
            "unix_timestamp_nanos must carry the sub-second part"
        );

        clock.advance(Duration::from_millis(600)); // 累计 1.5s
        assert_eq!(clock.unix_timestamp(), 1_001, "cross-second carry");
        assert_eq!(
            clock.unix_timestamp_nanos(),
            1_000 * 1_000_000_000 + 1_500_000_000
        );

        // Instant 与 UNIX 时钟必须同步前进(同刻度)
        assert_eq!(
            clock.now().duration_since(start),
            Duration::from_millis(1500)
        );
    }

    #[test]
    fn test_mock_clock_set_time_subsecond_zeroed() {
        // set_time 以秒为 API 口径,纳秒归零(语义与 with_instant 一致)
        let clock = MockClock::new();
        clock.set_time(Instant::now(), 1_700_000_000);
        assert_eq!(
            clock.unix_timestamp_nanos(),
            1_700_000_000u64 * 1_000_000_000
        );
    }

    #[test]
    fn test_system_clock_now_datetime_close_to_utc_now() {
        let clock = SystemClock;
        let via_clock = clock.now_datetime();
        let via_utc = Utc::now();
        let delta = (via_clock - via_utc).num_milliseconds().abs();
        assert!(
            delta < 1000,
            "SystemClock::now_datetime vs Utc::now difference must be < 1s, got {delta}ms"
        );
    }

    #[test]
    fn test_mock_clock_now_datetime_advance_10s() {
        let clock = MockClock::with_instant(Instant::now(), 1_700_000_000);
        let before = clock.now_datetime();
        clock.advance(Duration::from_secs(10));
        let after = clock.now_datetime();
        assert_eq!(after - before, chrono::Duration::seconds(10));
    }

    #[test]
    fn test_mock_clock_now_datetime_advance_subsecond() {
        let clock = MockClock::with_instant(Instant::now(), 1_700_000_000);
        let before = clock.now_datetime();
        clock.advance(Duration::from_millis(1500));
        let after = clock.now_datetime();
        assert_eq!(after - before, chrono::Duration::milliseconds(1500));
    }
}