Writes one bash file for each job, plus submit.sh. It submits nothing.
Arguments
- x
One
slurm_itobject, or a list of them. List position is chain order: jobiwaits for jobi - 1to succeed. Every job MUST take its ownname, and no job may take the namesubmit.- dir
Character(1). The directory to write into.
slurm_write()creates it when it is absent, and it owns every*.shfile in it. The path MUST hold no whitespace character, because it reaches#SBATCH --output=.
Value
Character vector of the paths written, invisibly. The job paths
come first, in chain order, and the submit.sh path comes last. Every
path is mode 0755.
Details
slurm_write() owns dir. It deletes every *.sh file there before it
writes. The caller regenerates the chain on every run. So a chain of four
written over a chain of five MUST NOT leave the fifth job file behind. The
new submit.sh does not name that file, and it still runs by hand.
submit.sh is the only generated file that names the submission command.
A job file runs under Slurm and knows nothing about the chain.
What every job file carries
Four items reach every job file, and no argument controls them.
The output and error paths, under
dir.A start timestamp, before the job body.
Peak memory, read on exit.
An end timestamp and the exit code, from an
EXITtrap.
Item 3 reads the cgroup v2 counter of the job's own cgroup. The job derives
that path at run time from /proc/self/cgroup. The root counter
/sys/fs/cgroup/memory.peak is not readable inside a Slurm job.
A job that cannot read its counter prints batchit_peak_memory_unavailable.
It reports no number. batchit reads no second counter. VmHWM from
/proc/self/status measures the job's own shell. It read 4,744 kB against a
payload that held 2,000,000,000 bytes in a child R process.
The option batchit.memory_peak_path names an explicit counter and turns
the derivation off. Set it where the cluster keeps the counter somewhere
else.
The trap captures the exit status in its first statement. So the job reports the status its body exited with, and not the status of the trap's own work.
A job file runs under set -euo pipefail. The body stops at its first
failing command, which is what a chain built on --dependency=afterok
needs.
The version gate and its interpreter
A job that names require_r_package carries a version gate before its body.
The gate MUST NOT depend on the environment that starts the job. So it runs
the interpreter under env -u R_TESTS. It also names that interpreter by an
absolute path.
The path defaults to file.path(R.home("bin"), "Rscript"), which is the R
that writes the chain. Set the option batchit.rscript_path where the
compute node keeps R somewhere else. A bare Rscript would name whichever
one comes first on the node's PATH. A gate that cannot say which R it
asked proves nothing about a version.
R CMD check exports R_TESTS, and every subprocess inherits it. So this
matters to a package that tests a generated job under R CMD check. A
production Slurm job carries no R_TESTS, so the unset costs nothing there.
The gate prints the version it read, as batchit: <package> <version>. The
job log then names the version the work ran under, and not only the version
a refusal wanted.
The job then puts the directory of that interpreter first on PATH, and it
does so after the gate. A bare Rscript in the body is therefore the
binary the gate checked. The gate itself still reads the PATH the job
started with, so a dummy Rscript first on that PATH cannot reach it.
What submit.sh checks before it submits
submit.sh runs two checks. A refusal writes batchit: REFUSED: to
standard error, exits 1, and submits nothing.
EVERY state
sinforeports for the node MUST beidle,mixedorallocated. The driver names the node withhostname -s. It deletes the trailing*that marks a node slurmctld cannot reach. It joins with commas the several lines that a node in more than one partition emits, then tests each one. A node drained in one partition and idle in another reportsdrained,idle, and it cannot run the job.A job name in the chain MUST NOT already appear in
squeue. This check fails closed. Asqueuethat exits non-zero refuses the submission, because it leaves a duplicate possible.
The node check reads the node the driver itself runs on. Run submit.sh on
a login node, and hostname -s names that login node.
What the tests of submit.sh do not prove
The tests drive the generated submit.sh with stub hostname, sinfo,
squeue and sbatch programs on PATH. So they prove the shell branching
against a protocol the tests wrote. Four things stay unproven.
The argument spelling that the real
sinfoandsqueueaccept.The output grammar that the real
sinfoandsqueueproduce.Which users' jobs
squeuereports. The check reads every job the caller can see, and no option scopes it to one user.That two
submit.shruns started at the same time cannot both pass. Each one readssqueuebefore either one submits.
See also
vignette("batchit"), section "Slurm: write a chain of
jobs".
Other slurm:
inside_slurm_job(),
slurm_it(),
slurm_status(),
slurm_submit()
Examples
dir <- file.path(tempdir(), "batchit-chain")
jobs <- list(
slurm_it(
script = "Rscript s1_build.R",
name = "proj_s1",
cpus = 6,
mem = "85G",
time = "12:00:00"
),
slurm_it(
script = "Rscript s2_report.R",
name = "proj_s2",
cpus = 2,
mem = "8G",
time = "01:00:00"
)
)
paths <- slurm_write(jobs, dir)
basename(paths)
#> [1] "proj_s1.sh" "proj_s2.sh" "submit.sh"
unlink(dir, recursive = TRUE)
