Jobs¶
Workers handle two job kinds: flake jobs and build jobs. The sections below list their messages and reports, and the handling of failed, aborted and lost jobs.
- Jobs arrive as
AssignJob { job_id, assignment_id, job, cluster }messages. - Echo the
assignment_idinJobUpdate,JobCompleted,JobFailed,BuildProgressandEvalProgress. Staleassignment_idvalues make the server drop the report. - Workers accept a re-sent
AssignJobfor a job already running. Later reports of the job then echo the newassignment_id. - Only cluster members get a
clusterfield. - Reports of a session go into a queue of 64 events, off the session's read loop. Reports apply in arrival order.
Flake Jobs¶
Flake jobs fetch and evaluate a flake. The server can pick the steps at queue time.
| Situation | Steps |
|---|---|
| An idle evaluation-only worker is available, and the evaluation is no cluster member | [FetchFlake], then a follow-up job [EvaluateFlake, EvaluateDerivations] on the Cached source |
| Otherwise | All three steps in a combined job |
| Field | Meaning |
|---|---|
steps |
FetchFlake, EvaluateFlake, EvaluateDerivations |
source |
Repository { url, commit }, or Cached { store_path } for a follow-up job or a gradient build upload |
wildcards |
Attribute patterns to evaluate |
timeout_secs |
Optional limit, sent as None by the server today |
input_overrides |
Flake input overrides of the task |
input_update |
Set for a flake update |
Fetch¶
- Clone the repository. Skip the clone for a
Cachedsource. - Run the flake update generator for a job with
input_update. - Serialise the tree at the pinned commit as a NAR, without a nix process. Add the NAR to the store as
<narHash>-source, the path ofnix flake prefetchfor the same tree. - Leave out inputs with an override. Overrides apply at evaluation. Override names without a matching input end up as
EvalMessagewarnings. - Pull missing locked inputs from the Gradient cache. Fetch the rest with
builtins.fetchTreein an eval worker.- Inputs of the same second-level domain (such as
github.com) download in sequence. Different domains download in parallel. - Failed inputs drop out with a warning.
- Inputs of the same second-level domain (such as
- Report
FetchResult { flake_source }. -
Upload the source and the inputs with a
Pushcache query. Upload and evaluation steps of the same job run side by side. -
Upload failures fail the whole flake job.
- Fetch-only jobs send
JobCompletedafter the upload, withEvalProgressresent meanwhile. - The server can then queue the follow-up job. Evaluations without a
flake_sourcefail instead.
Evaluate¶
| Step | Worker action |
|---|---|
EvaluateFlake |
Status EvaluatingFlake only, no work |
EvaluateDerivations |
Eval cache pull, attribute discovery, closure walk, eval cache push |
- Eval Cache: Workers pull the evaluation cache before discovery and push the cache after a walk with derivations. Empty or schemaless caches stay local.
- Attribute Failures: Workers send them as
EvalMessage { level: Error, source: "nix-eval:<attr>" }before the lastEvalResult. - Import From Derivation: Workers send
ImportRequest { drv_paths }during discovery and wait forImportResult. - Walk: Workers walk an entry point as soon as discovery returned it.
- Waves: Waves hold up to 256 of the most recently found derivations. Leaf derivations then come early in the walk, and their builds can start mid-evaluation.
sequenceDiagram
participant W as Worker
participant S as Server
loop each wave with unvisited dependencies
W->>S: QueryKnownDerivations { drv_paths }
S->>W: KnownDerivations { known }
end
loop each 50 derivations
W->>S: CacheQuery { Push } + uploads (.drv files, sources)
W->>S: JobUpdate { EvalResult { derivations } }
end
W->>S: JobUpdate { EvalResult { derivations, warnings } }
W->>S: JobUpdate { EvalStats }
W->>S: JobCompleted
- Derivations in
knownhave complete closures in the graph. Workers skip their subtrees. - Workers treat a failed
QueryKnownDerivationsas an emptyknown. Full rewalks get an emptyknowntoo. - Batches upload their
.drvfiles and input sources before theirEvalResultmessage. Builds can start during the walk. - Input
.drvfiles travel with the batch walking the input. - Walk and batch uploads run side by side. Uploads of consecutive batches overlap.
EvalResultmessages follow their own batch uploads, in walk order.- Workers query and upload each path no more than once per evaluation, apart from the input upload of the fetch step.
- The server can store each batch in the graph and move each startable build to
Queuedright away. - Consecutive
EvalResultbatches of a job, already waiting in the session queue, reach the graph writer together. Limits of the merge: 5000 derivations, and no other event in between. - Evaluations in
EvaluatingFlakeorEvaluatingDerivationturnBuildingonJobCompleted. EvalResulterrors (no derivation matched, a failed listing) become error messages. Error messages fail the evaluation at the end.
Build Jobs¶
Build jobs hold exactly 1 BuildSpec, meaning a shared build (derivation_build).
kind |
When | Action |
|---|---|---|
Build |
Default | Prefetching inputs, then building the derivation with the Nix daemon |
Substitute |
The outputs are available in an upstream cache | Fetching the outputs without a Nix store |
Download |
A fixed-output derivation of the builtin system |
Downloading the file without a Nix store |
SubstituteandDownloadjobs can start on any worker (systembuiltin).- Build jobs also get
drv_path,outputs,is_fixed_output,timeout_secsandmax_silent_secsfrom the spec. -
Default limits:
build.defaultTimeoutSecs(14400) andbuild.defaultMaxSilentSecs(3600). -
Check for an abort, then report
Buildingbefore anything that can fail. - Skip prefetch and build with all outputs already in the local store. Report
BuildOutput { substituted: true }and upload the outputs anyway. - Prefetch: Read the
.drv, drop inputs already in the store, and pull the rest over the transfer protocol. - Build through the daemon. Stream the log as
LogChunkmessages. - Report
BuildOutputwith outputs,hydra-build-productsand metrics. - Upload the outputs, then send
JobCompleted. Upload errors fail the job asTransient.
Reports¶
JobUpdate kind |
Effect on the server |
|---|---|
Fetching, EvaluatingFlake, EvaluatingDerivations |
Evaluation status |
FetchResult { flake_source } |
Storing the fetched source in evaluation.flake_source |
EvalResult |
Storing a batch of derivations in the graph |
EvalStats |
Evaluation metrics |
InputUpdateResult |
Flake update candidate lock and bumped inputs |
InputUpdateExpansion |
Input names matching a glob in a discover_only pass. A new evaluation per name |
Building { build_id } |
Build turning Building. Already aborted builds get AbortJob instead |
BuildOutput |
Output sizes, build products, metrics, the substituted flag |
Stage(Prefetch / Build / Upload) |
Live stage of the job in the worker pool, for the estimated time. Prefetch and Build for the Build kind only |
Compressing |
No change. Workers no longer send it |
EvalProgressmessages hold a download row per flake input while fetching, and the live thunk count while evaluating.- Download rows appear only with a missing input. Eval workers download the inputs themselves, up to 1 download per second-level domain.
JobCompletedandJobFailedhold the phase timeline shown on the Job Board.- Build metrics also hold the number of concurrent builds on the worker, the cores of the build and the worker's CPU score.
Failures¶
BuildFailureKind |
Result |
|---|---|
Transient |
Retried up to build.maxAttempts (3), backoff build.retryBackoffSecs (30 s) doubling. Builds available in a cache take the substitution miss path after the last attempt |
Permanent |
Failed. Builds needing the failed build turn DependencyFailed |
Timeout |
Timed out. Builds needing the timed-out build turn DependencyFailed |
SubstituteUnavailable |
Re-queued. Built normally after build.substituteMissEscalationThreshold (2) misses |
InputsUnavailable |
Self-heal, see below |
CorruptEvalCache |
Purging the evaluation cache blob and re-queuing the evaluation. A repeat without a blob left: evaluation Failed. For builds: Permanent |
Aborted |
Aborted by the server. No cascade |
Canceled |
Stopped by the worker. Re-queued without using an attempt, see Worker Stop |
- InputsUnavailable: An input listed by the cache was missing. Causes are an uncached path,
404/410on the URL,NarUnavailableor a NAR hash mismatch.- The server can delete the outdated cache row and object, reset the producing build and retry.
- Inputs still present but without a producer stay in place.
- Builds fail permanently after
build.inputsUnavailableMaxLoops(3) loops.InputsUnavailablefailures also use up attempts. Attempts and loops both default to 3. Attempts run out first.
- DependencyFailed can spread upward over the dependency graph from
PermanentandTimeoutfailures, across evaluations. - Eval Job Outage: Eval jobs failing
Transientre-queue the evaluation, up tobuild.maxAttempts(3) attempts. Abandoned dispatches stay out of this count.- Typical causes are a dropped server connection, or an object PUT or
CacheQuerywithout an answer.
- Typical causes are a dropped server connection, or an object PUT or
- Out of Memory: Builds killed by the kernel for memory count as
Transient, whatever kind the worker sent. The attempts close with the reasonOom. - Not Enough Resources: A queued build can turn
FailedPermanentafter 5 minutes without a worker able to hold the build. Such attempts close with the reasonNotEnoughResourcesand without a dispatched job.- The predicted peak memory of such builds, plus 10 %, is above the free memory of every connected worker able to build them.
- Workers with memory reserved for running jobs are large enough while the total memory can take the build.
- Unmeasured builds and workers without a sample stay out of this check.
- Evaluations end
Completed, orFailedwith a build inFailed,AbortedorDependencyFailed, or with an error message present.
Cluster Members¶
Cluster members arrive as AssignJob with cluster = { attempt, role, index, hold_secs }.
| Event | Worker |
|---|---|
AssignJob with cluster |
Holding the slot without running the job, then accepting. Rejecting a second member of the same attempt |
StartCluster { attempt, roster } |
Running the held member. The member's signal route opens with the roster |
ClusterSignal from the server |
Delivered to the inbox of the running member of that attempt. Dropped once the member finished. No executor reads the inbox yet |
ClusterSignal to the server |
Wire and server routing only. No executor sends signals yet. to = None reaches all other started members |
AbortCluster { attempt } |
Dropping a held member unreported and aborting a running member (JobFailed { Aborted }) |
AbortJob for a held member |
Releasing the slot with JobFailed { Aborted } and aborted by server |
No StartCluster within hold_secs |
Releasing the slot and reporting JobFailed { Aborted } with cluster start timed out. Checks happen on the 10 s heartbeat, up to 10 s late |
| Worker stop | Releasing all held members the same way, with worker draining |
- Held members count against
eval.maxConcurrent/build.maxConcurrentlike running jobs. hold_secs: the server'sscheduler.clusterPrepareTimeoutSecs(30 s) plus a 10 s margin.
Abort and Lost Workers¶
- API Abort: Evaluation status
Aborted. Pending jobs of the evaluation leave the queue.AbortJobmessages go to the evaluation job and to running builds wanted by no other live evaluation.- Only live evaluations wanting a build decide, not the evaluation that dispatched the build.
- Newer Evaluation: Tasks with
hard_abortconcurrency abort the older evaluation like the API. Tasks withsoft_abortcancel only its eval job. - Unbuildable Evaluation: Evaluations without a worker for their systems end
Abortedthe same way. - Workers stop the daemon build at once and answer
JobFailed { Aborted }. - Assignments of an unconfirmed abort close as abandoned after 5 min.
- Lost Worker: Open assignments close as abandoned.
- Back to
Queuedfor each build inBuilding, with its running attempt closed asAbortedwith the reasonWorkerLost. - Builds with 10 lost workers in a row end
FailedPermanent. An attempt closed any other way will start the row again. A requeue after an ended cluster attempt is no lost worker. - Running evaluations go to
Waitingand back into the queue. - Evaluations with 10 eval dispatches fail on the next lost worker. Dispatches of all outcomes count, fetch-only and canceled jobs too.
- Back to
Worker Stop¶
sequenceDiagram
participant W as Worker
participant S as Server
Note left of W: SIGTERM
W->>S: JobFailed with Aborted (held cluster members)
W->>S: Draining
Note left of W: aborting all running jobs
W->>S: JobFailed with Canceled
W-xS: close
- Workers release held cluster members first, then send
Drainingand abort all running jobs. - Aborted jobs report
JobFailed { Canceled }with their phase timeline. Failures during a stop all report asCanceled. - Workers wait up to 5 s for these reports.
- Jobs without a report within the 5 s re-queue with the closed session, as for a lost worker.
- Canceled builds return to
Queued. Their attempts close asAbortedand stay out ofbuild.maxAttemptsand the substitution miss count. - Canceled eval jobs return their evaluation straight to
Queued. - Their assignments close as abandoned. These stay out of
build.maxAttempts, but still count toward the 10 eval dispatches above.