Skip to main content

futu_cache/qot_cache/
waiters.rs

1use std::sync::Arc;
2use std::sync::atomic::Ordering;
3
4use dashmap::mapref::entry::Entry;
5use tokio::sync::Notify;
6
7use super::QotCache;
8
9/// v1.4.110 codex Phase 3 Slice 6c: cold-cache wait key for BasicQot.
10///
11/// 输入 cache_key (legacy `"market_code"` 或 broker-aware
12/// `"market_code@b1007"`), 输出 `"<cache_key>:basic"` 形式 wait key.
13/// 与 OrderBook wait key 分桶, 避免互相错唤醒.
14#[inline]
15pub fn basic_qot_wait_key(cache_key: &str) -> String {
16    format!("{cache_key}:basic")
17}
18
19/// v1.4.110 codex Phase 3 Slice 6c: cold-cache wait key for OrderBook.
20#[inline]
21pub fn order_book_wait_key(cache_key: &str) -> String {
22    format!("{cache_key}:orderbook")
23}
24
25#[inline]
26pub fn odd_lot_order_book_cache_key(cache_key: &str) -> String {
27    format!("{cache_key}:orderbook_odd")
28}
29
30#[inline]
31pub fn odd_lot_order_book_wait_key(cache_key: &str) -> String {
32    format!("{cache_key}:orderbook_odd")
33}
34
35pub fn ticker_wait_key(cache_key: &str) -> String {
36    format!("{cache_key}:ticker")
37}
38
39impl QotCache {
40    /// v1.4.110 codex Phase 3 Slice 6c: 注册 cold-cache wait waiter.
41    ///
42    /// 返已存在或新建的 `Arc<Notify>`. handler 调:
43    /// 1. `register_cold_cache_waiter("91_BTCUSDT@b1007:basic")` 获 Notify
44    /// 2. 主动发 Pull_SubData CMD6824
45    /// 3. `tokio::time::timeout(Duration::from_secs(3), notify.notified())` 等
46    /// 4. 再 `get_basic_qot_broker(&key)` 读 cache (可能仍 None — 真 timeout)
47    ///
48    /// `wait_kind` 推荐: `"basic"` / `"orderbook"`. 不混 sub_type 数字防误唤.
49    pub fn register_cold_cache_waiter(&self, wait_key: &str) -> Arc<Notify> {
50        self.register_cold_cache_waiter_flight(wait_key).0
51    }
52
53    /// Register a cold-cache waiter and identify the request that created the
54    /// flight.
55    ///
56    /// The returned bool is true only for the caller that inserted the waiter.
57    /// Handlers use it to singleflight the active Pull_SubData request while
58    /// still letting all concurrent callers await the same Notify.
59    pub fn register_cold_cache_waiter_flight(&self, wait_key: &str) -> (Arc<Notify>, bool) {
60        match self.cold_cache_waiters.entry(wait_key.to_string()) {
61            Entry::Occupied(entry) => (entry.get().clone(), false),
62            Entry::Vacant(entry) => {
63                self.cold_cache_waiter_entries
64                    .fetch_add(1, Ordering::Relaxed);
65                (entry.insert(Arc::new(Notify::new())).clone(), true)
66            }
67        }
68    }
69
70    /// v1.4.110 codex Phase 3 Slice 6c: 唤醒指定 cold-cache wait waiter.
71    ///
72    /// push parser update path 调 (`update_basic_qot` / `update_order_book` /
73    /// `_broker` 变种). 没 waiter → no-op. 有 waiter → `notify_waiters()`
74    /// broadcast 给所有 awaiter, 然后 remove (Arc 仍被 awaiter 持有, 自然释放).
75    pub fn notify_cold_cache_waiters(&self, wait_key: &str) {
76        if !self.has_cold_cache_waiters() {
77            return;
78        }
79        if let Some((_, n)) = self.cold_cache_waiters.remove(wait_key) {
80            self.decrement_cold_cache_waiter_entries();
81            n.notify_waiters();
82        }
83    }
84
85    pub fn has_cold_cache_waiters(&self) -> bool {
86        self.cold_cache_waiter_entries.load(Ordering::Relaxed) > 0
87    }
88
89    pub fn notify_basic_qot_cold_cache_waiters(&self, cache_key: &str) {
90        if self.has_cold_cache_waiters() {
91            self.notify_cold_cache_waiters(&basic_qot_wait_key(cache_key));
92        }
93    }
94
95    pub fn notify_order_book_cold_cache_waiters(&self, cache_key: &str) {
96        if self.has_cold_cache_waiters() {
97            self.notify_cold_cache_waiters(&order_book_wait_key(cache_key));
98        }
99    }
100
101    pub fn notify_odd_lot_order_book_cold_cache_waiters(&self, cache_key: &str) {
102        if self.has_cold_cache_waiters() {
103            self.notify_cold_cache_waiters(&odd_lot_order_book_wait_key(cache_key));
104        }
105    }
106
107    pub fn notify_ticker_cold_cache_waiters(&self, cache_key: &str) {
108        if self.has_cold_cache_waiters() {
109            self.notify_cold_cache_waiters(&ticker_wait_key(cache_key));
110        }
111    }
112
113    fn decrement_cold_cache_waiter_entries(&self) {
114        let _ = self.cold_cache_waiter_entries.fetch_update(
115            Ordering::Relaxed,
116            Ordering::Relaxed,
117            |count| count.checked_sub(1),
118        );
119    }
120
121    /// v1.4.110 codex audit Round3 #22: cold-cache wait timeout 后清 idle waiter.
122    ///
123    /// `wait_for_basic_cache` / `wait_for_order_book_cache` 3s timeout 仍 cache
124    /// miss 时调. 若 push 始终没来, `notify_cold_cache_waiters` 不会触发, entry
125    /// 会一直留在 `cold_cache_waiters` map (虽 bounded by distinct wait_key 数,
126    /// 仍是慢速 leak).
127    ///
128    /// **只删 caller 自己注册的那个 entry, 且无其他并发 awaiter 时才删**:
129    /// `remove_if` closure 在 entry lock 下原子检查两条:
130    /// 1. `Arc::ptr_eq(stored, caller_notify)` — stored 必须就是 caller 当初
131    ///    `register_cold_cache_waiter` 拿到的同一 Arc. 防 race: caller timeout
132    ///    后到本调用之间, 若 push 触发 `notify_cold_cache_waiters` 删了旧 entry,
133    ///    另一个 `wait_for_*` 又 register 建了**新** entry (不同 Arc), `ptr_eq`
134    ///    false → 不误删别人的新 entry.
135    /// 2. `Arc::strong_count(stored) <= 2` — DashMap stored Arc 1 + caller
136    ///    持有的 `caller_notify` 1. `> 2` 表示有其他 `wait_for_*` 仍 await 同
137    ///    entry → 保留让它们能被 notify 唤醒.
138    ///
139    /// caller 约定: 必须把 `register_cold_cache_waiter` 返回的 `Arc<Notify>`
140    /// 原样传进来 (caller 全程持有未 drop).
141    pub fn cleanup_cold_cache_waiter_if_idle(&self, wait_key: &str, caller_notify: &Arc<Notify>) {
142        if !self.has_cold_cache_waiters() {
143            return;
144        }
145        if self
146            .cold_cache_waiters
147            .remove_if(wait_key, |_, stored| {
148                Arc::ptr_eq(stored, caller_notify) && Arc::strong_count(stored) <= 2
149            })
150            .is_some()
151        {
152            self.decrement_cold_cache_waiter_entries();
153        }
154    }
155}