Skip to content

dist: add load-aware worker capacity - #2899

Open
codeandsolder wants to merge 1 commit into
mozilla:mainfrom
codeandsolder:upstream-pr/load-aware-worker-capacity
Open

codeandsolder wants to merge 1 commit into
mozilla:mainfrom
codeandsolder:upstream-pr/load-aware-worker-capacity

Conversation

@codeandsolder

@codeandsolder codeandsolder commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Allow an otherwise idle distributed build server to temporarily advertise zero capacity when the host is busy with non-sccache work.

On Linux, setting SCCACHE_DIST_MAX_HOST_CPU_PERCENT enables aggregate CPU sampling from /proc/stat. When measured busy time exceeds that threshold and the worker is not executing distributed work, its heartbeat advertises zero CPUs.

Existing distributed jobs are never interrupted.

Capacity is restored only after CPU usage remains at or below the configured threshold continuously for SCCACHE_DIST_REENABLE_AFTER_SECONDS (default 300).

The scheduler accepts zero-capacity registrations and heartbeats, updates an existing server's advertised CPU count, and naturally excludes zero-capacity workers from new allocations.

Scheduler load weighting represents workers that cannot accept another job with f64::INFINITY, both for zero advertised capacity and for ordinary admission-limit exhaustion. Using a true ineligible sentinel rather than a finite synthetic load keeps scheduler correctness independent of the oversubscription policy: increasing the allowed jobs-per-core ratio in the future cannot make an unavailable worker rank ahead of an eligible higher-load worker.

With the option unset, worker behavior is unchanged.

Host-load accounting

The load-aware path is intended to yield to external host work, not to sccache's own CPU use.

To preserve that distinction:

  • startup seeds /proc/stat, then measures an initial 100 ms host-load window before HTTP/TLS server and heartbeat startup;
  • initial advertised capacity is derived from that pre-registration sample, avoiding both an unmeasured first heartbeat and treating sccache startup CPU as external load;
  • distributed compile execution and toolchain submission mark their sampling interval as sccache-owned activity;
  • live-job and activity state bracket each /proc/stat sample, covering jobs that start or finish around a heartbeat;
  • CPU intervals containing sccache-owned work do not trigger withdrawal and do not count toward recovery;
  • if CPU sampling fails, the low-load recovery window is reset;
  • if the failed interval contained sccache-owned work, that contamination marker is preserved because the next successful /proc/stat delta may span the unknown interval.

A threshold of 0 is well-defined: withdrawal requires busy > threshold, while recovery accepts busy <= threshold.

Capacity withdrawal and restoration are logged at info! level for operational visibility.

Testing

  • cargo +1.99.0 fmt --all -- --check
  • Rust 1.99 load-aware regressions covering configuration, threshold boundaries, sustained recovery, missing samples, sccache-owned CPU intervals, sampling races/activity tracking, and the server heartbeat/run-job path
  • scheduler ineligibility regression:
    • cargo +1.99.0 test --locked --no-default-features --features dist-server --bin sccache-dist scheduler_tests::ineligible_load_weights_are_infinite -- --exact
  • zero-capacity scheduler regression:
    • cargo +1.99.0 test --locked --no-default-features --features dist-server --bin sccache-dist scheduler_tests::zero_cpu_heartbeat_removes_server_capacity -- --exact
  • Mozilla CI exercises the full project matrix, including the Rust 1.91 Clippy configuration

AI Disclosure: AI developed, human directed and reviewed

@codecov-commenter

codecov-commenter commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.03025% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.18%. Comparing base (b937482) to head (d44ee99).

Files with missing lines Patch % Lines
src/dist/http.rs 97.11% 13 Missing ⚠️
src/bin/sccache-dist/main.rs 89.87% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2899      +/-   ##
==========================================
+ Coverage   78.54%   79.18%   +0.64%     
==========================================
  Files          74       74              
  Lines       44039    44553     +514     
==========================================
+ Hits        34592    35281     +689     
+ Misses       9447     9272     -175     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codeandsolder-git-wrangler
codeandsolder-git-wrangler Bot force-pushed the upstream-pr/load-aware-worker-capacity branch 5 times, most recently from 0cdea01 to 53c6507 Compare October 17, 2026 01:04
@codeandsolder-git-wrangler
codeandsolder-git-wrangler Bot force-pushed the upstream-pr/load-aware-worker-capacity branch from 53c6507 to 4768c93 Compare October 18, 2026 01:16
@codeandsolder-git-wrangler
codeandsolder-git-wrangler Bot force-pushed the upstream-pr/load-aware-worker-capacity branch from 4768c93 to d44ee99 Compare October 30, 2026 23:53
@sylvestre

Copy link
Copy Markdown
Collaborator

The idea is fine, but there's a real bug: the CPU sample covers the previous 30s heartbeat interval, so a worker that just finished a sccache job sees its own compile load and disables itself for 5 minutes. Every other server option lives in server.toml (src/config.rs server::Config); this one is two env vars read inside Server::start. The tests are dropped in the middle of production code in http.rs, and the end-to-end test never checks the advertised CPU count

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.

3 participants