The memory adapter¶
gitlab.com/phpboyscout/go/dispatch/memory is an in-memory adapter that implements the whole
adapter SPI, including seven optional interfaces (Holder, Requeuer,
NativePolicyCompiler, Watcher, CessationSignal, IdentityWeakener and MultiFinder). It
doesn't implement OutputReader, so it reads no output. It behaves like Slurm or like Kubeflow,
as its configuration chooses. The conformance suite is run against it, and the simulator uses it as
the scheduler. It is not for production.
Importing the package registers it under the name memory.
Configuration¶
The registry's Factory decodes AdapterConfig.Settings strictly: an unknown field at any level,
an unknown capability, action or tristate, or trailing data is refused with ErrInvalid.
Durations are Go duration strings.
{
"shape": "slurm",
"maxJobId": 100,
"minJobAge": "5m",
"callBound": "25s",
"slurmdTimeout": "40s",
"watchBuffer": 64,
"capabilities": {"AccountingHistory": {"value": "yes", "source": "probed"}},
"rules": {"restart": {"value": "yes"}},
"native": {"restart": {"value": "yes"}},
"cessation": {"ceased": true, "detail": "node fenced"}
}
| Setting | Default | Meaning |
|---|---|---|
shape |
required | slurm or kubeflow |
name |
memory |
the backend name refs carry |
namespace |
default |
Kubeflow: the namespace in object ids |
firstJobId, maxJobId |
1, 67043328 | Slurm: job ids wrap at maxJobId |
minJobAge |
never | an ended object is purged this long after it ends |
callBound |
25s |
Declarations.CallBound |
nodeLossWindow |
5m |
Declarations.NodeLossWindow |
slurmdTimeout |
0 | Slurm: a job on a lost node reports Running this long before it ends NodeLost |
evidenceWindow |
10s (Kubeflow) |
Kubeflow: how long pod-derived causes stay readable |
watchBuffer |
64 | each subscriber's channel capacity |
autoRun |
false | start a runnable object at the next call, for a harness with no environment actor |
capabilities, rules, native |
per shape | override the capability, Perform and native tables |
identityWeakened |
none | the reason IdentityWeakened answers for every spec; empty keeps execution identity intact |
cessation |
none | the cessation command's answer: ceased, detail, fail, hang |
site |
none | a site plugin: rewriteComment, setRequeue, refused by read-back |
A native entry of yes whose rules entry is not yes is refused. In Go, memory.New takes the
same settings as a memory.Config.
Shapes¶
Slurm:
- every object has the job's constant name
dj-<ULID>, a wrapped job id, and a submit time truncated to the second; Startis gated;Openof an object that ended isErrFinished, and an admin hold refusesOpen;Stopacts on one id and submit time;- a hold is a start time of
SubmitTime + HoldBase + instance. It is refused on a running job, survivesOpen, and only aReleasenaming the current instance lifts it; Requeuerevives a finished or cancelled object toPending, ungated, with its restart count raised. A pending object refuses it, and the requeue bit is restored either way;- a requeue bit left set, as
RequeueExternallyleaves it, makes a preemption requeue the job on Slurm's own authority (Pending, causeRequeued) instead of ending itPreempted.RestoreRequeueBitclears it; an ended object refuses the update.
Kubeflow:
- names are
dj-<ulid>-s<N>, a duplicate is refused, and a stop deletes; - preemption sends the object back to pending without touching its restart counter;
- a lost node wedges it
Runningwith causeNodeLost, and it never ends on its own; - pod-derived causes (
NodeLost,OutOfMemory,NonZeroExit) read asUnknownonceevidenceWindowhas passed; - a hold deactivates the Workload, ending a running execution with cause
Held; RequeueandRestoreRequeueBitareErrUnsupported.
Both shapes give each execution an opaque DISPATCH_ATTEMPT and a DISPATCH_EPOCH from
model.Epoch. On Kubeflow the epoch's R counts executions, because the restart counter shown does
not count re-admissions.
Deadlines and time¶
Every Hold, Release, Requeue and RestoreRequeueBit carries a CallMeta.Deadline. A call
whose command started after its deadline does nothing and returns ErrExpired. The start is time.Now() at the call,
unless the context carries one:
ctx = memory.WithCallStart(ctx, startedAt)
err := b.Hold(ctx, ref, model.HoldMeta{Instance: 2, CallMeta: model.CallMeta{Deadline: d}})
A HoldMeta also carries who placed the hold, until when and why. The Kubeflow shape writes them
beside the hold as annotations, shown in the ground truth as HeldBy, HeldUntil and
HoldReason until the hold is released. The Slurm shape keeps only the instance, as a StartTime
hold does.
The adapter starts no goroutines and sets no timers. Purges, lost nodes ending and evidence
vanishing are worked out from the clock inside each call, so a testing/synctest bubble's fake
clock drives all of them. Watch sends from inside the calls that change an object. A full
buffer, a done context or Close closes the stream, and the subscriber resubscribes to get every
object's current observation.
Environment and ground truth¶
b.Env() drives the scheduler's side and reads the ground truth, which every view from
b.View(readOnly) shares:
| Method | Effect |
|---|---|
Run(ref, nodes...) |
start a runnable object; a gated or held one is refused |
Complete, Fail(ref, code), End(ref, cause) |
end the running execution |
Preempt, LoseNode, RecoverNode |
scheduler events, per shape |
RequeueExternally |
Slurm: an operator's requeue, which leaves the requeue bit set |
RestartSilently |
Kubeflow: a kubelet restart nothing above the pod shows |
AdminHold |
Slurm: JobHeldAdmin |
CreateForeign(job, comment) |
an object with the job's name and an unparseable comment |
SetCessation |
change the cessation command's answer |
Objects, Object(ref), DownNodes |
ground truth, including purged and deleted objects, whether each was opened, its holds, its executions and their DISPATCH_* environment |
Conformance¶
conformance.Backend(t, harness) runs the backend conformance suite, and conformance.Core(t,
harness) the core's. The memory adapter passes both in both shapes; see
memory/conformance_test.go and memory/core_conformance_test.go for the harnesses, and write an
adapter for writing one of your own. That harness injects LateCall
with WithCallStart set past the deadline plus the bound, and CrashBetweenSteps with
WithCrashBetweenSteps, under which a Requeue sets Requeue=1 and returns ErrCrashed.