/etc/sjp/sjp.toml (or -c FILE, $SJP_CONFIG). Unset keys take the defaults below;
python3 -m sjp.daemon --print-config prints them all. Restart sjpd after changes.
Partitions, nodes, PriorityTier and QOS caps come from Slurm.
| setting |
default |
|
mode |
"observe" |
How far sjp may go. observe: log what it would do, change nothing. advise: also write the placement table, so sjp.place() sets partitions (no node pins). enforce: also pin nodes, release unstarted pins, recheck and widen pending jobs, and act on QOS limits |
state_dir |
"/run/sjp" |
Where sjpd writes policy.lua (read by the plugin) and status.json
|
log_file |
"/var/log/sjp/decisions.jsonl" |
Every decision and action, one JSON object per line; read with python3 -m sjp.report
|
text_log |
"/var/log/sjp/sjp.log" |
The same, one readable line each; "" = off |
disable_file |
"/etc/sjp/disable" |
While this file exists, sjp.place() returns false for every job and sjpd changes nothing (except releasing its pins) |
| setting |
default |
|
score_interval |
1.0 |
Seconds between placement table updates |
node_poll_interval |
1.0 |
Seconds between scontrol show nodes reads |
queue_poll_interval |
8.0 |
Seconds between squeue reads; it takes a job table lock, so keep it slow. Pin releases, rechecks and widening run after each read |
act_interval |
15.0 |
Seconds between QOS limit checks ([limits]) and status.json updates |
policy_max_age |
300.0 |
Seconds; the plugin ignores an older table ("stale"), so if sjpd stops, your own rules take over |
| setting |
default |
|
batch_partitions |
[] |
Partitions sjp may assign; empty = all from scontrol show partitions
|
exclude_partitions |
[] |
Partitions never assigned, by name |
exclude_interactive |
true |
Never assign a partition named interactive
|
exclude_gpu_nodes |
true |
Ignore nodes with a GPU; partitions left without nodes are dropped |
| setting |
default |
|
demand |
six [MB per CPU, weight] pairs |
The memory per CPU jobs typically ask for, with weights. Free space counts as far as these jobs could use it (phi). Fit yours with python3 -m sjp.fit --since <date>
|
demand_median |
4267 |
MB/CPU; only for the stranded-CPU figure in the log |
tolerance |
0.25 |
Besides the best partition, also allow those within this many CPUs of phi per job CPU |
user_nodes |
"follow" |
A job's own --nodelist/--exclude: follow places it among those nodes, in partitions holding them; ignore drops them and places the job like any other (for sites where users ask for hardware by features only) |
always_place |
false |
Place jobs even when no node has room now, in the partitions their shape fits best on an empty cluster; otherwise sjp.place() returns "no room" and your rules apply. The recheck moves them once room opens |
buckets.mem_per_cpu |
[1024, 2048, 4096, 6144, 8192, 12288, 24576] |
MB/CPU bucket edges of the table |
buckets.cpus |
[1, 4, 8, 16, 32, 64, 128] |
CPU bucket edges |
buckets.walltime_h |
[1, 6, 24, 72] |
Time limit bucket edges, hours |
See Decision making.
| setting |
default |
|
enabled |
true |
Choose the node too (--nodelist) when a job can start at once; enforce only |
min_gain |
1.0 |
Nodes within this many CPUs of phi of the best count as equally good; among them, the closest memory per CPU wins |
min_ratio_gain |
0.1 |
Pin only if the chosen node beats the worst candidate by min_gain or by this much in |log ratio| (0.1 ≈ 10%); otherwise Slurm picks the node |
release_after |
60.0 |
Seconds a pinned job may stay pending before sjpd removes the pin and restores its partitions |
max_age |
10.0 |
Seconds; the plugin places and pins only from free-space data this fresh |
wait_for_room |
false |
With always_place and [recheck]: also pin those jobs to the node expected to have room first (from the time limits of the jobs running and waiting there). The recheck moves the pin; it is released only past the job's [starvation] budget. enforce only |
time_aware |
false |
Also weigh time limits: prefer nodes whose running jobs end around when the job would, so long jobs gather and nodes running short jobs empty together. Uses time limits, not real run times |
| setting |
default |
|
enabled |
true |
Widen jobs sjp placed that pend too long to every partition that can hold them; once per job, enforce only |
budget_hours |
{fat = 1.0, slim = 4.0} |
Hours pending for Resources/Priority (since eligible) before widening |
fat_ratio_threshold |
6000 |
MB/CPU at which a job counts as fat |
| setting |
default |
|
interval |
60 |
Seconds between rechecks of sjp's pending jobs (and those it skipped for want of room): each is placed again against the cluster as it is now, and moved if the choice changed and a node there has room; with wait_for_room, re-pinned to the node now expected to have room first. enforce only; 0 = off |
See QOS limits and flex.
| setting |
default |
|
mode |
"off" |
Jobs held only by a QOS CPU cap that would fit on idle nodes. off: nothing. global: raise the QOS's caps for everyone, briefly. flex: move those jobs to flex_qos_name
|
state_file |
"/var/lib/sjp/limits.json" |
global: the base caps during a pulse, restored if sjpd stops mid-pulse |
ceiling |
2.0 |
global: raised caps as a multiple of base |
min_idle_share |
0.25 |
Act only while at least this share of the cluster's phi is idle (was raise_above, still accepted) |
count_pending |
false |
Count the CPUs of jobs waiting for room (Resources, Priority) as taken when working out the idle share: they get the idle capacity first. Not jobs the caps hold, or that cannot start yet |
lower_below |
0.10 |
global: end a pulse early when the idle share falls below this |
hysteresis |
5 |
global: consecutive checks that must meet the conditions before a pulse |
pulse_seconds |
60.0 |
global: how long the caps stay raised |
cooldown_seconds |
300.0 |
global: minimum time at base between pulses. flex: minimum time between moves of one job |
flex_qos_name |
"flex" |
flex: the QOS held jobs move to; create it and allow it for users first |
flex_revert_after |
60.0 |
flex: seconds a moved job may stay pending before it moves back |
python3 -m sjp.daemon [-c FILE] [--mode MODE] [--dry-run] [--state-dir DIR]
[--log-file FILE] [--text-log FILE] [--once] [--print-config]
--dry-run forces observe. --state-dir also moves the logs unless --log-file/
--text-log are given. --once: one pass to stdout.