From 03f1828561b6560976c1b632b89411a59fa25b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 3 Oct 2026 19:19:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=EF=BC=9A=E6=B8=B8=E7=8E=A9?= =?UTF-8?q?=E8=AE=A1=E6=95=B0=E6=8E=92=E5=BA=8F=E7=A7=BB=E5=87=BA=E4=BA=92?= =?UTF-8?q?=E6=96=A5=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - game_play_counter.rs:drain 只负责在锁内取走增量,新增 sort_deltas 在释放锁后排序,避免 O(n log n) 排序阻塞并发 record --- .../crates/api-server/src/game_play_counter.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/server-rs/crates/api-server/src/game_play_counter.rs b/server-rs/crates/api-server/src/game_play_counter.rs index 60f131c7d..d8fb7c598 100644 --- a/server-rs/crates/api-server/src/game_play_counter.rs +++ b/server-rs/crates/api-server/src/game_play_counter.rs @@ -157,13 +157,17 @@ impl GamePlayCounter { return None; } state.last_flush_at = now; - Some(drain_pending(&mut state.pending)) + let deltas = drain_pending(&mut state.pending); + drop(state); + Some(sort_deltas(deltas)) } /// 无条件取走全部增量,供关停 flush 使用。 pub fn take_pending(&self) -> Vec { let mut state = self.lock(); - drain_pending(&mut state.pending) + let deltas = drain_pending(&mut state.pending); + drop(state); + sort_deltas(deltas) } /// 写库失败时把增量放回,等待下一次 flush。 @@ -207,11 +211,14 @@ impl GamePlayCounter { } fn drain_pending(pending: &mut HashMap) -> Vec { - let mut deltas = pending + pending .drain() .filter_map(|(game_id, delta)| (delta > 0).then_some(GamePlayCountDelta { game_id, delta })) - .collect::>(); - // 稳定批次顺序,便于测试与日志比对。 + .collect() +} + +/// 稳定批次顺序,便于测试与日志比对;调用方已释放计数锁,排序不阻塞并发 `record`。 +fn sort_deltas(mut deltas: Vec) -> Vec { deltas.sort_by(|left, right| left.game_id.cmp(&right.game_id)); deltas }