Skip to content

[Bugfix] Miscalculated load with cache aware routing - #211

Open
simondanielsson wants to merge 4 commits into
vllm-project:mainfrom
simondanielsson:bugfix/double-decrement
Open

[Bugfix] Miscalculated load with cache aware routing#211
simondanielsson wants to merge 4 commits into
vllm-project:mainfrom
simondanielsson:bugfix/double-decrement

Conversation

@simondanielsson

@simondanielsson simondanielsson commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Purpose

Caused by double-decrements on request counters, making the load look lower than it is.

Details

The code previously handled increments and decrements manually, which was very error prone because decrements need to happen in many places and conditions.

Solution: Use a RAII guard pattern to automatically decrement on every exit path of send_typed_request (including early returns).

There were at least two instance of double decrementing:

  1. This is clearly a double count since line 906 always runs

let response = match res.bytes().await {
Ok(body) => {
let mut response = Response::new(axum::body::Body::from(body));
*response.status_mut() = status;
*response.headers_mut() = response_headers;
response
}
Err(e) => {
// IMPORTANT: Decrement load on error before returning
if load_incremented {
if let Some(worker) = self.worker_registry.get_by_url(worker_url) {
worker.decrement_load();
RouterMetrics::set_running_requests(worker_url, worker.load());
}
}
let error_msg = format!("Failed to get response body: {}", e);
(StatusCode::INTERNAL_SERVER_ERROR, error_msg).into_response()
}
};
// Decrement load counter for non-streaming requests if it was incremented
if load_incremented {
if let Some(worker) = self.worker_registry.get_by_url(worker_url) {
worker.decrement_load();
RouterMetrics::set_running_requests(worker_url, worker.load());
}
}

  1. Not needed as line 906 always decrements anyways, even with retries

// For retryable failures, we need to decrement load since send_typed_request
// won't have done it (it only decrements on success or non-retryable failures)
if is_retryable_status(response.status()) && load_incremented {
if let Some(cleanup_worker) = worker_for_cleanup {
cleanup_worker.decrement_load();
RouterMetrics::set_running_requests(
cleanup_worker.url(),
cleanup_worker.load(),
);
}
}

Test Plan

Added new test cases to ensure load is correct in many different cases:

  1. streamed/not streamed
  2. retried/not retried
  3. response outcome (success, retryable, not retryable, send failed)
cargo test --lib load

Test Result

Without fixes: 4 fail

running 21 tests
test core::worker::tests::test_load_counter_operations ... ok
test core::worker::tests::test_concurrent_load_decrements ... ok
test core::worker::tests::test_load_guard_multiple_workers ... ok
test core::worker::tests::test_concurrent_load_increments ... ok
test core::worker::tests::test_load_guard_single_worker ... ok
test core::worker::tests::test_total_load_calculation ... ok
test policies::cache_aware::tests::test_cache_aware_with_imbalanced_load ... ok
test policies::power_of_two::tests::test_power_of_two_with_cached_loads ... ok
test policies::cache_aware::tests::test_cache_aware_with_balanced_load ... ok
test core::worker::tests::test_load_guard_panic_safety ... ok
test core::worker::tests::test_load_counter_performance ... ok
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_success ... ok
test routers::http::router::tests::test_load_returns_to_baseline_streaming_success ... ok
test routers::http::pd_router::tests::test_load_monitor_updates ... ok
test routers::http::router::tests::test_load_returns_to_baseline_on_send_failure ... FAILED
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_non_retryable ... ok
test routers::http::router::tests::test_load_counter_not_double_decremented_on_retryable_status ... FAILED
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_retries_exhausted ... FAILED
test routers::http::router::tests::test_streaming_retryable_does_not_leak_load ... ok
test routers::http::router::tests::test_load_returns_to_baseline_streaming_with_retries ... FAILED
test tokenizer::factory::tests::test_download_tokenizer_from_hf ... ok

failures:

---- routers::http::router::tests::test_load_returns_to_baseline_on_send_failure stdout ----

thread 'routers::http::router::tests::test_load_returns_to_baseline_on_send_failure' (98149) panicked at src/routers/http/router.rs:2497:9:
assertion `left == right` failed
  left: 4
 right: 5

---- routers::http::router::tests::test_load_counter_not_double_decremented_on_retryable_status stdout ----

