Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

192 lines
6.7 KiB
Python

# Copyright 2023-2024 SGLang Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
import logging
import time
from collections import defaultdict
from dataclasses import dataclass
from typing import Dict, List, Optional
from sglang.srt.managers.schedule_batch import Req
logger = logging.getLogger(__name__)
DRAIN_SCHEDULE_TOLERANCE = 1.2
@dataclass
class AdapterStats:
num_waiting_reqs: int = 0
max_wait_time_secs: float = 0.0
max_remaining_tokens: int = 0
is_draining_for: Optional[str] = None
def _reset_stats(self):
self.num_waiting_reqs = 0
self.max_wait_time_secs = 0.0
self.max_remaining_tokens = 0
def is_starving(self, drain_wait_threshold: float):
return (
self.max_wait_time_secs > drain_wait_threshold and self.num_waiting_reqs > 0
)
class LoRADrainer:
"""
Drainer for LoRA requests that manages draining. It tracks:
- Number of waiting requests per adapter
- Maximum wait time for requests needing each adapter
- Maximum number of tokens needed for running requests for each adapter
"""
def __init__(self, max_loras_per_batch: int, max_wait_time_secs: float = 0.0):
self.max_loras_per_batch = max_loras_per_batch
self.max_wait_time_secs = max_wait_time_secs
self.adapter_to_stats: Dict[Optional[str], AdapterStats] = defaultdict(
AdapterStats
)
def update_draining_state(
self,
waiting_queue: List[Req],
running_reqs: List[Req],
) -> None:
"""
Update LoRA drainer state based on current waiting queue and running requests.
This method updates adapter statistics, identifies starving adapters that need
to be scheduled, and marks adapters for draining to make room for starving ones.
"""
self._update_adapter_stats(waiting_queue, running_reqs)
self._update_draining_loras(running_reqs)
self._update_fully_drained_loras(running_reqs)
def _update_adapter_stats(
self,
waiting_queue: List[Req],
running_reqs: List[Req],
) -> None:
for stats in self.adapter_to_stats.values():
stats._reset_stats()
for req in waiting_queue:
stats = self.adapter_to_stats[req.lora_id]
stats.num_waiting_reqs += 1
stats.max_wait_time_secs = max(
stats.max_wait_time_secs,
time.monotonic() - req.time_stats.wait_queue_entry_time,
)
for req in running_reqs:
stats = self.adapter_to_stats[req.lora_id]
stats.max_remaining_tokens = max(
stats.max_remaining_tokens,
req.sampling_params.max_new_tokens - len(req.output_ids),
)
def _update_draining_loras(self, running_reqs: List[Req]) -> None:
"""
Select LoRA adapters to drain based on starvation detection.
This method identifies adapters in the waiting queue that are "starving"
(waiting too long) and marks currently running adapters as "draining"
to make room for the starving adapters. Draining adapters will not
accept new requests, allowing them to complete and free up slots.
"""
running_adapter_ids = {req.lora_id for req in running_reqs}
if len(running_adapter_ids) < self.max_loras_per_batch:
return None
starving_adapters = set()
draining_for_adapters = set()
for adapter_id, stats in self.adapter_to_stats.items():
if stats.is_starving(self.max_wait_time_secs):
starving_adapters.add(adapter_id)
draining_for_adapter = stats.is_draining_for
if draining_for_adapter is not None:
draining_for_adapters.add(draining_for_adapter)
new_starving_adapters = starving_adapters - draining_for_adapters
if not new_starving_adapters:
return None
sorted_new_starving_adapters = sorted(
new_starving_adapters,
key=lambda adapter: self.adapter_to_stats[adapter].max_wait_time_secs,
reverse=True,
)
eligible_to_drain_adapters = {
adapter
for adapter in running_adapter_ids
if self.adapter_to_stats[adapter].is_draining_for is None
}
for starving_adapter in sorted_new_starving_adapters:
if not eligible_to_drain_adapters:
break
min_eligible_adapter = min(
eligible_to_drain_adapters,
key=lambda adapter_id: self.adapter_to_stats[
adapter_id
].max_remaining_tokens,
)
self.adapter_to_stats[min_eligible_adapter].is_draining_for = (
starving_adapter
)
logger.debug(
f"LoRA adapter {min_eligible_adapter} is draining for {starving_adapter}"
)
eligible_to_drain_adapters.remove(min_eligible_adapter)
def _update_fully_drained_loras(self, running_reqs: List[Req]) -> None:
"""
Clear draining state for adapters that have fully drained.
An adapter is considered fully drained when it was marked as draining
but no longer has any running requests.
"""
running_adapter_ids = {req.lora_id for req in running_reqs}
for adapter_id, stats in self.adapter_to_stats.items():
if stats.is_draining_for is None:
continue
if adapter_id not in running_adapter_ids:
logger.debug(f"LoRA adapter {adapter_id} finished draining")
stats.is_draining_for = None
def can_schedule(self, req: Req) -> bool:
"""
Check if a request can be scheduled based on draining state.
If the adapter for this request is currently draining, only allow
scheduling if the request's max_new_tokens is within tolerance of
the max remaining tokens for the draining adapter.
"""
stats = self.adapter_to_stats[req.lora_id]
if not stats.is_draining_for:
return True
return (
req.sampling_params.max_new_tokens
<= stats.max_remaining_tokens * DRAIN_SCHEDULE_TOLERANCE
)