refactor(worker): canonical runtime API on WorkerRuntime (Finding 4 Part B) - #1128
Conversation
Part B of the Worker trait trim outlined in .claude/plans/2026-04-14-worker-module-followup-cleanup.md (Finding 4, categories B + C). Follows the same Option 2 principle we landed on in Part A (PR #1127): canonical implementation on the underlying struct, trait surface preserved, callers unchanged, only delete truly dead methods. What changed - model_gateway/src/worker/worker.rs * Delete dead `Worker::reset_load` (grep confirmed 0 callers; trait had a default no-op body, BasicWorker had the only override). * Promote `WorkerRuntime::{status,set_status,revision,bump_revision}` from private to `pub fn` so external callers reach the runtime through a single accessor instead of being funneled through the trait each time. * Add canonical `pub fn` counter methods on `impl WorkerRuntime` as the single source of truth for runtime state: consecutive failures, consecutive successes, total pending probes, load counter, routing- key load, and processed-request counter. * Introduce `WorkerRuntime::try_decrement_load` which performs the saturating `fetch_update` and returns `bool`; the caller warns on underflow. Keeps the BasicWorker-specific side effects (tracing warn + metric update) at the forwarding layer. * Rewrite the 15 counter methods on `impl Worker for BasicWorker` so each one is a one-line forward to the corresponding `WorkerRuntime` method. `increment_load` / `decrement_load` still call `update_running_requests_metrics()` after the delegation so the metric export path is unchanged. `set_status` still updates `Metrics::set_worker_health` after the delegation. Why - Finding 4 Part A trimmed the metadata delegates. Part B does the equivalent consolidation for runtime state. Before this PR, every counter mutation went through a ~6-line block that reached into the WorkerRuntime's internal atomic fields; those fields had to be `pub` for the trait impl to touch them, and each piece of atomic bookkeeping (ordering + `+1` for post-increment) was duplicated at every call site in BasicWorker. - Collapsing the implementation onto WorkerRuntime lets us keep precisely one copy of each atomic ordering choice, and lets future `Worker` implementations (e.g. the GrpcWorker in bindings/golang that currently carries its own raw atomics) adopt the canonical runtime wholesale without duplicating the memory-ordering rules. - `reset_load` had no callers anywhere in the tree — keeping it in the trait just added noise. Deleting dead methods keeps the trait definition honest. How - Option 2 principle: trait methods stay (so callers still write `worker.load()`, `worker.increment_processed()`, etc. — no churn across routers/ and workflow/). The impl on BasicWorker becomes one-line forwards via `self.runtime.load().method()`. The canonical code lives in exactly one place: `impl WorkerRuntime`. - Category C (circuit breaker) is already Option 2-compliant — the six BasicWorker wrappers (`circuit_breaker_state`, `circuit_breaker_can_execute`, `record_circuit_breaker_outcome`, plus the `is_available` / `record_outcome` default impls and the `resilience` accessor) already forward to `CircuitBreaker::state()`, `.can_execute()`, `.record_outcome()` which own the canonical implementation in worker/circuit_breaker.rs. No changes needed. Verification - `cargo check -p smg` — clean. - `cargo clippy -p smg --all-targets -- -D warnings` — clean. - `cargo clippy -p smg-golang --all-targets -- -D warnings` — clean (GrpcWorker in bindings/golang still compiles against the trimmed trait surface). - `cargo test -p smg --lib` — 538 passed; 0 failed; 4 ignored. Refs: worker module deep refactor follow-up cleanup (Finding 4 Part B) Signed-off-by: Simo Lin <linsimo.mark@gmail.com>
There was a problem hiding this comment.
Clean refactoring — reviewed all changes. Atomic orderings preserved, return values match, side effects (metrics updates) stay at the BasicWorker forwarding layer, reset_load removal is safe (zero callers confirmed). Fields are now properly encapsulated behind WorkerRuntime's public API. LGTM.
📝 WalkthroughWalkthroughThe PR refactors Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Warning Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@model_gateway/src/worker/worker.rs`:
- Around line 594-596: The new public mutator set_status(WorkerStatus) (and the
other public mutators around 642-654) introduce a second write path that
bypasses BasicWorker's forwarding layer and skips Metrics::set_worker_health,
update_running_requests_metrics(), and decrement_load() underflow checks; change
these methods to non-public (remove pub) or otherwise restrict visibility so
callers must go through BasicWorker's runtime forwarding API, and if internal
updates are needed ensure they invoke the same helpers
(Metrics::set_worker_health, update_running_requests_metrics, decrement_load)
used by the forwarding layer so metrics and runtime state remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 78c85359-fae4-4fdb-b496-db3f08aa0fd6
📒 Files selected for processing (1)
model_gateway/src/worker/worker.rs
Summary
Part B of the Worker trait trim outlined in
.claude/plans/2026-04-14-worker-module-followup-cleanup.md(Finding 4, categories B + C). Follows the same Option 2 principle that landed in Part A (#1127): canonical implementation on the underlying struct, trait surface preserved so callers don't churn, only delete truly dead methods.Part A consolidated the metadata delegates onto
WorkerMetadata. Part B does the equivalent consolidation for runtime state, so the atomic bookkeeping (orderings, post-increment adjustments, saturating decrements) lives in exactly one place:impl WorkerRuntime.What changed
All edits live in
model_gateway/src/worker/worker.rs:Worker::reset_load.grep -rn reset_loadconfirmed 0 callers anywhere in the tree (trait default was a no-op{}, and the only override was inBasicWorker). Dead code removed, not deprecated.WorkerRuntime's lifecycle methods topub.status,set_status,revision,bump_revisionwere all private onimpl WorkerRuntime— promoted topub fnso callers can reach them through a single accessor without bouncing through theWorkertrait.impl WorkerRuntimeas the single source of truth for runtime state:consecutive_failures_increment/_reset,consecutive_successes_increment/_reset,total_pending_probes/_increment/_resetload,increment_load, newtry_decrement_load() -> bool(returnsfalseon underflow so callers can warn)routing_key_load,increment_routing_key_load,decrement_routing_key_loadprocessed_requests,increment_processedimpl Worker for BasicWorkerso each is a one-line forward of the formself.runtime.load().method(). BasicWorker-specific side effects (update_running_requests_metrics()after every load mutation,Metrics::set_worker_healthafterset_status) stay at the forwarding layer — only the atomic bookkeeping moves toWorkerRuntime.Not touched (already Option 2-compliant):
circuit_breaker_state,circuit_breaker_can_execute,record_circuit_breaker_outcome, plus theis_available/record_outcomedefault impls and theresilienceaccessor) already forward toCircuitBreaker::state(),.can_execute(),.record_outcome()which own the canonical implementation inworker/circuit_breaker.rs. No changes needed.GrpcWorkerWorker impl inbindings/golang/src/policy.rs— this one carries its own raw atomics with comments saying the counters are intentionally no-ops ("FFI workers don't run the state machine — these counters are unused"). Consolidating it ontoArc<WorkerRuntime>is a bigger cross-crate change and belongs to a separate PR. This PR leaves it alone and verifies it still compiles against the trimmed trait.Why
Before this PR, every counter mutation on
BasicWorkerwent through a ~6-line block that reached intoWorkerRuntime's internal atomic fields directly (e.g.self.runtime.load().consecutive_failures.fetch_add(1, Ordering::AcqRel) + 1). Consequences:pubfor the trait impl to touch them, which leaked implementation details.AcqRelfor health counters,Relaxedfor load/processed,Release/Acquirefor status) was duplicated at every call site. If we ever need to reason about memory ordering or benchmark alternatives, we'd have to hunt 15+ call sites.+ 1and the saturatingfetch_updateidiom lived inside the trait impl.Collapsing the implementation onto
WorkerRuntimekeeps precisely one copy of each atomic ordering choice, and lets futureWorkerimplementations (e.g. the FFIGrpcWorker) adopt the canonical runtime wholesale without duplicating the memory-ordering rules.reset_loadhad no callers anywhere — keeping it in the trait just added noise. Deleting dead methods keeps the trait definition honest.How — Option 2 principle
Trait methods stay (so callers still write
worker.load(),worker.increment_processed(), etc. — zero churn acrossrouters/andworkflow/). The impl onBasicWorkerbecomes one-line forwards. The canonical code lives in exactly one place:impl WorkerRuntime.Before / after on a representative method (
consecutive_failures_increment):decrement_loadis the one method with non-trivial BasicWorker-side behavior (it logs when the counter is already zero). The new split is:Test plan
cargo check -p smg— clean.cargo clippy -p smg --all-targets -- -D warnings— clean.cargo clippy -p smg-golang --all-targets -- -D warnings— clean (GrpcWorkerinbindings/golangstill compiles against the trimmed trait).cargo check -p smg-python— clean.cargo test -p smg --lib— 538 passed; 0 failed; 4 ignored (no test changes were required — the existing worker counter tests inworker::worker::testsexercise the new forwarding code path transparently through the same trait methods).Refs:
.claude/plans/2026-04-14-worker-module-followup-cleanup.md(Finding 4 Part B)Summary by CodeRabbit