Skip to content

Read a preflight result

The adapter does not hand you a "preflight result" object. slurm.New runs preflight and fixes its answers for the life of the Backend. You read them through two methods:

d := b.Declarations()                    // model.Declarations, no error
caps, err := b.Capabilities(ctx, nil)    // map[model.Capability]model.Support

Through dispatch.Open, the core reads the same two methods and uses them to build a job's plan. The tutorial prints both. Capabilities and declarations lists every value and reason the adapter can give. This page is about what to do with them.

What preflight does

Steps 1 to 7 read the cluster on every call to slurm.New. Steps 8 and 9 write, so they run only on a backend that is not read-only, and only when there is no fresh cached result.

Step Does Fails with
1 checks that sbatch, squeue, scontrol, scancel, sacct, timeout, date and base64 exist ErrUnsupported, naming the missing commands
2 reads sbatch --version ErrUnsupported, unless the release is 25.11 or 26.05
3 reads scontrol show config: MinJobAge, KillWait, SlurmdTimeout, MessageTimeout, AccountingStoreFlags, PrivateData, AuthType and AuthInfo, and ClusterName when present. Nothing else in the dump is read an error with no sentinel if a key is missing, repeated or unreadable
4 lists your jobs with squeue --me --json=v0.0.44, which must report that data parser ErrUnreachable, or an error for an unreadable answer
5 reads $HOME on the host, which becomes the literal $HOME stands for in output.dir ErrUnsupported if it is not an absolute path
6 with transport: ssh only: reads the host's clock against yours an error with no sentinel if date +%s.%N does not print seconds and nanoseconds
7 finds the authentication credential's lifetime: an AuthInfo ttl=, else under auth/munge the TTL munge -n \| unmunge prints, after checking that munge and unmunge exist ErrUnsupported if munge or unmunge is missing; an error with no sentinel for an AuthInfo ttl= that is not a positive whole number, under any AuthType, or for unmunge output with no TTL
8 stops any probe job of yours that is more than 10 minutes old, left by a crashed preflight the stop's error
9 submits the probe and tests holds, the controller's clock and accounting ErrRejected if the cluster changes the probe; see Errors and error mapping for the rest

Any read in steps 1 to 7 that fails to run, or exits non-zero, is ErrUnreachable.

A read-only backend runs steps 1 to 7 and never writes the cache. With a matching cached result it takes Hold and AccountingHistory and the skew from it. Without one, both answers are unknown, the execution window is 0, and CallBound is worked out with a skew of 0. Such a backend makes no hold-family or requeue call, so its CallBound is informational.

Steps 1 to 7 are never cached. Because they run every time, a cluster that changes release or loses a command is noticed on the next slurm.New.

Set your poll interval from the declarations

The adapter has no Watcher, so a supervisor polls it. PolledLoss for state, cause and exit-code is the cluster's MinJobAge: poll at least that often, or a job can end and leave squeue between two polls. The supervisor refuses a poll interval slower than the shortest bounded window. If MinJobAge is too short for the poll you want, ask your administrator to raise it.

Act on Hold

You see Do
yes nothing
no, AuthType "..." has no known landing bound holds and requeue are unavailable on this cluster. Write recovery policies without a hold action, or ask your administrator about auth/munge or auth/slurm
no, naming a probe step the cluster behaves differently from what holds rely on. Read the reason: it names the step, such as the hold not decoding from the job's start time. Prepare a cluster lists what the probe depends on
unknown, read-only, and no cached probe open the backend writable once with a preflight.cacheDir, then open read-only backends with the same cache directory, adapter settings and target

With Hold not yes, Hold and Release return dispatch.ErrUnsupported with the reason, and a recovery rule whose action is hold reports the same answer from Rules.

Act on AccountingHistory

You see Do
yes, comments are kept nothing
yes, comments are not kept: ... finished jobs read from sacct have no submission or attempt number. Ask your administrator to add job_comment to AccountingStoreFlags
no a job that has left squeue is dispatch.ErrNotFound. Poll within MinJobAge
unknown treat it as no. The reason says why it could not be established

Inspect and Ceased read sacct only when the answer is yes, so a read-only backend with no cached probe never does.

Checking a result in your own program

Support.Value prints as yes, no or unknown, and Support.Source as declared, probed or catalogue. To fail fast on a cluster that cannot hold:

caps, err := b.Capabilities(ctx, nil)
if err != nil {
    return err
}

if h := caps[model.HoldCapability]; h.Value != model.Yes {
    return fmt.Errorf("holds are %s on this cluster: %s", h.Value, h.Reason)
}

Where the probe's answers are kept

With preflight.cacheDir set, steps 8 and 9 results are stored in one file there, named for the cluster, release and target. A result is reused only if all of these hold:

  • it is younger than preflight.maxAge (24 hours by default);
  • the cluster name, Slurm release and target are the same. For transport: ssh the target is the transport, host, user, port and jump host; for any other transport it is the transport's name and the host name of the machine the adapter runs on, such as local:<hostname>;
  • a hash over your adapter settings and scontrol show config is the same. Only the dump's "Configuration data as of" line is ignored.

Anything else, including a file that cannot be read, is a miss, and the probe runs again. The directory must be a real directory of mode 0700 owned by you. A cache found anywhere else, or reached through a symlink, is not read. A writable slurm.New that has to write into such a directory fails with an unsafe-directory error rather than continue. Only a writable backend creates the directory, after its probe; a read-only one never does.

The cached clock skew comes with it, so it is as old as the cache. Lower maxAge if clocks on your cluster drift.