Skip to main content

bge_m3_embedding_server/embedder/worker/
propagation.rs

1// Copyright (c) 2026 J. Patrick Fulton
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Engine propagation broadcast drain and post-inference cache-miss signaling.
16
17use super::trt_retry::CHUNK_CACHE_HIT_THRESHOLD_MS;
18use crate::embedder::types::{EmbedStats, JitSuspectSender};
19
20/// Emits the `chunk_run` INFO event and, on a cache miss, notifies both the
21/// JIT-suspect channel (adaptive warmup scheduling) and the engine propagation
22/// broadcast channel (peer worker fast disk-load).
23///
24/// Returns `Some((batch_len, max_chunk_seq))` when a shape was broadcast on
25/// the engine propagation channel.  The call site MUST insert this shape into
26/// `warmed_local` so the originating worker self-skips its own broadcast on
27/// the next `drain_engine_propagation` iteration (COR-1).
28///
29/// # 5000 ms threshold heuristic (COR-10)
30///
31/// `CHUNK_CACHE_HIT_THRESHOLD_MS` (5 s) is a **heuristic** proxy for "TRT
32/// engine JIT compile occurred", not a semantic guarantee.  False negatives
33/// are possible for fast-JIT small shapes; false positives are impossible
34/// because a cache-hit path never exceeds ~100 ms.  The trade-off is
35/// acceptable: the worst outcome of a false negative is that the adaptive
36/// warmup task eventually resubmits the shape on the next real cache miss.
37pub(super) fn log_inference_complete(
38    stats: &EmbedStats,
39    worker_id: usize,
40    _route: &'static str,
41    jit_suspect_tx: Option<&JitSuspectSender>,
42    engine_propagation_tx: Option<&tokio::sync::broadcast::Sender<(usize, usize)>>,
43    batch_len: usize,
44) -> Option<(usize, usize)> {
45    let cache_hit = stats.inference_ms < CHUNK_CACHE_HIT_THRESHOLD_MS;
46    tracing::info!(
47        target: "bge_m3_embedding_server::trt_shape",
48        worker_id,
49        chunk_batch = batch_len,
50        chunk_max_seq = stats.max_chunk_seq,
51        inference_ms = stats.inference_ms,
52        cache_hit,
53        "chunk_run"
54    );
55    if !cache_hit {
56        if let Some(tx) = jit_suspect_tx {
57            let _ = tx.try_send((batch_len, stats.max_chunk_seq));
58        }
59        if let Some(tx) = engine_propagation_tx {
60            let _ = tx.send((batch_len, stats.max_chunk_seq));
61            return Some((batch_len, stats.max_chunk_seq));
62        }
63    }
64    None
65}
66
67/// Drains pending broadcast notifications and runs `trt_prewarm` for each
68/// new shape.
69///
70/// Called at the start of each worker loop iteration (between requests) so
71/// peers eagerly warm their in-memory TRT profile before the next real
72/// request for a new shape arrives.
73///
74/// `warmed_local` tracks shapes already warmed by this worker in the current
75/// session.  The originating worker self-skips on subsequent drains because
76/// `log_inference_complete` inserts the broadcast shape into `warmed_local`
77/// at the call site before returning control to the request loop.
78pub(super) fn drain_engine_propagation<F>(
79    rx: &mut tokio::sync::broadcast::Receiver<(usize, usize)>,
80    warmed_local: &mut std::collections::HashSet<(usize, usize)>,
81    worker_id: usize,
82    mut prewarm: F,
83) where
84    F: FnMut((usize, usize)),
85{
86    loop {
87        match rx.try_recv() {
88            Ok(shape) => {
89                if warmed_local.insert(shape) {
90                    prewarm(shape);
91                }
92            }
93            Err(
94                tokio::sync::broadcast::error::TryRecvError::Empty
95                | tokio::sync::broadcast::error::TryRecvError::Closed,
96            ) => break,
97            Err(tokio::sync::broadcast::error::TryRecvError::Lagged(n)) => {
98                tracing::warn!(
99                    worker_id,
100                    lagged = n,
101                    "engine_propagation: broadcast lagged; some shapes missed"
102                );
103                // Continue draining; missed shapes will be re-broadcast on
104                // the next slow-inference event for that shape.
105            }
106        }
107    }
108}