thread 'routers::http::router::tests::test_load_counter_not_double_decremented_on_retryable_status' (98145) panicked at src/routers/http/router.rs:2281:9:
assertion `left == right` failed: worker load must return exactly to baseline (no double decrement)
  left: 4
 right: 5

---- routers::http::router::tests::test_load_returns_to_baseline_non_streaming_retries_exhausted stdout ----

thread 'routers::http::router::tests::test_load_returns_to_baseline_non_streaming_retries_exhausted' (98147) panicked at src/routers/http/router.rs:2369:9:
assertion `left == right` failed
  left: 2
 right: 5

---- routers::http::router::tests::test_load_returns_to_baseline_streaming_with_retries stdout ----

thread 'routers::http::router::tests::test_load_returns_to_baseline_streaming_with_retries' (98151) panicked at src/routers/http/router.rs:2479:9:
assertion `left == right` failed
  left: 2
 right: 5


failures:
    routers::http::router::tests::test_load_counter_not_double_decremented_on_retryable_status
    routers::http::router::tests::test_load_returns_to_baseline_non_streaming_retries_exhausted
    routers::http::router::tests::test_load_returns_to_baseline_on_send_failure
    routers::http::router::tests::test_load_returns_to_baseline_streaming_with_retries

test result: FAILED. 17 passed; 4 failed; 0 ignored; 0 measured; 471 filtered out; finished in 1.47s

With fix:

running 22 tests
test core::worker::tests::test_load_counter_operations ... ok
test core::worker::tests::test_load_guard_multiple_workers ... ok
test core::worker::tests::test_load_guard_panic_safety ... ok
test core::worker::tests::test_concurrent_load_decrements ... ok
test core::worker::tests::test_concurrent_load_increments ... ok
test core::worker::tests::test_load_guard_single_worker ... ok
test core::worker::tests::test_total_load_calculation ... ok
test policies::cache_aware::tests::test_cache_aware_with_imbalanced_load ... ok
test policies::cache_aware::tests::test_cache_aware_with_balanced_load ... ok
test policies::power_of_two::tests::test_power_of_two_with_cached_loads ... ok
test core::worker::tests::test_load_counter_performance ... ok
test routers::http::pd_router::tests::test_load_monitor_updates ... ok
test routers::http::router::tests::test_load_returns_to_baseline_on_dp_parse_error ... ok
test routers::http::router::tests::test_load_returns_to_baseline_on_send_failure ... ok
test routers::http::router::tests::test_load_returns_to_baseline_streaming_success ... ok
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_non_retryable ... ok
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_success ... ok
test routers::http::router::tests::test_load_returns_to_baseline_non_streaming_retries_exhausted ... ok
test routers::http::router::tests::test_load_counter_not_double_decremented_on_retryable_status ... ok
test routers::http::router::tests::test_load_returns_to_baseline_streaming_with_retries ... ok
test routers::http::router::tests::test_streaming_retryable_does_not_leak_load ... ok
test tokenizer::factory::tests::test_download_tokenizer_from_hf ... ok

test result: ok. 22 passed; 0 failed; 0 ignored; 0 measured; 471 filtered out; finished in 1.20s

Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results

Signed-off-by: simondanielsson <simon.danielsson99@hotmail.com>
@simondanielsson simondanielsson changed the title fix: remove redundant decrement on failure [Bugfix] Remove redundant request decrements on failure Aug 14, 2026
@simondanielsson
simondanielsson marked this pull request as ready for review August 14, 2026 10:46
@simondanielsson

Copy link
Copy Markdown
Contributor Author

@BugenZhao Happy to get a review when you have time 🙏

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e68b6c46f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/routers/http/router.rs
@simondanielsson simondanielsson changed the title [Bugfix] Remove redundant request decrements on failure [Bugfix] Miscalculated load with cache aware routing Aug 14, 2026
Signed-off-by: simondanielsson <simon.danielsson99@hotmail.com>
@simondanielsson

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 794d18c571

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/routers/http/router.rs
Signed-off-by: simondanielsson <simon.danielsson99@hotmail.com>
@simondanielsson

Copy link
Copy Markdown
Contributor Author

@codex review

@simondanielsson

Copy link
Copy Markdown
Contributor Author

Failing Trivy CI fixed in : #212

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 7d1227ce58

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant