Skip to content

Commit e20b0f2

Browse files
authored
Merge pull request #109 from zdtsw-forking/sync/upstream-c586e17
[sync] upstream llm-d/llm-d-batch-gateway c586e17 [2026-07-21]
2 parents 0fa9baf + 57d05aa commit e20b0f2

13 files changed

Lines changed: 804 additions & 71 deletions

File tree

.github/workflows/ci-integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
steps:
5353
- uses: actions/checkout@v7
5454

55-
- uses: actions/setup-go@v6
55+
- uses: actions/setup-go@v7
5656
with:
5757
go-version-file: go.mod
5858

.github/workflows/create-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
uses: actions/checkout@v7
6969

7070
- name: Set up Go
71-
uses: actions/setup-go@v6
71+
uses: actions/setup-go@v7
7272
with:
7373
go-version-file: go.mod
7474

.github/workflows/pre-commit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- uses: actions/checkout@v7
2424

2525
- name: Set up Go
26-
uses: actions/setup-go@v6
26+
uses: actions/setup-go@v7
2727
with:
2828
go-version-file: go.mod
2929
cache: true

benchmarks/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ The benchmark compares dispatch strategies under a realistic traffic pattern: in
1515
| 2 | Ungated batch | sync, aggressive | Batch-gateway with high concurrency (global=200, perEndpoint=100), AIMD disabled. No flow control. |
1616
| 3 | AIMD only | sync, AIMD enabled | Processor reactively adjusts concurrency based on 429/5xx backpressure. Single layer. |
1717
| 4 | AIMD + flow control | sync, AIMD + Router | Two layers: Router prioritizes interactive (priority 100) over batch (priority -1, sheddable); AIMD adjusts on top. |
18-
| 5 | Async processor | async, budget gate | Async-processor with PromQL-based dispatch budget. **Blocked on integration.** |
18+
| 5 | Async processor | async, endpoint-scrape gate | Async dispatch via llm-d-async with endpoint-scrape gating on vLLM queue depth. |
1919

2020
### How to read results
2121

2222
- **Scenario 0** = ideal interactive latency with no batch interference
2323
- **Scenarios 1-2** = how much batch degrades interactive without gating
24-
- **Scenarios 3-4** = how gated dispatch protects interactive while batch still makes progress
24+
- **Scenarios 3-5** = how gated dispatch protects interactive while batch still makes progress
2525
- **Scenario 3 vs 4** = incremental value of Router flow control on top of AIMD
26+
- **Scenario 5** = async dispatch path — batch goes through Redis queues + async-processor instead of sync HTTP
2627

2728
## Prerequisites
2829

@@ -243,6 +244,9 @@ Compare TTFT p99 during burst phases across scenarios:
243244
| `GUIDE_NAME` | No | `optimized-baseline` | Inference pool name |
244245
| `BG_IMAGE_REPO` | No || Batch-gateway image repo override |
245246
| `BG_IMAGE_TAG` | No || Batch-gateway image tag override |
247+
| `DISPATCHER_VERSION` | No | `v0.7.3` | Async-processor image version (scenario 5) |
248+
| `DISPATCHER_CHART` | No | OCI chart | Async-processor Helm chart reference |
249+
| `DISPATCHER_CHART_VERSION` | No | `0.7.3` | Async-processor chart version |
246250

247251
## Directory Structure
248252

benchmarks/benchmark.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
2 - Ungated batch (aggressive concurrency, no AIMD)
1212
3 - Admission control + AIMD (saturation-based batch rejection + adaptive concurrency)
1313
4 - Flow control + AIMD (priority dispatch ordering + adaptive concurrency)
14-
5 - Async processor (blocked on integration)
14+
5 - Async processor (async dispatch via llm-d-async with endpoint-scrape gate)
1515
6 - Low batch concurrency (fixed perEndpoint cap, no AIMD)
1616
1717
Usage:
@@ -1185,6 +1185,7 @@ def _generate_narrative(results, cfg):
11851185
ungated = next((r for r in results if r.scenario == 2), None)
11861186
aimd = next((r for r in results if r.scenario == 3), None)
11871187
fc = next((r for r in results if r.scenario == 4), None)
1188+
async_proc = next((r for r in results if r.scenario == 5), None)
11881189
low_conc = next((r for r in results if r.scenario == 6), None)
11891190

11901191
lines = []
@@ -1227,6 +1228,14 @@ def _generate_narrative(results, cfg):
12271228
f"{overhead:.0f}% above baseline ({low_conc_burst['ttft_p99']:.0f} ms) "
12281229
f"using a fixed per-endpoint concurrency cap.")
12291230

1231+
if async_proc:
1232+
async_burst = _aggregate_phases(async_proc.phases, "burst")
1233+
if async_burst and baseline_ttft:
1234+
overhead = ((async_burst["ttft_p99"] - baseline_ttft) / baseline_ttft) * 100
1235+
lines.append(f"Async dispatch (S5) limited interactive TTFT p99 impact to "
1236+
f"{overhead:.0f}% above baseline ({async_burst['ttft_p99']:.0f} ms) "
1237+
f"using async-processor with endpoint-scrape gating.")
1238+
12301239
# Batch completion summary with per-job SLO status
12311240
for r in results:
12321241
if r.scenario >= 2 and r.batch_timeline:
@@ -1948,10 +1957,6 @@ def run_scenario(cfg, scenario):
19481957
namespace = namespace_for_scenario(scenario)
19491958
log(f"━━━ Scenario {scenario}: {name} ━━━")
19501959

1951-
if scenario == 5:
1952-
log(" ERROR: Scenario 5 (async) is blocked on async-processor integration")
1953-
return ScenarioResult(scenario=scenario, name=name)
1954-
19551960
# Verify namespace exists
19561961
try:
19571962
kubectl(["get", "ns", namespace], cfg.context)

benchmarks/setup.sh

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ set -euo pipefail
3030
# BENCH_DB_PASSWORD — PostgreSQL password (default: random 24-char string)
3131
# PROMETHEUS_RELEASE — Prometheus Operator release label for ServiceMonitor discovery (default: llmd-kube-prometheus-stack)
3232
# PROMETHEUS_NAMESPACE — Namespace where Prometheus is deployed (default: llm-d-monitoring)
33+
# DISPATCHER_VERSION — async-processor image version for scenario 5 (default: v0.7.3)
34+
# DISPATCHER_CHART — async-processor Helm chart reference (default: OCI chart)
35+
# DISPATCHER_CHART_VERSION — async-processor chart version (default: 0.7.3)
3336

3437
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3538
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
@@ -166,6 +169,12 @@ GIE_VERSION="${GIE_VERSION:-v1.5.0}"
166169
GIE_REPO="${GIE_REPO:-}"
167170
GIE_UPSTREAM_REPO="https://github.com/kubernetes-sigs/gateway-api-inference-extension.git"
168171

172+
# Async-processor settings for scenario 5
173+
DISPATCHER_VERSION="${DISPATCHER_VERSION:-v0.7.3}"
174+
DISPATCHER_IMAGE="${DISPATCHER_IMAGE:-ghcr.io/llm-d-incubation/llm-d-async:${DISPATCHER_VERSION}}"
175+
DISPATCHER_CHART="${DISPATCHER_CHART:-oci://ghcr.io/llm-d-incubation/charts/async-processor}"
176+
DISPATCHER_CHART_VERSION="${DISPATCHER_CHART_VERSION:-0.7.3}"
177+
169178
# --- Inference backend ---
170179
if [ "${MODE}" = "sim" ]; then
171180
# Sim mode: deploy inference-sim (no GPU, no router, no Istio)
@@ -199,6 +208,11 @@ spec:
199208
- "8000"
200209
- --time-to-first-token=${SIM_TTFT}
201210
- --inter-token-latency=${SIM_ITL}
211+
$([ "${SCENARIO}" = "5" ] && cat <<FAKEARGS
212+
- --fake-metrics
213+
- '{"kv-cache-usage": 0, "waiting-requests": 0, "running-requests": 0}'
214+
FAKEARGS
215+
)
202216
ports:
203217
- containerPort: 8000
204218
name: modelserver
@@ -229,6 +243,14 @@ spec:
229243
name: http
230244
EOF
231245

246+
# --- Scenario 5 sim mode: enable fake metrics on inference-sim ---
247+
if [ "${SCENARIO}" = "5" ]; then
248+
log " Enabling --fake-metrics on inference-sim for endpoint-scrape gate"
249+
${K} -n "${NAMESPACE}" patch deployment inference-sim --type=json \
250+
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--fake-metrics"},{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"{\"kv-cache-usage\": 0, \"waiting-requests\": 0, \"running-requests\": 0}"}]' >/dev/null
251+
${K} -n "${NAMESPACE}" rollout status deployment/inference-sim --timeout=120s >/dev/null
252+
fi
253+
232254
# --- Scenarios 3/4 sim mode: deploy GIE EPP ---
233255
if [ "${SCENARIO}" = "3" ] || [ "${SCENARIO}" = "4" ]; then
234256
log "Deploying GIE EPP (sim mode, scenario ${SCENARIO})"
@@ -500,6 +522,11 @@ if [ -n "${VALUES_FILE}" ]; then
500522
--set-json "processor.config.globalInferenceGateway=null"
501523
--set-json "processor.config.modelGateways={\"${MODEL}\":{\"url\":\"http://epp-bench-epp.${NAMESPACE}.svc.cluster.local:8081\",\"requestTimeout\":\"5m\",\"maxRetries\":3,\"initialBackoff\":\"2s\",\"maxBackoff\":\"30s\",\"inferenceObjective\":\"batch-sheddable\"}}"
502524
)
525+
elif [ "${SCENARIO}" = "5" ]; then
526+
# Scenario 5: async dispatch — use inferencePoolName to match async-processor queue name
527+
BG_EXTRA_ARGS+=(
528+
--set-json "processor.config.modelGateways={\"${MODEL}\":{\"inferencePoolName\":\"sim-pool\"}}"
529+
)
503530
else
504531
# Other scenarios: direct to inference-sim
505532
BG_EXTRA_ARGS+=(
@@ -508,7 +535,7 @@ if [ -n "${VALUES_FILE}" ]; then
508535
fi
509536
fi
510537

511-
${H} install batch-gateway \
538+
${H} upgrade --install batch-gateway \
512539
"${REPO_ROOT}/charts/batch-gateway/" \
513540
-n "${NAMESPACE}" \
514541
-f "${VALUES_FILE}" \
@@ -554,8 +581,59 @@ fi
554581

555582
# --- Scenario 5: Async processor ---
556583
if [ "${SCENARIO}" = "5" ]; then
557-
log "ERROR: Scenario 5 (async) is blocked on async-processor integration"
558-
log " Skipping async-processor deployment"
584+
log "Deploying async-processor (scenario 5)"
585+
586+
# Determine pool name and URLs for queue coordination
587+
if [ "${MODE}" = "sim" ]; then
588+
ASYNC_POOL_NAME="sim-pool"
589+
ASYNC_IGW_URL="http://inference-sim.${NAMESPACE}.svc.cluster.local:8000"
590+
ASYNC_METRICS_URL="http://inference-sim.${NAMESPACE}.svc.cluster.local:8000/metrics"
591+
else
592+
ASYNC_POOL_NAME="${GUIDE_NAME}"
593+
ASYNC_IGW_URL="http://vllm-metrics.${NAMESPACE}.svc.cluster.local:8000"
594+
ASYNC_METRICS_URL="http://vllm-metrics.${NAMESPACE}.svc.cluster.local:8000/metrics"
595+
596+
# Create a Service for the vLLM decode deployment (endpoint-scrape needs it)
597+
log " Creating vLLM metrics Service"
598+
${K} -n "${NAMESPACE}" apply -f - <<EOVLLMSVC
599+
apiVersion: v1
600+
kind: Service
601+
metadata:
602+
name: vllm-metrics
603+
spec:
604+
selector:
605+
llm-d.ai/role: decode
606+
ports:
607+
- port: 8000
608+
targetPort: 8000
609+
name: http
610+
EOVLLMSVC
611+
fi
612+
613+
DISPATCHER_IMAGE_REPO="$(echo "${DISPATCHER_IMAGE}" | cut -d: -f1)"
614+
DISPATCHER_IMAGE_TAG="$(echo "${DISPATCHER_IMAGE}" | cut -d: -f2)"
615+
616+
${H} upgrade --install async-processor "${DISPATCHER_CHART}" \
617+
--version "${DISPATCHER_CHART_VERSION}" \
618+
-n "${NAMESPACE}" \
619+
--set "ap.image.repository=${DISPATCHER_IMAGE_REPO}" \
620+
--set "ap.image.tag=${DISPATCHER_IMAGE_TAG}" \
621+
--set ap.messageQueueImpl=redis-sortedset \
622+
--set ap.concurrency=1 \
623+
--set ap.redis.enabled=true \
624+
--set "ap.redis.url=redis://redis-master.${NAMESPACE}.svc.cluster.local:6379" \
625+
--set ap.redis.pollIntervalMs=500 \
626+
--set ap.redis.batchSize=10 \
627+
--set-json "ap.redis.queuesConfig=[{\"queue_name\":\"llm-d-async:requests:${ASYNC_POOL_NAME}\",\"result_queue_name\":\"llm-d-async:results:${ASYNC_POOL_NAME}\",\"request_path_url\":\"/v1/chat/completions\",\"igw_base_url\":\"${ASYNC_IGW_URL}\",\"gate_type\":\"endpoint-scrape\",\"gate_params\":{\"url\":\"${ASYNC_METRICS_URL}\",\"metric\":\"vllm:num_requests_waiting\",\"max_count_per_pod\":\"5\",\"fallback\":\"1.0\"}}]" \
628+
--set ap.modelServerMonitor.enabled=false \
629+
--set ap.metrics.enabled=true \
630+
--set ap.metrics.port=9091 \
631+
--set ap.metrics.secure=false \
632+
--wait --timeout=120s >/dev/null
633+
634+
log " Waiting for async-processor to be ready..."
635+
${K} -n "${NAMESPACE}" wait --for=condition=available deployment/async-processor --timeout=120s >/dev/null
636+
log " Async-processor deployed (pool: ${ASYNC_POOL_NAME}, gate: endpoint-scrape)"
559637
fi
560638

561639
if [ -n "${VALUES_FILE}" ]; then

benchmarks/teardown.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ teardown_namespace() {
5454
# Non-helm resources
5555
${K} -n "${NS}" delete -k "${SCRIPT_DIR}/manifests/vllm/" --ignore-not-found 2>/dev/null || true
5656
${K} -n "${NS}" delete gateway llm-d-inference-gateway --ignore-not-found 2>/dev/null || true
57+
${K} -n "${NS}" delete svc vllm-metrics --ignore-not-found 2>/dev/null || true
5758
${K} -n "${NS}" delete secret batch-gateway-secrets --ignore-not-found 2>/dev/null || true
5859
${K} -n "${NS}" delete pvc --all --ignore-not-found 2>/dev/null || true
5960

0 commit comments

Comments
 (0)