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 spread_tables: DashMap<i32, Vec<CachedSpreadBand>>,
pub cold_cache_waiters: DashMap<String, Arc<Notify>>,
/* private fields */
}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} 占位符).
spread_tables: DashMap<i32, Vec<CachedSpreadBand>>C++ INNData_Qot_Spread: spread table code → price bands.
Filled from CMD6503 at QOT handler registration time and refreshed every
8h, matching NNBiz_Qot_Spread::SetTimerUpdateSpreadInfo. Read paths
are synchronous and lock-free enough for push hot paths.
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
Sourcepub fn make_kline_key_by_dims(sec_key: &str, dims: KlineDims) -> String
pub fn make_kline_key_by_dims(sec_key: &str, dims: KlineDims) -> 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).
pub fn make_kline_key( sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> String
Sourcepub fn update_klines_by_dims(
&self,
sec_key: &str,
dims: KlineDims,
klines: Vec<CachedKLine>,
)
pub fn update_klines_by_dims( &self, sec_key: &str, dims: KlineDims, klines: Vec<CachedKLine>, )
更新 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).
pub fn update_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )
Sourcepub fn get_klines_by_dims(
&self,
sec_key: &str,
dims: KlineDims,
) -> Option<Vec<CachedKLine>>
pub fn get_klines_by_dims( &self, sec_key: &str, dims: KlineDims, ) -> Option<Vec<CachedKLine>>
获取 K 线 (v1.4.106 codex 1140 F3: 4-tuple key, rehab + session 隔离).
pub fn get_klines( &self, sec_key: &str, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>
Sourcepub fn update_klines_broker_by_dims(
&self,
key: &QotSecurityKey,
dims: KlineDims,
klines: Vec<CachedKLine>,
)
pub fn update_klines_broker_by_dims( &self, key: &QotSecurityKey, dims: KlineDims, 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.
pub fn update_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, klines: Vec<CachedKLine>, )
Sourcepub fn get_klines_broker_by_dims(
&self,
key: &QotSecurityKey,
dims: KlineDims,
) -> Option<Vec<CachedKLine>>
pub fn get_klines_broker_by_dims( &self, key: &QotSecurityKey, dims: KlineDims, ) -> Option<Vec<CachedKLine>>
v1.4.110 Phase 2 Slice 5: 获取 K 线 (broker-aware).
pub fn get_klines_broker( &self, key: &QotSecurityKey, rehab: i32, kl_type: i32, session: i32, ) -> Option<Vec<CachedKLine>>
Source§impl QotCache
impl QotCache
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 register_cold_cache_waiter_flight(
&self,
wait_key: &str,
) -> (Arc<Notify>, bool)
pub fn register_cold_cache_waiter_flight( &self, wait_key: &str, ) -> (Arc<Notify>, bool)
Register a cold-cache waiter and identify the request that created the flight.
The returned bool is true only for the caller that inserted the waiter. Handlers use it to singleflight the active Pull_SubData request while still letting all concurrent callers await the same Notify.
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 持有, 自然释放).
pub fn has_cold_cache_waiters(&self) -> bool
pub fn notify_basic_qot_cold_cache_waiters(&self, cache_key: &str)
pub fn notify_order_book_cold_cache_waiters(&self, cache_key: &str)
pub fn notify_odd_lot_order_book_cold_cache_waiters(&self, cache_key: &str)
pub fn notify_ticker_cold_cache_waiters(&self, cache_key: &str)
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).
Source§impl QotCache
impl QotCache
pub fn new() -> Self
Sourcepub fn replace_spread_tables<I>(&self, tables: I)
pub fn replace_spread_tables<I>(&self, tables: I)
Replace the whole spread-table cache after a successful CMD6503 pull.
C++ INNData_Qot_Spread::SetSpreadInfo installs a flattened full table
snapshot. We use a code-keyed map but preserve the same replacement
semantics so stale removed codes do not linger across refreshes.
Sourcepub fn spread_value_raw(&self, spread_code: u32, price_raw: i64) -> i64
pub fn spread_value_raw(&self, spread_code: u32, price_raw: i64) -> i64
Return the raw 1e9 fixed-point spread value for a security price.
Mirrors the spread-band selection in
NNBiz_Qot_Spread::GetStockSpreadPriceForTade with bUp=true and
enTrdMarket=Unknown, which is what quote snapshot / BasicQot push use.
Missing table returns 0, matching C++ cache miss falling through with
initial nPriceSpread=0 at the API projection layer.
Sourcepub fn price_spread_for_raw_price(
&self,
spread_code: u32,
price_raw: i64,
) -> f64
pub fn price_spread_for_raw_price( &self, spread_code: u32, price_raw: i64, ) -> f64
Project BasicQot.priceSpread / SnapshotBasicData.priceSpread.
Sourcepub fn price_spread_for_price(&self, spread_code: u32, price: f64) -> f64
pub fn price_spread_for_price(&self, spread_code: u32, price: f64) -> f64
Project priceSpread from a floating-point API price.
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 get_broker_name_for_app_lang(
&self,
broker_id: i64,
app_lang: i32,
) -> Option<String>
pub fn get_broker_name_for_app_lang( &self, broker_id: i64, app_lang: i32, ) -> Option<String>
latest C++ GetBrokerName 按 App 语言选择券商简称,并在缺省时
fallback 到 en/tc/sc。Rust 当前 CMD 18008 cache 只保存简称三语字段;
因此这里对齐 C++ 的 abbreviation-first 分支,完整名 fallback 等
cache 结构扩展后再自然接入。
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 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 update_odd_lot_order_book_broker(
&self,
key: &QotSecurityKey,
ob: CachedOrderBook,
)
pub fn update_odd_lot_order_book_broker( &self, key: &QotSecurityKey, ob: CachedOrderBook, )
Update odd-lot order book cache (MY/SG only in C++ 10.7).
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 get_odd_lot_order_book_broker(
&self,
key: &QotSecurityKey,
) -> Option<CachedOrderBook>
pub fn get_odd_lot_order_book_broker( &self, key: &QotSecurityKey, ) -> Option<CachedOrderBook>
Get odd-lot order book cache (MY/SG only in C++ 10.7).
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 保留).