Repository navigation
Queue every compile at the fleet leader against per-machine slot and memory budgets - #1737
Merged
Merged
Conversation
) CompileRun gains peakMemoryBytes, disengaged when nothing was measured. POSIX reaps with wait4 and reads ru_maxrss (scale in MaxRssUnitBytes); Windows spawns suspended into a job object and reads PeakProcessMemoryUsed. A failed job call only costs the measurement. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
The invocation log gains queued-ms and peak-memory-bytes columns (format v3, so v2 lines are counted as unreadable), and `pace-cc -s` prints the total slot wait and the largest peak compile memory when any record carries them. Absent readings stay absent. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…#1728) docs/tools/pace-cc.md named the v2 layout; it now describes v3 with the queued-ms and peak-memory-bytes columns. Tally::peakMemoryMax becomes an optional (absent is not zero), which also removes a bool between uint64_t members, and the exit-code and unsigned readers fold into one ParseOptionalInteger template. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ape (#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
Formatting only: constructor initializer-list line wraps in PeakEstimates.cpp. No token changes. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ill (#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…n on (#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…r reset (#1728) Settle and Abandon replace Release; LoseLeadership returns every parked ticket. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
… dedup owners (#1728) A refused ticket re-parked at the front broke Requeue's sorted-by-ticket assumption; entries now carry an order key. Dedup owners are looked up by cacheKey. Adds tests for the refused-machine local caller and for woken, undelivered Stored waiters. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
ADMIT (0x22), SETTLE (0x23) and MACHINE-BUDGET (0x24) with their owning
decoded forms, EstimateKeyFields, and byte pins for the three admission
enums. Wire version and floor move to 16 together because ADMIT may be
answered with Status::Progress; ProgressIsCompileOnly becomes
ProgressVerbsAre({Compile, Admit}). MACHINE-BUDGET is ProvenNodeOnly.
The daemon and a node running no consensus refuse all three as capacity
verbs (DispatchNotPermitted); a scheduler answers the same until the
leader serves them.
Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…w follow-ups (#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ort (#1728) Every node states its machine's compile budget to the fleet's queue on the presence loop's round, whether or not it runs a worker (R6): - --machine-slots, --machine-memory-budget (size or N% of RAM) and --compile-estimate-default, restart-only and not worker rows, so a --slots=0 node accepts them. MachineBudgetOf derives 75% of RAM less the cache tier's reserve, floored at the default estimate. - IHeldCharges / HeldChargeRegistry (RAII handles) for the leases a node grants, summed with CompileCapacity's running compiles. - Op::Compile carries trailing admission fields (chargedBytes, cacheKey, sourcePath); the worker holds the charge with the slot and reports the measured peak under {cacheKey, sourcePath, fingerprint}. - SendMachineBudgetOnce dials through DialAndAnnounce; a refusal is said on its transition through SchedulerReachability and the peaks are offered again. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…iew follow-ups (#1728) - WorkerProtocol takes its IJobMeasurementSink as a required argument; every other site passes IgnoreJobMeasurements() explicitly. - A worker-tier case runs an admitted compile through the real wiring and asserts the measured peak lands in the ledger. - The budget dial is filed under its own reachability subject, and is skipped in a round whose presence announce reached no scheduler. - ParseMachineSlots is ParseSlots plus a zero refusal; Doxygen on the registry handle; the wire history notes COMPILE's 10 -> 13 fields. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…hed nobody (#1728) Round 1 gated the budget on the presence being recorded, which starved a node with an empty advertised endpoint (R6) and let a refused presence suppress it. The loop now skips only when the presence dial ran and could not reach a scheduler; an empty endpoint and a refusal still send the budget. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ne budgets (#1728) AdmissionResponder on the scheduling leader serves ADMIT, SETTLE and MACHINE-BUDGET. An ADMIT parks on the node's reactor (interruptibleSleepUntil, so a reactor torn down mid-wait frees the frame), the endpoint pulses Status::Progress through its per-verb pulse, and the decision wakes it through a stop token. A grant keeps the connection open and holds its charge on a new per-connection ConnectionScope: a SETTLE gives it back, a close or any other frame abandons it, and the hold releases in a destructor. The endpoint's read watch tells the scope when the peer leaves, and retires itself with CancelRead (not Close) after a reply that left a hold. SchedulerService owns the AdmissionQueue, PeakEstimates and a placement source over WorkerRegistry; each grant re-asks the gate with a fresh caller context (R9) and a worker grant mints a real signed lease. Demotion calls LoseLeadership and answers every waiter NotLeader naming the new leader. A loopback local lease is recorded in HeldChargeRegistry for its lifetime. Eight admission counters (new SchedulerAdmission surface) and two gauges, rendered by /metrics and carried by the live-stats snapshot (grammar 7). Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…r returns it to the front (#1728) SETTLE{WorkerRefused} no longer abandons the ticket the queue just put back: AdmissionQueue::Settle re-parks the SAME ticket at the front with the refusal counted (R7), and SettleAdmission now only gives the worker's lease and job back, keeping the ticket record so the connection's next ADMIT waits on it. A worker that stops being live between the pump placing on it and the grant minting its lease is no longer answered NoWorker: GrantFor reports it, and AdmissionQueue::ReturnToFront puts the ticket back at the front without a refusal, so the request keeps its whole bound and the machine is not marked refused. Service-level cases on a ManualClock cover the worker grant (signed token, lease, job), settle and abandon releasing both, the refusal keeping the ticket, AlreadyInFlight, a keyless worker never placed on, and the vanished worker; WorkerRegistry::Candidates and Find get cases of their own. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…dged by its pulse (#1728) After SETTLE{WorkerRefused} the connection keeps the put-back ticket as a RequeuedHold with its waiter registered, so a pump that decides it first has somewhere to leave the decision; the next ADMIT on that connection binds to it without a new Enqueue, and any other frame or the close abandons it (Ruling 9). The AdmissionWorkerRefused help and docs say so. A parked ADMIT has no total ceiling any more: it parks in steps, and its answer window (AdmissionIdleBound, 30 s at the default cadence) bounds the silence between pulses, renewed by the endpoint each time a pulse reaches the peer the responder asked to be judged that way (ConnectionScope::JudgeByPulse). One departure is one AdmissionAbandoned: the endpoint marks the scope when it files an undelivered grant, and the hold that departure releases does not count it again. The idle window before the next request is the hold's own (FrameServer::IdleWindowFor), and a retired peer watch is silent only for the Cancelled its retirement produced (FrameServer::ClassifyWatch), so a reset resolved before the retirement still tells the scope. The demotion NotLeader carries its own uncounted rationale, and MetricsSnapshot::admission says the live-stats frame carries it. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…1728) A request parked forever is pulsed forever, so a per-read deadline never fires: a build that lost the requeued ticket hung the case for twenty minutes instead of failing it. ReadAnswer now gives up after ten seconds in all. clang rejects the NetError designated initializer that omits its context. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
… case that could not fail (#1728) test-loops refused the pump's for (freed) loop and the park's for (;;), both now a while. The over-the-wire departure case is removed: under a neuter of the one-count fix it stayed green in 40 of 40 runs (the endpoint always wrote the grant before it saw the departure), so it asserted nothing the scope-level case does not, and a green test on the wrong path retires the suspicion. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
… no private membership fake (#1728) The re-gate case used FlippableMembership, a private IMembershipOracle the membership-fakes check refuses. The fixture now admits through Testing::RosterFold (loopback, then the key roster) with each machine's key presented under its own id, and the re-gate case parks an ADMIT from a REMOTE caller that proved its key; the forget lands as that key revoked in the roster while it waits, and the grant is refused NotAMember holding nothing. With the grant-time Gate neutered the case goes red (a grant is written instead). Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…der, never re-parked (#1728) A grant outlives a demotion, and SETTLE{WorkerRefused} arriving after it put the ticket back in a follower's queue, which nothing pumps: the re-ADMIT then parked for ever behind pulses that kept the connection alive. SettleAdmission now decides under the admission lock, against a flag the demotion writes in the same locked step it forgets the queue in, and abandons the ticket there (SettleResult::NotLeading); the responder drops the put-back hold and answers NotLeader naming the leader. The requeued re-ADMIT refuses the same way on a node that no longer leads, as defence in depth, and says why its payload is not read (Ruling 9). The pulse-renewal docs say a pulse the kernel ACCEPTED, not one that reached the peer: a vanished host is bounded by TCP retransmission, not by the window. The NoteDepartureCounted call site names its test gap. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
A node that runs consensus but no scheduler now relays the queue's ADMIT and SETTLE from a launcher on its own machine to the leader, instead of answering NotLeader. AdmitForwarder is an IFrameResponder that keeps ONE proven, sealed upstream connection per client connection, on the client's ConnectionScope for as long as the leader holds something for it (a grant until its settle, a ticket a worker refused until the ADMIT that asks again), so any sequence of requests and every final reply travel in order and a client close closes upstream. The endpoint keeps writing this node's own pulse to the client; the leader's pulses bound the upstream silence, so a dead leader behind a healthy pulse is noticed. An unreachable, unproven, lost or silent leader, or one that moves more than twice, is answered EndpointBusy (AdmissionForwardFailed) and the launcher fails open; a NotLeader naming a new leader is followed transparently before the grant. A forwarded Local grant is recorded in HeldChargeRegistry. Only callers on this machine are forwarded; every other verb keeps the redirect. SchedulingRedirectResponder delegates the queue verbs through ForwardAdmissionWith; ProvenSchedulerDialer dials from the loop and proves this node's identity key. The idle-window timer moves from ReactorExchange to a shared header, and the leader's test fixture to AdmissionTestUtils.hpp so both suites stand up the same fake leader. Counters AdmissionForwarded, AdmissionForwardFailed and AdmissionForwardAbandoned on a new AdmissionForwarding surface, served where the node redirects. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
Breaking changes in 0.7.0 - 0.10.0 and what pace did about each: - 0.8.0, Windows paths handed to core-cpp are read as UTF-8: pace passes no path to the affected entry points (parseLogFileSpec, homeResolvedPath, absolutePath, listDirectory, loadImage, ScopedTempDir); nothing to do. - 0.8.0, homeResolvedPath/absolutePath/parseLogFileSpec return std::expected: pace does not call them; nothing to do. - 0.9.0, core::log::Sink::console() writes to stderr: pace has its own Logger and uses no core::log; no stdout log parsing exists; nothing to do. The build is clean on cl-debug without source changes. 0.7.0 and 0.10.0 list no Breaking entries. No other pin of core-cpp's version exists outside CMakeLists.txt. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ide (#1728) The seam check read only selection blocks spelling std::ranges::<name>, so core-cpp 0.10.0's Stride (std::views::stride) was not a row. The scope (ranges or views) is now part of the row, read from the header, so views::stride, std::views::stride and std::ranges::views::stride are refused with core::ranges::Stride offered, while std::views::iota stays legal. The selftest gains a views row in the staged header, three planted violations and a clean case. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…de (#1728) std::views::stride is missing from every libc++, so the helper could not build on a macOS leg. The target links core::async, which is header-only and carries core-cpp's include path, so no library is added to the link. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…itself (#1728) Ruling 10: every node that does not lead -- a learner, or a voter following another -- relays a launcher's ADMIT and SETTLE to the leader instead of answering NotLeader. The forwarder is built under ForwardsAdmission (every consensus member) and wired into the scheduler surface too: an ADMIT is relayed unless this node's queue decides it (it leads, or the connection proved this machine's own key), and a connection holding a forwarded link stays with the forwarder whoever leads by the time it settles. A leader or redirect naming this node's own announced endpoint is refused NotLeader naming nobody, never dialled. Review fixes: the upstream silence bound is the wire's own DefaultCompileIdleTimeout (ForwardPolicy::upstreamIdle), not derived from this node's pulse; an undecodable grant is refused ForwardFailed and its link closed; the AdmissionForwarding surface is served under the predicate the relay is built under, with the prover asserted rather than guarded; a dial in progress is documented as not cancelled. Tests: the production ProvenSchedulerDialer against a leader on the RosterFold behind RelabelledPeerListener (charge keyed to the forwarding machine, a stranger refused, relabelling control), voter-follower relay and settle across a leadership change, no-leader refusal, no self-forwarding (endpoint, redirect, own proven key), a slower-pulsing leader kept, an undecodable grant, the largest grant's margin. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
A held admission connection is the lease: its charge is given back when it
closes, and a connection to a host that vanished never closes on its own
(Ruling 18). The launcher's admit dial (`Cc::DefaultAdmitBudget`) and the
node's relay to the leader (`Node::ForwardDialBudget::keepAlive`) now ask
for keepalive, per dial as the rulebook requires.
The intervals are core-cpp's `KeepAliveSettings {}`, which every
`KeepAlive::Yes` dial applies -- a dial takes none of its own -- pinned by a
`static_assert` beside the budget (`Cc::AdmitKeepAliveProbe`), because the
vanished-peer bound (16 s on Linux and macOS, 30 s on Windows) is derived
from them. The leader's side of the held connection is an ACCEPTED socket,
which core-cpp gives no way to arm; that half needs a core-cpp API.
Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…E_SCHEDULER names (#1728) Ruling 16. The leader charges a compile to the machine that ASKED, and can name that machine only by a key the connection proved or by loopback: this machine's node proves its key when it relays, while a launcher dialling a remote scheduler directly proves nothing and would be charged to no machine. So `PACE_SCHEDULER` no longer redirects ADMIT; it keeps its one meaning, where a LEASE goes when no queue answers. Both decisions are pure functions in MissFlow, so they are cases rather than comments: `Cc::AdmissionEndpointOf` and `Cc::PlanUnreachableAt` -- a plan is refused for a remembered-unreachable endpoint only when every place it could be spent was unreached, and the LEASE fallback is not dialled at a scheduler a launcher a moment ago could not reach, exactly as before the queue. The responder's comment no longer claims a ticket names the charged machine, and the pace-cc page says where ADMIT goes and what a stopped node costs a passthrough that made no FETCH: one connect per fifteen-second memo window. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…budget is a gauge (#1728) The wait sum `pace_admission_wait_milliseconds_total` was paired with a count spelled as the sum of three grant counters, and each grant added the wait since the request was FIRST enqueued -- so a request a worker refused added its first wait again on every regrant. It is now a `_sum`/`_count` pair: `AdmissionWaitCount` (`pace_admission_wait_count_total`, catalogued, attributed to the scheduler's admission surface, documented) moves once per grant, and the sum adds the time since the request was last PARKED (`AdmissionVerdict::waited`). The grant still tells the client the whole wait (`queued`), which is what the launcher sums. And spec goal 9 asked for budget beside charge per machine: `/metrics` now renders `pace_admission_budget_bytes{machine}` and `pace_admission_in_use_bytes{machine}` for every machine that reported a budget, from one table, carried by the live-stats codec too (grammar 8; the layout pin moves with the new counter). Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…1728) `AdmitSession` printed "settling the admission failed" to stderr itself, so after a leader failover every compile of a build said it -- a line per compile, unasked, while the leader reads the connection's end as a release anyway. The session now takes a required `Cc::AdmitNote` sink, and the launcher hands it one gated on `PACE_VERBOSE`. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…ing as open (#1728) `FrameServer::Shutdown()` returns once nothing is open, and `main` destroys the node's `HeldChargeRegistry` right after the endpoint. The connection task deregistered itself first and ended its scope's hold second, so a held admission's charge could be given back to a registry that was already gone. The hold is now ended before the connection is untracked. Not covered by a case that discriminates the order: a slow-releasing hold was tried, and with the order reversed it still passed, because the drain also waits for the accept and sweep loops, which end on the same reactor thread behind the release. The hazard needs both loops to have ended before the connection's own teardown, which no fixture here can arrange. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
#1728) The leader's launchers reach its queue over loopback and are charged to `SchedulerService::OwnMachineId`, and the queue admits nothing on a machine that has not reported. A case now runs the production budget round, dialler and proof from the leader to itself and shows the budget filed under that same machine, and a loopback `LocalOnly` request granted against it. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
… rules (#1728) The compile-queue section states what the final review fixed: the grant carries the lease grant's dial hint; an admission's worker lease is filed under its ticket (Ruling 17); a launcher asks the queue at its own node (Ruling 16); where the queue answered, a launcher that gives up runs the compile charged here; what a stopped node costs a passthrough with no FETCH; and the keepalive bound on held admission dials (Ruling 18), with the half it does not cover yet -- the leader's accepted socket, which core-cpp cannot arm. Two overlong lines are re-wrapped to the file's width. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
- `WorkerRegistry::JobStarted` has its doc block back; `Candidates`/`Find` had been inserted inside it. - `ProcessRunner.cpp`: `JobHandle` sits above the attribute-list class's doc block rather than between it and its class, and `PeakProcessMemory` says what it reads -- the peak commit charge of one process of the job. - `MetricsSurface::AdmissionForwarding` is served on every node that answers the scheduling verbs, voters as well as learners (Ruling 10). - pace-node `main.cpp`: a duplicated comment above the admission relay. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…t the daemon's (#1728) The daemon page's metrics section is checked against what the daemon renders; the budget and in-use gauges are node series, documented on the distributed-compilation page beside the charged one. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
- `DecodeStatsReading` reads the admission block through `ReadAdmission`, as it reads consensus through `ReadConsensus`: grammar 8's second list took it past the cognitive-complexity threshold. - Two test helpers: a const-reference parameter, and a regate lambda that returns an explicit copy. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…t as a build-dir define (#1728) The helper's absolute build-tree path was a COMPILE_DEFINITION on ProcessRunner_test.cpp, so the preprocessed unit differed per build directory and missed the warm replay build. It now travels in the test environment; the cases SKIP when it is absent. The trivial-process case reuses the helper, so no CMAKE_COMMAND path is needed either. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…cOS runner (#1728) Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
ISocket::armKeepAlive, for a server that decides an accepted connection is long-lived. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…nnection (#1736) A held admission connection is the lease; a caller host that vanished without a FIN kept its charge until MaxCompileLeaseLifetime. Once a reply leaves the connection holding a grant, the endpoint arms keepalive through ISocket::armKeepAlive, once per connection, with the intervals the dials use (Pace::AdmitKeepAliveProbe, moved out of the launcher so one constant names them). What arms is a column of the hold (IConnectionHold::KeepAlive). SealedFrameSocket and TotalDeadlineSocket forward the call. A failure to arm is a warning, not a refusal. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
…and that a refusal to arm only warns (#1736) Pure cases over FrameServer::ArmHeldKeepAlive (nothing held, a hold asking for none, once per connection; Unsupported warns once and keeps the hold), a forwarded grant armed at the follower and the leader (the relabelling fake's forward sits under the ledger), and TotalDeadlineSocket checks the interval too. Rules: name the accepted-socket door in wire-and-protocol.md and make the wrapper-forwarding sentence true. Signed-off-by: Christian Parpart <c.parpart@lastrada.net>
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.
Closes #1728.
Closes #1736.
Why
Several builds side by side on one workstation run hundreds of compiles at once. The kernel's OOM killer then takes down unrelated builds: 18 kills in 90 minutes, as measured in #1728. Every one of those compiles already goes through
pace-cc, but until now pace could only say "compile it yourself".What
Every compile that misses the cache now waits its turn in one queue on the scheduling leader. The leader admits work against each machine's slot and memory budget:
Distributed::AdmissionQueue,AdmissionResponder):Status::Progress, so the wait is never silent.max(charged, held).Admit/Settleto the leader inline (AdmitForwarder), and never to itself.MachineBudgetreport each round: budget, held charges and measured peaks. A node with--slots=0sends one too, and that is not a worker registration. New flags:--machine-slots--machine-memory-budget(default 75% of RAM minus the cache tier)--compile-estimate-default(default 2 GiB)pace-cc,MissFlow/AdmitSession):wait4on POSIX, a job object on Windows), and feeds the estimates. Estimates are keyed by cache key, then by (source path, compiler).pace-cc -sshows the queue wait and the peak memory./metricsgets admission counters, a real wait_sum/_count, and per-machine budget and in-use gauges.pacectl live-stats fleet,/fleet.jsonand the fleet page show the queue depth and the clients (machine, build tree, running, queued).Admit,SettleandMachineBudgetverbs.Status::Progressis allowed on{Compile, Admit}.core::ranges::Stride, because libc++ has nostd::views::stride(ranges: core::ranges::Stride, because libc++ has no std::views::stride contour-terminal/core-cpp#80). Theranges-seamcheck now derivesviews::selections.ISocket::armKeepAlive(net: arm TCP keepalive on an accepted socket contour-terminal/core-cpp#82).IConnectionHold::KeepAlive(), once per connection). A caller whose host vanishes without a FIN is noticed in about 16 s (30 s on Windows), not after the one-hour lease sweep. Every pace socket decorator forwardsarmKeepAlive..agent/rules/distributed-compilation.md, tripwires inAGENT.md, operator docs for pace-cc, pace-node and pacectl, and the protocol grammar.Decisions made along the way (recorded in the rules)
PACE_ADDR).PACE_SCHEDULERno longer redirects it, and dispatch planning no longer requires it.MaxWorkerRefusals(3) the compile runs locally, still charged. A worker failure settlesNotStored, never an abandon, so estimates aren't poisoned.Compatibility
0xFC v15 ↔ v16 do not interoperate (
MinSupportedVersion= 16). Raft (v5) and the on-disk state are unchanged, so an upgraded node keeps its identity and cluster state. In a fleet mixing 0.6.0 and this version, registration, dispatch andpacectlfail between old and new machines; every machine still compiles locally and uses its own cache. Upgrade every member together.Known limitations (each filed)
Tested
scripts/local-gate.sh, WSL Ubuntu 24.04) on the head commit:scripts/tsan-gate.sh): clean on PaceTest, pace-node-tests, pace-cc-tests and pacectl-tests, with the canary live.masterand are now ticketed: FileTrust test hard-codes the English name of LOCAL SERVICE and fails on non-English Windows #1733, Tests that run python3 fail on Windows where the Store app-execution alias shadows a real Python #1734 and Link-using tests assume a created symlink/junction can be followed; on Windows a non-admin link may not open (error 448) #1735.FleetAdmission_test.cpp), each shown red with its rule removed:kill -9does not shrink the budget;Still to be done by the owner after merge: the acceptance run on the 32-core workstation (six concurrent
cmake --build, zero OOM kills).