diff --git a/python/sglang/srt/managers/scheduler.py b/python/sglang/srt/managers/scheduler.py index 82f33337f..d1a7d2c9e 100644 --- a/python/sglang/srt/managers/scheduler.py +++ b/python/sglang/srt/managers/scheduler.py @@ -3723,6 +3723,19 @@ class Scheduler( self.running_batch.batch_is_full = False self.chunked_req = None + # Surface the paused state to dashboards immediately. The scheduler + # event loop short-circuits before reaching ``on_idle`` while paused, + # so without this hop ``gen_throughput`` retains its last non-zero + # value and KV events are not flushed for the entire pause window + # (e.g. across a weight update). Zero the gauge, force a one-shot + # idle log by resetting the rate-limit timestamp, and flush pending + # KV events. + self.metrics_reporter.last_gen_throughput = 0.0 + if self.metrics_reporter.current_scheduler_metrics_enabled: + self.metrics_reporter.metrics_collector.last_log_time = 0.0 + self.metrics_reporter._maybe_log_idle_metrics() + self.kv_events_publisher.publish_kv_events() + def continue_generation(self, recv_req: ContinueGenerationReqInput): if recv_req.torch_empty_cache: before_mb = torch.cuda.memory_reserved() / (1024 * 1024) diff --git a/test/registered/unit/managers/test_scheduler_pause_generation.py b/test/registered/unit/managers/test_scheduler_pause_generation.py index 170e8bac0..7b3e9b20b 100644 --- a/test/registered/unit/managers/test_scheduler_pause_generation.py +++ b/test/registered/unit/managers/test_scheduler_pause_generation.py @@ -43,6 +43,10 @@ class TestSchedulerPauseGeneration(unittest.TestCase): full_evictable_size=0, ) ) + # pause_generation zeros gen_throughput and flushes KV events. + scheduler.metrics_reporter = MagicMock() + scheduler.metrics_reporter.current_scheduler_metrics_enabled = False + scheduler.kv_events_publisher = MagicMock() return scheduler def test_inplace_only_sets_flag(self):