pub struct QotCache {
pub basic_qot: DashMap<SecurityKey, CachedBasicQot>,
pub us_stock_overnight: DashMap<u64, bool>,
pub klines: DashMap<String, Vec<CachedKLine>>,
pub order_books: DashMap<SecurityKey, CachedOrderBook>,
pub tickers: DashMap<SecurityKey, Vec<CachedTicker>>,
pub rt_data: DashMap<String, Vec<CachedTimeShare>>,
pub brokers: DashMap<SecurityKey, CachedBroker>,
pub broker_dict: DashMap<i64, CachedBrokerInfo>,
pub cold_cache_waiters: DashMap<String, Arc<Notify>>,
}Expand description
行情缓存管理器
Fields§
§basic_qot: DashMap<SecurityKey, CachedBasicQot>基本报价缓存
us_stock_overnight: DashMap<u64, bool>US stock overnight-enabled state, keyed by backend stock_id.
C++ stores this as stockID -> bool in INNData_Qot_USStockOvernight:
NNData_Qot_USStockOvernight.cpp:21-35(missing key => false)NNBiz_Qot_USStockState.cpp:180-190writesovernight_type == 1APIServer_Qot_MarketState.cpp:238-244reads it for 11 -> 37 projection
klines: DashMap<String, Vec<CachedKLine>>K 线缓存: key = “market_code:kl_type”
order_books: DashMap<SecurityKey, CachedOrderBook>摆盘缓存
tickers: DashMap<SecurityKey, Vec<CachedTicker>>逐笔缓存: 保留最近 N 条
rt_data: DashMap<String, Vec<CachedTimeShare>>分时缓存
v1.4.106 codex 1140 F6 (P2 audit Finding 6): RT cache key 加 session
维度. 之前 DashMap<SecurityKey, ...> 把 RTH/ETH/PRE/AFTER 全部混到
同一桶, 客户端订阅 RTH 也能读到 PRE 数据. 现在 key 是
“sec_key:s{session}” (RequestSection 0=NORMAL/1=FULL/2=PREMARKET/
3=AFTERHOURS), 隔离不同 session.
brokers: DashMap<SecurityKey, CachedBroker>经纪队列缓存
broker_dict: DashMap<i64, CachedBrokerInfo>v1.4.106 codex 1140 F7 (P2 audit Finding 7): 券商 ID → 信息映射.
由 CMD 18008 拉取后填充, 用于 push parser 从 broker_id 查真名 (替代
Broker#{bid} 占位符).
cold_cache_waiters: DashMap<String, Arc<Notify>>v1.4.110 codex Phase 3 Slice 6c: cold-cache wait waiters.
key = "<cache_key>:<wait_kind>" (e.g. "91_BTCUSDT@b1007:basic" /
"1_00700:orderbook"). value = shared Arc<Notify> 让 handler 阻塞等
push parser 写 cache 后唤醒.
对齐 C++ APIServer_Qot_StockBasic.cpp:226-320 WaitForReady —
已订阅但 cache 未就绪时 handler 主动 Pull_SubData + 等 push 写 cache.
设计 trade-off:
- 用
DashMap<String, Arc<Notify>>而非RwLock<HashMap>: 高并发读 写不锁全表 - key 编码 wait_kind 防 basic / orderbook 共用同一 Notify 互相错唤醒
- update path 调
notify_waiters(broadcast 给所有 awaiter) 然后从 map 中 remove (Arc 被 awaiter 持有, 自然释放)
Implementations§
Source§impl QotCache
impl QotCache
pub fn new() -> Self
Sourcepub fn register_cold_cache_waiter(&self, wait_key: &str) -> Arc<Notify>
pub fn register_cold_cache_waiter(&self, wait_key: &str) -> Arc<Notify>
v1.4.110 codex Phase 3 Slice 6c: 注册 cold-cache wait waiter.
返已存在或新建的 Arc<Notify>. handler 调:
register_cold_cache_waiter("91_BTCUSDT@b1007:basic")获 Notify- 主动发 Pull_SubData CMD6824
tokio::time::timeout(Duration::from_secs(3), notify.notified())等- 再
get_basic_qot_broker(&key)读 cache (可能仍 None — 真 timeout)
wait_kind 推荐: "basic" / "orderbook". 不混 sub_type 数字防误唤.
Sourcepub fn notify_cold_cache_waiters(&self, wait_key: &str)
pub fn notify_cold_cache_waiters(&self, wait_key: &str)
v1.4.110 codex Phase 3 Slice 6c: 唤醒指定 cold-cache wait waiter.
push parser update path 调 (update_basic_qot / update_order_book /
_broker 变种). 没 waiter → no-op. 有 waiter → notify_waiters()
broadcast 给所有 awaiter, 然后 remove (Arc 仍被 awaiter 持有, 自然释放).
Sourcepub fn cleanup_cold_cache_waiter_if_idle(
&self,
wait_key: &str,
caller_notify: &Arc<Notify>,
)
pub fn cleanup_cold_cache_waiter_if_idle( &self, wait_key: &str, caller_notify: &Arc<Notify>, )
v1.4.110 codex audit Round3 #22: cold-cache wait timeout 后清 idle waiter.
wait_for_basic_cache / wait_for_order_book_cache 3s timeout 仍 cache
miss 时调. 若 push 始终没来, notify_cold_cache_waiters 不会触发, entry
会一直留在 cold_cache_waiters map (虽 bounded by distinct wait_key 数,
仍是慢速 leak).
只删 caller 自己注册的那个 entry, 且无其他并发 awaiter 时才删:
remove_if closure 在 entry lock 下原子检查两条:
Arc::ptr_eq(stored, caller_notify)— stored 必须就是 caller 当初register_cold_cache_waiter拿到的同一 Arc. 防 race: caller timeout 后到本调用之间, 若 push 触发notify_cold_cache_waiters删了旧 entry, 另一个wait_for_*又 register 建了新 entry (不同 Arc),ptr_eqfalse → 不误删别人的新 entry.Arc::strong_count(stored) <= 2— DashMap stored Arc 1 + caller 持有的caller_notify1.> 2表示有其他wait_for_*仍 await 同 entry → 保留让它们能被 notify 唤醒.
caller 约定: 必须把 register_cold_cache_waiter 返回的 Arc<Notify>
原样传进来 (caller 全程持有未 drop).
Sourcepub fn set_us_stock_overnight_state(&self, stock_id: u64, is_overnight: bool)
pub fn set_us_stock_overnight_state(&self, stock_id: u64, is_overnight: bool)
Update C++-style US overnight stock state (stockID -> bool).
Ref: NNData_Qot_USStockOvernight.cpp:21-35 and
NNBiz_Qot_USStockState.cpp:180-190.
Sourcepub fn is_us_stock_overnight(&self, stock_id: u64) -> bool
pub fn is_us_stock_overnight(&self, stock_id: u64) -> bool
Query whether a US stock is currently in overnight trading.
C++ cache miss returns false (NNData_Qot_USStockOvernight.cpp:29-34).
Sourcepub fn get_broker_name(&self, broker_id: i64) -> Option<String>
pub fn get_broker_name(&self, broker_id: i64) -> Option<String>
v1.4.106 codex 1140 F7 (P2): 查 broker_id → broker name (中文简称).
cache miss → None, 调用方决定 fallback 策略 (push parser 用
Broker#{id} 作 emergency fallback, 但同时 warn-log 提示 dict 未加载).
Sourcepub fn install_broker_dict(&self, entries: Vec<(i64, CachedBrokerInfo)>)
pub fn install_broker_dict(&self, entries: Vec<(i64, CachedBrokerInfo)>)
v1.4.106 codex 1140 F7 (P2): 批量写入 broker dict (CMD 18008 解析后调).
Sourcepub fn update_basic_qot(&self, key: &str, qot: CachedBasicQot)
pub fn update_basic_qot(&self, key: &str, qot: CachedBasicQot)
更新基本报价
Sourcepub fn get_basic_qot(&self, key: &str) -> Option<CachedBasicQot>
pub fn get_basic_qot(&self, key: &str) -> Option<CachedBasicQot>
获取基本报价
Sourcepub fn update_basic_qot_broker(&self, key: &QotSecurityKey, qot: CachedBasicQot)
pub fn update_basic_qot_broker(&self, key: &QotSecurityKey, qot: CachedBasicQot)
v1.4.110 Phase 2 Slice 5: 更新基本报价 (broker-aware).
用 QotSecurityKey::cache_key() 派生 String key. broker_id=None → 与
update_basic_qot(public_sec_key, ...) 等价; broker_id=Some(N) → 写
独立 cache key "market_code@b{N}" (crypto multi-broker isolation).
Sourcepub fn get_basic_qot_broker(
&self,
key: &QotSecurityKey,
) -> Option<CachedBasicQot>
pub fn get_basic_qot_broker( &self, key: &QotSecurityKey, ) -> Option<CachedBasicQot>
v1.4.110 Phase 2 Slice 5: 获取基本报价 (broker-aware).
Sourcepub fn make_rt_key(sec_key: &str, session: i32) -> String
pub fn make_rt_key(sec_key: &str, session: i32) -> String
构造 RT 分时 cache key (v1.4.106 codex 1140 F6).
之前 key 仅 sec_key, RTH/ETH/PRE/AFTER/OVERNIGHT 混桶. 现在
“sec_key:s{session}” 隔离. session 来自 push 解析的
TimeSharingPlans.section_type[0] (RequestSection enum):
0=NORMAL/1=FULL/2=PREMARKET/3=AFTERHOURS/5=OVERNIGHT.
GetRT handler 对 C++ 的 Session_ETH/Session_ALL 读取语义做动态拼接,
不依赖额外 aggregate cache 桶。
Sourcepub fn make_rt_key_broker(key: &QotSecurityKey, session: i32) -> String
pub fn make_rt_key_broker(key: &QotSecurityKey, session: i32) -> String
v1.4.110 Phase 2 Slice 5: 构造 RT 分时 cache key (broker-aware).
用 QotSecurityKey::cache_key() 作 prefix. broker_id=None → 与
make_rt_key(public_sec_key, session) 等价; broker_id=Some(N) → 写
独立 cache key "market_code@b{N}:s{session}".
Sourcepub fn update_rt_data_broker(
&self,
key: &QotSecurityKey,
session: i32,
rt_data: Vec<CachedTimeShare>,
)
pub fn update_rt_data_broker( &self, key: &QotSecurityKey, session: i32, rt_data: Vec<CachedTimeShare>, )
v1.4.110 Phase 2 Slice 5: 更新 RT 分时 (broker-aware).
Sourcepub fn get_rt_data_broker(
&self,
key: &QotSecurityKey,
session: i32,
) -> Option<Vec<CachedTimeShare>>
pub fn get_rt_data_broker( &self, key: &QotSecurityKey, session: i32, ) -> Option<Vec<CachedTimeShare>>
v1.4.110 Phase 2 Slice 5: 获取 RT 分时 (broker-aware).
Sourcepub fn make_kline_key(
sec_key: &str,
rehab: i32,
kl_type: i32,
session: i32,
) -> String
pub fn make_kline_key( sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> String
构造 K 线 cache key (v1.4.106 codex 1140 F3 4-tuple).
之前 key 仅 (sec_key, kl_type) 2-tuple, 同股票同 KLType 但前复权 vs
后复权 / RTH vs ETH 数据互相覆盖. 对齐 C++ APIServer_Qot_KL.cpp:
GetNewestKLByCount(stock_id, enRehabType, enKLType, num, session, ...)
用 4 维 key.
rehab: proto Qot_Common.RehabType (0=None, 1=Forward, 2=Backward), 对齐 backendFTCmdKline.ExrightType. 同一股票同一 kl_type 不同 rehab 走独立 cache, 不互相覆盖.kl_type: proto Qot_Common.KLType (1=1Min, 2=Day, …, 11=Quarter).session: proto FTCmdKline.RequestSection (0=NORMAL, 1=FULL, 2=PREMARKET, 3=AFTERHOURS). RTH/ETH 隔离, push 来自 backend 的point.section_type[0]决定写入桶, read 由 client subscription session 决定 (尚无, 默认 NORMAL).
Sourcepub fn update_klines(
&self,
sec_key: &str,
rehab: i32,
kl_type: i32,
session: i32,
klines: Vec<CachedKLine>,
)
pub fn update_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )
更新 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).
Sourcepub fn get_klines(
&self,
sec_key: &str,
rehab: i32,
kl_type: i32,
session: i32,
) -> Option<Vec<CachedKLine>>
pub fn get_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>
获取 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).
Sourcepub fn update_klines_broker(
&self,
key: &QotSecurityKey,
rehab: i32,
kl_type: i32,
session: i32,
klines: Vec<CachedKLine>,
)
pub fn update_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )
v1.4.110 Phase 2 Slice 5: 更新 K 线 (broker-aware).
用 QotSecurityKey::cache_key() 作 prefix, broker_id=None 时退化到原行为.
composite 维度仍是 4-tuple (rehab, kl_type, session), broker_id 是第 5
维通过 QotSecurityKey 注入到 prefix.
Sourcepub fn get_klines_broker(
&self,
key: &QotSecurityKey,
rehab: i32,
kl_type: i32,
session: i32,
) -> Option<Vec<CachedKLine>>
pub fn get_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>
v1.4.110 Phase 2 Slice 5: 获取 K 线 (broker-aware).
Sourcepub fn update_order_book(&self, key: &str, ob: CachedOrderBook)
pub fn update_order_book(&self, key: &str, ob: CachedOrderBook)
更新摆盘
Sourcepub fn update_order_book_broker(
&self,
key: &QotSecurityKey,
ob: CachedOrderBook,
)
pub fn update_order_book_broker( &self, key: &QotSecurityKey, ob: CachedOrderBook, )
v1.4.110 Phase 2 Slice 5: 更新摆盘 (broker-aware).
Sourcepub fn get_order_book_broker(
&self,
key: &QotSecurityKey,
) -> Option<CachedOrderBook>
pub fn get_order_book_broker( &self, key: &QotSecurityKey, ) -> Option<CachedOrderBook>
v1.4.110 Phase 2 Slice 5: 获取摆盘 (broker-aware).
Sourcepub fn append_tickers(&self, key: &str, new_tickers: Vec<CachedTicker>)
pub fn append_tickers(&self, key: &str, new_tickers: Vec<CachedTicker>)
追加逐笔(保留最近 1000 条)
Sourcepub fn append_tickers_broker(
&self,
key: &QotSecurityKey,
new_tickers: Vec<CachedTicker>,
)
pub fn append_tickers_broker( &self, key: &QotSecurityKey, new_tickers: Vec<CachedTicker>, )
v1.4.110 Phase 2 Slice 5: 追加逐笔 (broker-aware).
Sourcepub fn get_tickers_broker(
&self,
key: &QotSecurityKey,
) -> Option<Vec<CachedTicker>>
pub fn get_tickers_broker( &self, key: &QotSecurityKey, ) -> Option<Vec<CachedTicker>>
v1.4.110 Phase 2 Slice 5: 获取逐笔 (broker-aware).
Sourcepub fn update_broker(&self, key: &str, broker: CachedBroker)
pub fn update_broker(&self, key: &str, broker: CachedBroker)
更新经纪队列
Sourcepub fn get_broker(&self, key: &str) -> Option<CachedBroker>
pub fn get_broker(&self, key: &str) -> Option<CachedBroker>
获取经纪队列
Sourcepub fn clear_security(&self, key: &str)
pub fn clear_security(&self, key: &str)
清除指定股票的所有缓存
Sourcepub fn clear_security_broker(&self, key: &QotSecurityKey)
pub fn clear_security_broker(&self, key: &QotSecurityKey)
v1.4.110 Phase 2 Slice 5: 清除指定股票的所有缓存 (broker-aware).
用 QotSecurityKey::cache_key() 派生 cache key 字符串. broker_id=None
→ 与 clear_security(public_sec_key) 等价; broker_id=Some(N) → 只清
该 broker 下的 cache (其他 broker 下同 stock_id 的 cache 保留).