Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
rohnnyjoy
force-pushed
the
active-count-platform-attributes
branch
from
October 9, 2026 18:44
8c20d91 to
6292c91
Compare
`execution.active.count` is the gauge an autoscaler reads for queue depth, and it carried only `execution.stage`. One scheduler serving two pools that differ by a platform property (a Linux pool and a macOS pool on `OSFamily`, say) therefore produced one queue depth for both, and a backlog of macOS actions scaled the Linux pool. Add `active_action_count_platform_properties` to `SimpleSpec`: a list of platform property keys, each of which becomes an attribute `execution.platform.<key>` on `execution.active.count` with the action's value for that key, or `""` when the action does not set it. The operator chooses the keys, so cardinality is theirs to bound; every series carries every listed key, so summing over them still gives the per-stage totals. Empty, the default, is today's behaviour exactly. The attribute set for an action is built once from the configuration (`ActiveCountAttributes` in nativelink-util; the scheduler factory refuses two keys the collector's Prometheus exporter would fold into one label, since it rewrites every character outside `[A-Za-z0-9_]` to `_`), and every add and subtract on the gauge goes through it: the queue insert, the stage transition, and the client-gone removal in the memory DB, and the periodic recount in the store DB. The memory DB moves an action between series whenever its attributes change, not only its stage, since a requeue may rewrite its platform properties (a memory escalation does). The store DB, when keys are configured, reads each stage's actions and counts them by value instead of asking the store for a total, reports each series as the change since its last pass, and records a series that emptied down to zero. A series exists only once an action has carried its values, so an idle pool reads absent rather than 0 until its first action; the field doc and the autoscaling pages say so and guard every example recording rule with `or vector(0)`. The store DB warns at construction when keys are set but the metric is off, since the keys then do nothing. Tests cover both backends: per-value counts through queued, executing and completed, an action without the key under `""`, the attribute following an action across stages, and every series back at zero once the actions are gone. The autoscaling docs now show the per-pool recording rule this makes possible, and the configuration reference is regenerated for the new field.
rohnnyjoy
force-pushed
the
active-count-platform-attributes
branch
from
October 9, 2026 19:33
6292c91 to
e41fb2f
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
execution.active.countcarried onlyexecution.stage, so one schedulerserving two pools that differ by a platform property (a Linux and a macOS pool
on
OSFamily) produced one queue depth for both, and a backlog in one poolscaled the other. This adds
active_action_count_platform_propertiestoSimpleSpec: each listed key becomes an attributeexecution.platform.<key>on the gauge with the action's value (
""when unset), so each pool's queuedepth is its own series. Default empty keeps today's output exactly.
Fixes #2917.
Every add and subtract on the gauge goes through one attribute set built from
the configuration (
ActiveCountAttributesin nativelink-util; the factoryrefuses two keys that the collector's Prometheus exporter would fold into one
label, as it rewrites every character outside
[A-Za-z0-9_]to_): the queueinsert, the stage transition and the client-gone removal in the memory DB, and
the periodic recount in the store DB. The memory DB moves an action between
series whenever its attributes change, not only its stage, because a requeue
can rewrite platform properties (memory escalation does), and a +1 and -1 with
different attributes would leave the gauge drifted for good. The store DB,
when keys are configured, reads each stage's actions and counts by value
instead of asking the store for a total, and records a series that emptied
down to zero. A series exists only once an action has carried its values, so an
idle pool reads absent rather than 0 until its first action: the field doc and
both autoscaling pages say so and guard every example recording rule with
or vector(0). The store DB warns at construction when keys are set whileenable_active_action_count_metricis off, since the keys then do nothing.Docs: the autoscaling pages now show the per-pool recording rules and the
config to enable them, keep
instance_namein the fast rule and the per-instancemaxguidance, and state the Prometheus label rewriting. The configurationreference (
reference/nativelink-config/main.mdx) is regenerated so the snippetlint knows the field; it was generated on this branch at v1.7.5, so its header
sha is that one, and the usual regeneration on main supersedes it.
How was this verified?
Unit tests, in
nativelink-scheduler/tests/execution_active_count_test.rs:platform_property_attributes_follow_each_action(memory DB): with["OSFamily"], a linux, a macos and a key-less action are counted asqueued{OSFamily=linux|macos|""}, each moves toexecutingand thencompletedunder its own value while the others stay put, no stage-onlyseries appears, and once the clients expire every series is back at zero.
store_backend_counts_each_platform_property_value(store DB, pausedtime): a fake store indexed by stage holds two linux queued, one macos
queued, one macos executing, one bare executing; after the first recount the
per-value series match, there is no stage-only series, after a rewrite the
deltas move the right series, and after the store empties every series reads
0.
dropping_executing_action_decrements_active_countis kept andreads the same recorder.
nativelink-util/tests/metrics_test.rs: the attribute set is the stage theneach key in order with
""for a key the action lacks, andnewrefuses["OSFamily","OSFamily"],["container-image","container.image"]and["gpu_type","gpu-type"]withInvalidArgumentnaming both keys, while["container-image","container_tag"]is accepted.Without the change the per-value series never exist (the lookups read 0) and
the memory DB does not accept keys at all, so both new tests fail to build or
fail on their first assertion.
Ran, on
rust 1.97.1(the workspace'srust-version):Not verified: against a live Redis deployment, or an HPA end to end. The store
path is covered by the fake store only.
Risk
Low for anyone who does not set the new field: the default is the empty list
and the emitted series are byte-identical to before. The memory DB now
compares the full attribute vector rather than the stage discriminant to decide
whether to move the count; with no keys that is the same decision.
With keys set: (1) the store backend's recount reads every action in every
stage each pass instead of asking for four totals, so on a large Redis-backed
queue that is a heavier query every 15 s per replica, which the doc comment
says; (2) cardinality is the operator's: each distinct value combination is a
series kept at 0 for the life of the process; (3) a pool with no action yet has
no series at all, so an unguarded per-pool rule reads absent, which the docs
and the field doc call out.
StoreAwaitedActionDb::newandmemory_awaited_action_db_factorygain a parameter, which touches everyscheduler test's construction site (the mechanical
ActiveCountAttributes::default()in the diff). A colliding key list is now astartup error rather than a silent overwrite.
AI assistance
An agent drafted the change and the tests from a written design; I reviewed
every line and ran the verification above.