Documentation
¶
Overview ¶
Package dispatch submits and supervises multi-node training jobs on the scheduler you already have, behind one contract.
A job's outcome is a coarse state plus a typed cause that is never collapsed into "failed". Capability support is reported per cluster, recovery policy is data the core validates and never executes, and the job level is computed by pure functions over the record and what the backend shows: Fold, Project and Next.
Open or Compose a Cluster over an adapter's Backend and a records.Records store, then submit through its Operator; MakePlan returns the plan Submit would record, without creating anything. Package supervise carries out what Next returns, and package conformance checks an adapter.
The tutorial, guides and reference are at https://dispatch.go.phpboyscout.uk. The design is in the project wiki's specs 0001 to 0005.
Example (SuperviseAJob) ¶
This is the tutorial "Submit and supervise a job": a job is submitted with a rule that resubmits it after preemption, a supervisor runs it, the scheduler preempts it, the supervisor recovers it, and it succeeds.
package main
import (
"context"
"fmt"
"time"
"gitlab.com/phpboyscout/go/dispatch"
"gitlab.com/phpboyscout/go/dispatch/memory"
"gitlab.com/phpboyscout/go/dispatch/records/recordstest"
"gitlab.com/phpboyscout/go/dispatch/supervise"
)
// waitFor polls the job's status until ok holds. The tutorial's scheduler is the memory adapter,
// which moves in milliseconds; a real one would be watched with dispatch.Watch.
func waitFor(ctx context.Context, o dispatch.Observer, id dispatch.JobID, ok func(dispatch.Status) bool) (dispatch.Status, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
for {
st, err := o.Get(ctx, id)
if err != nil {
return dispatch.Status{}, err
}
if ok(st) {
return st, nil
}
select {
case <-ctx.Done():
return st, fmt.Errorf("job %s stayed %s: %w", id, st.State, ctx.Err())
case <-time.After(5 * time.Millisecond):
}
}
}
func main() {
ctx := context.Background()
backend, err := memory.New(memory.Config{Shape: memory.ShapeSlurm, AutoRun: true})
if err != nil {
fmt.Println(err)
return
}
store := recordstest.New()
cluster := dispatch.Compose(backend, dispatch.Config{Store: store})
spec := dispatch.JobSpec{
Name: "finetune",
Command: []string{"torchrun", "train.py"},
Nodes: 2,
Recovery: dispatch.RecoveryPolicy{Rules: []dispatch.Rule{
{On: []dispatch.CauseKind{dispatch.CausePreempted}, Action: dispatch.ActionResubmit, Budget: 2},
}},
}
me := dispatch.Actor{Kind: dispatch.ActorHuman, ID: "ada"}
plan, err := cluster.Operator().Submit(ctx, spec, me, dispatch.WithSupervisor())
if err != nil {
fmt.Println(err)
return
}
fmt.Println("rule 0 is enforced by the", plan.Rules[0].Enforcer)
fmt.Println("uncovered:", plan.Uncovered)
sup, err := supervise.New(backend, store)
if err != nil {
fmt.Println(err)
return
}
done := make(chan error, 1)
go func() { done <- sup.Run(ctx, plan.Job) }()
obs := cluster.Observer()
st, err := waitFor(ctx, obs, plan.Job, func(s dispatch.Status) bool { return s.State == dispatch.StateRunning })
if err != nil {
fmt.Println(err)
return
}
fmt.Println(st.State, "as submission", st.Submissions[0].N)
if err := backend.Env().Preempt(*st.Submissions[0].Canonical); err != nil {
fmt.Println(err)
return
}
st, err = waitFor(ctx, obs, plan.Job, func(s dispatch.Status) bool {
return s.State == dispatch.StateRunning && len(s.Submissions) == 2
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(st.State, "as submission", st.Submissions[1].N, "after", st.Plan.Rules[0].Firings, "firing of rule 0")
if err := backend.Env().Complete(*st.Submissions[1].Canonical); err != nil {
fmt.Println(err)
return
}
if err := <-done; err != nil {
fmt.Println(err)
return
}
st, err = obs.Get(ctx, plan.Job)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(st.State, "with cause", st.Cause.Kind)
for _, e := range st.History {
fmt.Printf(" %s -> %s (submission %d)\n", e.From, e.To, e.Submission)
}
}
Output: rule 0 is enforced by the supervisor uncovered: [node-lost out-of-memory time-limit non-zero-exit launch-failed no-capacity requeued unknown] running as submission 1 running as submission 2 after 1 firing of rule 0 succeeded with cause none pending -> recovering (submission 2) recovering -> succeeded (submission 2)
Index ¶
- Constants
- Variables
- func Adapters() []string
- func FindAll(ctx context.Context, b Backend, jobs []JobID, states ...ObjectState) (map[JobID]Discovery, error)
- func Register(name string, f Factory) error
- func Watch(ctx context.Context, o Observer, id JobID, every time.Duration) <-chan Event
- type AbandonTarget
- type Action
- type Actor
- type ActorKind
- type AdapterConfig
- type AttemptRec
- type Backend
- type Budgets
- type Call
- type CallMeta
- type CancelReceipt
- type Capability
- type Cause
- type CauseKind
- type CessationEvidence
- type CessationSignal
- type CheckpointMode
- type CleanupState
- type Cluster
- type Completeness
- type Config
- type Declarations
- type Desired
- type Discovery
- type Disposition
- type Enforcer
- type Escalation
- type Event
- type Evidence
- type ExecutionObs
- type Extension
- type Extensions
- type Factory
- type Filter
- type Hold
- type HoldKind
- type HoldMeta
- type HoldRec
- type Holder
- type IdentityWeakener
- type Image
- type Job
- func (j *Job) ActiveHold() *HoldRec
- func (j *Job) AllRequeueClear() time.Time
- func (j *Job) Escalated(id string) bool
- func (j *Job) HoldCallsClear(ref ObjectRef) time.Time
- func (j *Job) HoldMeta(instance uint32, m CallMeta) HoldMeta
- func (j *Job) Latest() *SubmissionRec
- func (j *Job) Object(ref ObjectRef) *ObjectRec
- func (j *Job) RequeueClear(ref ObjectRef) time.Time
- func (j *Job) Residuals() []model.AbandonTarget
- func (j *Job) RestoreClear(ref ObjectRef) time.Time
- func (j *Job) Submission(n int) *SubmissionRec
- func (j *Job) Terminal() bool
- type JobID
- type JobSpec
- type Level
- type MultiFinder
- type NativeOffer
- type NativePolicyCompiler
- type ObjectObs
- type ObjectRec
- type ObjectRef
- type ObjectState
- type Observer
- type Operator
- type Outcome
- type Output
- type OutputReader
- type Plan
- type Provenance
- type RecoveryPolicy
- type Requeuer
- type Rule
- type RuleAssignment
- type RuleSupport
- type SharedCheckpoint
- type Source
- type StartRequest
- type State
- type Status
- type Step
- type StepKind
- type StopReceipt
- type StopReport
- type Stopper
- type Stream
- type Submission
- type SubmissionRec
- type SubmitOption
- func AcceptUnenforced(rule int) SubmitOption
- func AcceptUnverified(rule int, enforcer Enforcer) SubmitOption
- func WithCleanupDeadline(d time.Duration) SubmitOption
- func WithIdempotencyKey(k string) SubmitOption
- func WithSubmitRetries(n int) SubmitOption
- func WithSupervisor() SubmitOption
- func WithUnmatched(p UnmatchedPolicy) SubmitOption
- func WithUnresolvedAge(d time.Duration) SubmitOption
- type SubmitOptions
- type Support
- type Tristate
- type UnmatchedPolicy
- type ValidationError
- type Watcher
Examples ¶
Constants ¶
const ( StatePending = model.StatePending StateHeld = model.StateHeld StateRunning = model.StateRunning StateRecovering = model.StateRecovering StateSucceeded = model.StateSucceeded StateFailed = model.StateFailed StateCancelled = model.StateCancelled StateUnknown = model.StateUnknown CauseNone = model.CauseNone CausePreempted = model.CausePreempted CauseNodeLost = model.CauseNodeLost CauseOutOfMemory = model.CauseOutOfMemory CauseTimeLimit = model.CauseTimeLimit CauseNonZeroExit = model.CauseNonZeroExit CauseLaunchFailed = model.CauseLaunchFailed CauseNoCapacity = model.CauseNoCapacity CauseHeld = model.CauseHeld CauseReleased = model.CauseReleased CauseRequeued = model.CauseRequeued CauseCancelled = model.CauseCancelled CauseDuplicate = model.CauseDuplicate CauseUnknown = model.CauseUnknown ProvenanceNone = model.ProvenanceNone ProvenanceScheduler = model.ProvenanceScheduler ProvenanceApplication = model.ProvenanceApplication ProvenanceActor = model.ProvenanceActor ActorUnset = model.ActorUnset ActorHuman = model.ActorHuman ActorSupervisor = model.ActorSupervisor ActorAdapter = model.ActorAdapter HoldUnset = model.HoldUnset HoldBackend = model.HoldBackend HoldRecovery = model.HoldRecovery ActionUnset = model.ActionUnset ActionResubmit = model.ActionResubmit ActionRestart = model.ActionRestart ActionRequeue = model.ActionRequeue ActionHold = model.ActionHold ActionEscalate = model.ActionEscalate EnforcerUnset = model.EnforcerUnset EnforcerBackend = model.EnforcerBackend EnforcerSupervisor = model.EnforcerSupervisor EnforcerNobody = model.EnforcerNobody CheckpointIsolated = model.CheckpointIsolated UnmatchedResidual = model.UnmatchedResidual UnmatchedManual = model.UnmatchedManual Unknown = model.Unknown Yes = model.Yes No = model.No SourceDeclared = model.SourceDeclared SourceProbed = model.SourceProbed SourceCatalogue = model.SourceCatalogue LevelJob = model.LevelJob LevelObjectOnly = model.LevelObjectOnly CleanupClear = model.CleanupClear CleanupPending = model.CleanupPending CleanupBlocked = model.CleanupBlocked ObjectGated = model.ObjectGated ObjectPending = model.ObjectPending ObjectHeld = model.ObjectHeld ObjectRunning = model.ObjectRunning ObjectEnded = model.ObjectEnded Live = model.Live EvidenceState = model.EvidenceState EvidenceCause = model.EvidenceCause EvidenceExitCode = model.EvidenceExitCode EvidenceExecution = model.EvidenceExecution ContainerImage = model.ContainerImage NativeRequeue = model.NativeRequeue NativeRestart = model.NativeRestart HoldCapability = model.HoldCapability PreTerminationSignal = model.PreTerminationSignal AccountingHistory = model.AccountingHistory AttemptInjection = model.AttemptInjection ReadmissionGate = model.ReadmissionGate WorkingDirectory = model.WorkingDirectory ProcsPerNode = model.ProcsPerNode OutputPath = model.OutputPath OutputRead = model.OutputRead OutputFollow = model.OutputFollow OutputRetained = model.OutputRetained )
The enum and capability values of model, re-exported so consumers import one package (spec 0001 D1).
const ( // UnresolvedMinAge is the default for how long a submission's attempts stay unknown before // they escalate. UnresolvedMinAge = 10 * time.Minute // CleanupDeadline is the default for how long clean-up may stay pending before it escalates. CleanupDeadline = 30 * time.Minute // CallDeadline is how far ahead a hold-family or requeue call's deadline is set. CallDeadline = 30 * time.Second )
Timing the protocol uses (spec 0004 D2, D4).
const CessationInterval = time.Minute
CessationInterval is how often a site's cessation signal is asked again while a shared- checkpoint hold stands.
const HoldBase = model.HoldBase
HoldBase is the offset of a Slurm hold's far-future start time (spec 0003 D9).
SharedHoldFor is the deadline of the hold a recovery rule becomes under a Shared checkpoint (spec 0001 D16). The hold is for a person; past it, it escalates and stays held.
const Unbounded = model.Unbounded
Unbounded is the PolledLoss window of evidence that polling never loses (spec 0003 D7).
Variables ¶
var ( ErrNotFound = model.ErrNotFound ErrUnsupported = model.ErrUnsupported ErrInvalid = model.ErrInvalid ErrRejected = model.ErrRejected ErrUnreachable = model.ErrUnreachable ErrReadOnly = model.ErrReadOnly ErrOutcomeUnknown = model.ErrOutcomeUnknown ErrAlreadyRegistered = model.ErrAlreadyRegistered )
The core's sentinels (spec 0001 D12).
Functions ¶
func FindAll ¶
func FindAll(ctx context.Context, b Backend, jobs []JobID, states ...ObjectState) (map[JobID]Discovery, error)
FindAll returns a Discovery for every job, keeping only objects in states when any are named: in one query when b is a MultiFinder, and otherwise by one Find per job.
func Register ¶
Register makes an adapter available to Open under name. A duplicate is ErrAlreadyRegistered, never an overwrite (spec 0001 D11).
func Watch ¶
Watch reports each state transition of a job, polling o every interval, until ctx ends; the channel is closed then. Transitions are the core's, from the record (spec 0001 D13). Without a store a job is never reported terminal, because an object's end is not the job's.
Watch is an observer API off the simulated path, and the one function there that starts a goroutine (spec 0005 D2 rule 1).
Types ¶
type AbandonTarget ¶
type AbandonTarget = model.AbandonTarget
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Action ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Actor ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type ActorKind ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type AdapterConfig ¶
type AdapterConfig struct {
Backend string
ReadOnly bool
// Settings is the adapter's own section, validated against its schema with
// additionalProperties false at every level.
Settings json.RawMessage
// Token is Config.Token: called on every use, never cached (spec 0001 D11).
Token func(ctx context.Context) (string, error)
}
AdapterConfig is what a Factory receives: no store and no job level (spec 0003 D13).
type AttemptRec ¶
type AttemptRec struct {
Number int
Reply records.Reply
Ref *ObjectRef
Matched bool
Abandoned bool
At time.Time
Detail string
}
AttemptRec is one Start call for a submission, numbered before the call (spec 0003 Notes).
func (*AttemptRec) Unresolved ¶
func (a *AttemptRec) Unresolved() bool
Unresolved reports whether the attempt's outcome is still unknown: no reply or an unknown one, no object matched to it, and not abandoned.
type Backend ¶
type Backend interface {
Capabilities(ctx context.Context, spec *JobSpec) (map[Capability]Support, error)
// Rules answers one RuleSupport per rule, in the policy's order.
Rules(ctx context.Context, spec JobSpec, policy RecoveryPolicy) ([]RuleSupport, error)
// Start creates the object with its gate closed; ErrOutcomeUnknown when it cannot tell.
Start(ctx context.Context, req StartRequest) (ObjectRef, error)
// Open lifts the gate; an object already past its gate is success.
Open(ctx context.Context, ref ObjectRef) error
// Find returns every object of the job, by its discovery name.
Find(ctx context.Context, job JobID) (Discovery, error)
Inspect(ctx context.Context, ref ObjectRef) (ObjectObs, error)
// Stop stops this one object only; an object already gone is success.
Stop(ctx context.Context, ref ObjectRef, reason string) error
// StopJob stops every object of the job by name, and verifies the result.
StopJob(ctx context.Context, job JobID, reason string) (StopReport, error)
Declarations() Declarations
Close() error
}
Backend is what an adapter implements: backend objects and their runs, never jobs or the record (spec 0003 D1). Optional capabilities are found by type assertion.
type Budgets ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Call ¶
type Call struct {
Ref ObjectRef
Instance uint32
Op records.CallOp
Deadline time.Time
Expires time.Time
}
Call is a recorded hold-family, requeue or restore call: its deadline and when it counts as expired.
type CallMeta ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type CancelReceipt ¶
type CancelReceipt struct {
Committed bool
// Already is the terminal state an earlier outcome left, when the cancel could not change it.
Already State
// CleanupComplete is set once Find shows nothing of the job's name running, pending or
// completing.
CleanupComplete bool
// Live names objects still stopping.
Live []ObjectRef
// CessationUnproven names nodes that were down or not responding, so the processes may live.
CessationUnproven []string
}
CancelReceipt reports a cancel's two halves separately: the commit, which is final, and backend clean-up, which may lag (spec 0004 D4).
type Capability ¶
type Capability = model.Capability
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Cause ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type CauseKind ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type CessationEvidence ¶
type CessationEvidence = model.CessationEvidence
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type CessationSignal ¶
type CessationSignal interface {
Ceased(ctx context.Context, ref ObjectRef) (CessationEvidence, error)
}
CessationSignal asks a site's own signal whether an object's processes are gone (spec 0003 D12).
type CheckpointMode ¶
type CheckpointMode = model.CheckpointMode
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type CleanupState ¶
type CleanupState = model.CleanupState
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Cluster ¶
type Cluster interface {
Observer() Observer
// Operator is nil when read-only or without a store.
Operator() Operator
// Stopper is nil only when read-only.
Stopper() Stopper
Close() error
}
Cluster is an opened adapter composed with the core (spec 0001 D6, D11).
func Compose ¶
Compose builds a Cluster over an already-built Backend, for hosts and tests that construct their adapter directly.
type Completeness ¶
type Completeness = model.Completeness
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Config ¶
type Config struct {
// Adapter is the adapter's own configuration, passed to its Factory as JSON.
Adapter any
// Token is called on every use; optional.
Token func(ctx context.Context) (string, error)
// Store nil allows read-only access only.
Store records.Records
ReadOnly bool
// Rand is the source of job identities; nil means crypto/rand (spec 0005 D2).
Rand io.Reader
}
Config is what Open needs (spec 0001 D11).
type Declarations ¶
type Declarations = model.Declarations
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Desired ¶
type Desired int
Desired is what the user wants: run, hold or cancel (spec 0002 appendix, control).
type Discovery ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Disposition ¶
type Disposition int
Disposition is a submission's standing: Open while it launches, Canonical once its handle is committed, Abandoned once a person closed it (spec 0002 appendix).
const ( DispositionOpen Disposition = iota DispositionCanonical DispositionAbandoned )
The dispositions.
type Enforcer ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Escalation ¶
Escalation is one escalation, identified by its cause so it is committed once (spec 0004 D5).
type Evidence ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type ExecutionObs ¶
type ExecutionObs = model.ExecutionObs
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Extension ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Extensions ¶
type Extensions = model.Extensions
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Factory ¶
type Factory func(ctx context.Context, cfg AdapterConfig) (Backend, error)
Factory builds an adapter's Backend.
type Filter ¶
type Filter struct {
// States keeps jobs in any of these states; empty keeps all.
States []State
// Settled includes records that have settled.
Settled bool
}
Filter selects jobs for List.
type Hold ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type HoldKind ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type HoldMeta ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type HoldRec ¶
type HoldRec struct {
Instance uint32
Hold Hold
// Cause is what ended the execution a recovery hold suspended, in Submission.
Cause Cause
Submission int
Active bool
Refused bool
Released time.Time
}
HoldRec is one hold instance (spec 0002 appendix, holds).
type Holder ¶
type Holder interface {
Hold(ctx context.Context, ref ObjectRef, m HoldMeta) error
// Release releases only the instance m names.
Release(ctx context.Context, ref ObjectRef, m HoldMeta) error
}
Holder places and releases backend holds (spec 0003 D9).
type IdentityWeakener ¶
type IdentityWeakener interface {
// IdentityWeakened says why spec's execution identity would be weakened, or "" when it would
// not be. MakePlan records it in Plan.IdentityWeakened and refuses nothing on it: the host shows it.
IdentityWeakened(ctx context.Context, spec JobSpec) (string, error)
}
IdentityWeakener is implemented by an adapter that, for some specs, lets the backend rerun a rank under the same execution identity, such as Kubeflow's site opt-in allowRankRestart. An adapter that does not implement it keeps every execution's identity its own.
type Image ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Job ¶
type Job struct {
Header records.Header
Revision uint64
Desired Desired
// Outcome is the terminal state committed, if any; terminal states never change.
Outcome *Outcome
Cancel *records.Control
Cleanup CleanupState
Settled bool
SettledAt time.Time
Submissions []*SubmissionRec
Objects []*ObjectRec
Holds []*HoldRec
Requeue *records.Control
RequeueCalls []Call
// RestoreCalls are restores of a requeue bit left set (spec 0003 D10).
RestoreCalls []Call
HoldCalls []Call
StopJobs []time.Time
Escalations []Escalation
// RuleUse counts the submissions authorized per rule, so budgets survive any actor.
RuleUse map[int]int
// Resubmits counts human Resubmit intents not yet authorized as a submission.
Resubmits int
// Resumed is the submission whose shared-checkpoint hold a cessation answer lifted, so its
// original rule now applies (spec 0004 D9); 0 when none.
Resumed int
// History is every state transition, in record order.
History []Event
}
Job is the record folded into the job level: everything Next and Project need, computed by Fold alone so every actor sees the same job from the same revision (spec 0001 D14).
func Fold ¶
Fold replays a record into the job level. It performs no I/O, and now is an argument, so deadlines such as Overdue are computed without a clock inside (spec 0001 D14). A history that cannot have been written by the protocol is refused with ErrInvalid, never folded silently.
func (*Job) ActiveHold ¶
ActiveHold returns the hold in force, or nil.
func (*Job) AllRequeueClear ¶
AllRequeueClear is RequeueClear over every object of the job.
func (*Job) HoldCallsClear ¶
HoldCallsClear is when every recorded hold-family call on ref has landed or expired.
func (*Job) HoldMeta ¶
HoldMeta returns the meta of a call on hold instance, carrying who placed it, until when and why from the record's hold control (spec 0003 D9). An instance the record does not hold carries only the instance and m.
func (*Job) Latest ¶
func (j *Job) Latest() *SubmissionRec
Latest returns the latest submission, or nil before the first.
func (*Job) RequeueClear ¶
RequeueClear is when every recorded requeue call on ref has landed or expired: the greatest expiry recorded for it (spec 0004 D6). The zero time means none was recorded.
func (*Job) Residuals ¶
func (j *Job) Residuals() []model.AbandonTarget
Residuals are the unmatched attempts the host keeps sweeping after the record settles, under the Residual policy (spec 0002 appendix).
func (*Job) RestoreClear ¶
RestoreClear is when every recorded restore call on ref has landed or expired.
func (*Job) Submission ¶
func (j *Job) Submission(n int) *SubmissionRec
Submission returns submission n, or nil.
type JobID ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type JobSpec ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Level ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type MultiFinder ¶
type MultiFinder interface {
// FindMany answers a Discovery for every job asked, keeping only objects in states when any
// are named.
FindMany(ctx context.Context, jobs []JobID, states ...ObjectState) (map[JobID]Discovery, error)
}
MultiFinder finds the objects of many jobs in one scheduler query, as Find does for one; FindAll uses it when the adapter has it. dunscaith's residual sweep is its consumer (dunscaith 0001 D8).
type NativeOffer ¶
type NativeOffer = model.NativeOffer
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type NativePolicyCompiler ¶
type NativePolicyCompiler interface {
Native(ctx context.Context, spec JobSpec, policy RecoveryPolicy) ([]Support, error)
Compile(ctx context.Context, spec JobSpec, policy RecoveryPolicy, remaining Budgets) (NativeOffer, error)
}
NativePolicyCompiler offers rules the backend would enforce itself (spec 0003 D3).
type ObjectObs ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type ObjectRec ¶
type ObjectRec struct {
Ref ObjectRef
Submission int
Attempt int
Foreign bool
Disposed bool
Retired bool
// Opening is set once an Open was decided for it; EverOpened once it was seen past its gate.
Opening bool
EverOpened bool
Ended bool
EndedAt time.Time
// VerifiedStopped is the reconciler's judgement from absence (spec 0002 appendix).
VerifiedStopped bool
StoppedAt time.Time
SeenInFind bool
Last *ObjectObs
// NodeLostSince is when the object was first seen running with a lost node.
NodeLostSince time.Time
// Ceased is the site cessation signal's latest answer, and CessationAt when it was recorded.
Ceased bool
CessationAt time.Time
}
ObjectRec is one scheduler object ever recorded for the job (spec 0002 appendix, objects).
type ObjectRef ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type ObjectState ¶
type ObjectState = model.ObjectState
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Observer ¶
type Observer interface {
Capabilities(ctx context.Context, spec *JobSpec) (map[Capability]Support, error)
Get(ctx context.Context, id JobID) (Status, error)
List(ctx context.Context, f Filter) ([]Status, error)
}
Observer reads jobs (spec 0001 D6).
type Operator ¶
type Operator interface {
Submit(ctx context.Context, spec JobSpec, by Actor, opts ...SubmitOption) (Plan, error)
// Cancel commits first, then stops; its receipt separates the two.
Cancel(ctx context.Context, id JobID, by Actor, reason string) (CancelReceipt, error)
Hold(ctx context.Context, id JobID, by Actor, reason string, until time.Time) error
Release(ctx context.Context, id JobID, by Actor) error
// Resubmit creates a new submission of the same job.
Resubmit(ctx context.Context, id JobID, by Actor, reason string) error
// Supersede creates a new job whose Supersedes names id.
Supersede(ctx context.Context, id JobID, by Actor, reason string, opts ...SubmitOption) (Plan, error)
GiveUp(ctx context.Context, id JobID, by Actor, reason string) error
Abandon(ctx context.Context, id JobID, t AbandonTarget, by Actor, reason string) error
// Requeue commits a revocable intent; ErrUnsupported without a Requeuer.
Requeue(ctx context.Context, id JobID, by Actor, reason string) error
}
Operator acts on jobs; every action names its actor and is recorded (spec 0001 D6). There is no Operator without a store.
type Output ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type OutputReader ¶
type OutputReader interface {
Output(ctx context.Context, id JobID, submission int, s Stream, follow bool) (io.ReadCloser, error)
}
OutputReader reads a submission's output where the cluster keeps it: a capability (OutputRead, OutputFollow, OutputRetained), never a Status field, found on a Cluster's Observer by type assertion. A cluster that cannot read output returns an error, which is what makes those capabilities visible on failure (spec 0001 D7, D15).
type Plan ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
func MakePlan ¶
func MakePlan(ctx context.Context, b Backend, job JobID, spec JobSpec, opts ...SubmitOption) (Plan, error)
MakePlan checks spec against what b supports and assigns every rule an enforcer, in spec 0001 D9's order, before anything is created. It is Submit's decision, separate so hosts can show a plan without submitting.
Example ¶
MakePlan shows the plan Submit would record, without creating anything. Here nobody would enforce the rule, so the plan is refused until the host says it will supervise the job.
package main
import (
"context"
"errors"
"fmt"
"gitlab.com/phpboyscout/go/dispatch"
"gitlab.com/phpboyscout/go/dispatch/memory"
)
func main() {
ctx := context.Background()
backend, err := memory.New(memory.Config{Shape: memory.ShapeSlurm})
if err != nil {
fmt.Println(err)
return
}
spec := dispatch.JobSpec{
Command: []string{"torchrun", "train.py"},
Nodes: 4,
Recovery: dispatch.RecoveryPolicy{Rules: []dispatch.Rule{
{On: []dispatch.CauseKind{dispatch.CausePreempted, dispatch.CauseNodeLost}, Action: dispatch.ActionResubmit, Budget: 3},
}},
}
_, err = dispatch.MakePlan(ctx, backend, "", spec)
fmt.Println(errors.Is(err, dispatch.ErrRejected), err)
plan, err := dispatch.MakePlan(ctx, backend, "", spec, dispatch.WithSupervisor())
if err != nil {
fmt.Println(err)
return
}
for _, a := range plan.Rules {
fmt.Println(a.Index, a.Effective.Action, a.Effective.On, "enforced by the", a.Enforcer)
}
fmt.Println("checkpoint:", plan.Checkpoint, "unmatched:", plan.Unmatched)
}
Output: true dispatch: rejected: nobody would enforce rule 0; supervise the job or AcceptUnenforced it 0 resubmit [preempted node-lost] enforced by the supervisor checkpoint: isolated unmatched: residual
type Provenance ¶
type Provenance = model.Provenance
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type RecoveryPolicy ¶
type RecoveryPolicy = model.RecoveryPolicy
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Requeuer ¶
type Requeuer interface {
Requeue(ctx context.Context, ref ObjectRef, m CallMeta) error
// RestoreRequeueBit clears a requeue bit left set outside Requeue, by a crash between its
// steps or by an operator; on Slurm it is `scontrol update Requeue=0`.
RestoreRequeueBit(ctx context.Context, ref ObjectRef, m CallMeta) error
}
Requeuer re-runs the same object, under a host-checked deadline (spec 0003 D10).
type Rule ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type RuleAssignment ¶
type RuleAssignment = model.RuleAssignment
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type RuleSupport ¶
type RuleSupport = model.RuleSupport
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type SharedCheckpoint ¶
type SharedCheckpoint = model.SharedCheckpoint
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Source ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type StartRequest ¶
type StartRequest = model.StartRequest
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type State ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Status ¶
type Status struct {
Job JobID
Name string
Level Level
State State
Cause Cause
Unattended bool
Cleanup CleanupState
Hold *Hold
Submissions []Submission
History []Event
Plan *Plan
Supersedes JobID
}
Status is a job as the core computes it from the record and what the backend shows (spec 0001 D4).
func Project ¶
Project is the job as status reports it: the folded record plus what the backend shows now (spec 0001 D4, D14). It performs no I/O.
func (Status) IdentityWeakened ¶
IdentityWeakened is the plan's reason the job's execution identity is weakened, or "" when it is not or there is no plan. A host shows it; the core does not act on it.
type Step ¶
type Step struct {
Kind StepKind
Submission int
Attempt int
TriggerKey string
Rule int
Ref ObjectRef
Obs *ObjectObs
Outcome State
Cause Cause
Escalation string
Detail string
// Hold is set for a recovery hold placed by StepPlaceHold; a backend hold names Instance.
Hold *Hold
Instance uint32
Op records.CallOp
// Deadline and Expires are a hold-family, requeue or restore call's, committed before it is issued.
Deadline time.Time
Expires time.Time
// FindAt is the Find a verified-stopped judgement rests on.
FindAt time.Time
// Wake is when a StepWait should next look.
Wake time.Time
}
Step is one thing to do. Only the fields its kind uses are set.
type StepKind ¶
type StepKind int
StepKind is one of the closed set of things Next can ask for (spec 0001 D14). The supervisor executes each through spec 0004 D2 to D6.
const ( StepWait StepKind = iota // StepObserve commits what the backend showed, as evidence. StepObserve // StepAuthorize commits submission N and its budget reservation. StepAuthorize // StepStart commits the attempt, calls Start with the gate closed, and records the reply. StepStart StepCanonical // StepOpen commits the decision to open, then lifts the gate. StepOpen StepRetire StepPlaceHold StepReleaseHold StepRequeue StepStop StepStopJob StepVerifiedStopped StepOutcome StepEscalate // StepSettle commits that the settled predicate holds. StepSettle // StepCease asks the adapter's cessation signal about an object and records the answer. StepCease // StepRestoreRequeue commits a restore call's deadline, then clears a requeue bit left set // outside Requeue (spec 0003 D10). StepRestoreRequeue )
The steps.
type StopReceipt ¶
type StopReceipt struct {
Report StopReport
}
StopReceipt is the emergency stop's result. The cancellation is not recorded, and recovery may still occur (spec 0001 D6).
type StopReport ¶
type StopReport = model.StopReport
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Stopper ¶
type Stopper interface {
EmergencyStop(ctx context.Context, id JobID, by Actor, reason string) (StopReceipt, error)
}
Stopper is the emergency stop, which needs no record (spec 0001 D6).
type Submission ¶
type Submission struct {
N int
Canonical *ObjectRef
Executions []ExecutionObs
Abandoned bool
}
Submission is submission N as status reports it: its canonical object, if committed, and the executions seen.
type SubmissionRec ¶
type SubmissionRec struct {
N int
TriggerKey string
Rule int
Disposition Disposition
Attempts []*AttemptRec
Canonical *ObjectRef
AuthorizedAt time.Time
}
SubmissionRec is submission N: its budget reservation, its attempts and its canonical handle.
type SubmitOption ¶
type SubmitOption func(*SubmitOptions)
SubmitOption configures Submit and Supersede.
func AcceptUnenforced ¶
func AcceptUnenforced(rule int) SubmitOption
AcceptUnenforced accepts that nobody enforces rule (by its index), so a matching cause gives Failed with an escalation (spec 0001 D9 step 5).
func AcceptUnverified ¶
func AcceptUnverified(rule int, enforcer Enforcer) SubmitOption
AcceptUnverified accepts rule (by its index in the policy) whose Perform support is Unknown, naming who will enforce it (spec 0001 D9 step 2).
func WithCleanupDeadline ¶
func WithCleanupDeadline(d time.Duration) SubmitOption
WithCleanupDeadline sets how long clean-up may stay pending before it escalates (spec 0004 D4).
func WithIdempotencyKey ¶
func WithIdempotencyKey(k string) SubmitOption
WithIdempotencyKey makes a repeated Submit return the existing job's plan (spec 0001 D10).
func WithSubmitRetries ¶
func WithSubmitRetries(n int) SubmitOption
WithSubmitRetries sets how often an ambiguous submit is retried; 0 disables retries (spec 0004 D3). It is recorded with the job, so every actor uses it.
func WithSupervisor ¶
func WithSupervisor() SubmitOption
WithSupervisor says the host will supervise this job, so Supervisor may enforce its rules (spec 0001 D9).
func WithUnmatched ¶
func WithUnmatched(p UnmatchedPolicy) SubmitOption
WithUnmatched chooses how an attempt whose outcome stayed unknown affects settlement; the default is Residual (spec 0004 D2).
func WithUnresolvedAge ¶
func WithUnresolvedAge(d time.Duration) SubmitOption
WithUnresolvedAge sets how long a submission's attempts may stay unknown before they escalate (spec 0004 D2).
type SubmitOptions ¶
type SubmitOptions struct {
Supervised bool
IdempotencyKey string
Unmatched UnmatchedPolicy
AcceptUnverified map[int]Enforcer
AcceptUnenforced map[int]bool
// SubmitRetries is how often an ambiguous submit is retried; nil means the default (2), and 0
// disables retries (spec 0004 D3).
SubmitRetries *int
// CleanupDeadline and UnresolvedAge override the defaults of 30 and 10 minutes (spec 0004 D2,
// D4).
CleanupDeadline time.Duration
UnresolvedAge time.Duration
}
SubmitOptions is what the options set; adapters and hosts read it through ApplySubmitOptions.
func ApplySubmitOptions ¶
func ApplySubmitOptions(opts ...SubmitOption) SubmitOptions
ApplySubmitOptions folds opts into their settings.
type Support ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type Tristate ¶
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type UnmatchedPolicy ¶
type UnmatchedPolicy = model.UnmatchedPolicy
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
type ValidationError ¶
type ValidationError = model.ValidationError
The value types live in model so dispatch/records can store them without a cycle (spec 0001 D1); they are re-exported here so consumers import one package.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package checkpoint publishes and resolves training checkpoints under one checkpoint root, on a POSIX filesystem reached through an afero.Fs.
|
Package checkpoint publishes and resolves training checkpoints under one checkpoint root, on a POSIX filesystem reached through an afero.Fs. |
|
creds
Package creds resolves the S3 credentials the checkpoint-publication helper signs with, from an ordered list of credential sources (spec 0001 D16).
|
Package creds resolves the S3 credentials the checkpoint-publication helper signs with, from an ordered list of credential sources (spec 0001 D16). |
|
creds/vault
Package vault is the vault credential-source kind (spec 0001 D16): S3 credentials read from a Vault KV v2 secret with go/config-vault, through a client from go/vaultclient, after logging in with Kubernetes auth or AppRole.
|
Package vault is the vault credential-source kind (spec 0001 D16): S3 credentials read from a Vault KV v2 secret with go/config-vault, through a client from go/vaultclient, after logging in with Kubernetes auth or AppRole. |
|
proxy
Package proxy is the one rule for the HTTP proxy a checkpoint-publication client may be told to use (spec 0001 D16).
|
Package proxy is the one rule for the HTTP proxy a checkpoint-publication client may be told to use (spec 0001 D16). |
|
s3
Package s3 is the checkpoint publication log on S3-compatible object storage (spec 0001 D16).
|
Package s3 is the checkpoint publication log on S3-compatible object storage (spec 0001 D16). |
|
s3/s3fake
Package s3fake is an in-process S3 server for tests: just the calls checkpoint publication makes (a put that honours If-None-Match: *, GET, ListObjectsV2 with prefix, delimiter and continuation, HEAD, DELETE), each refused unless its SigV4 signature verifies.
|
Package s3fake is an in-process S3 server for tests: just the calls checkpoint publication makes (a put that honours If-None-Match: *, GET, ListObjectsV2 with prefix, delimiter and continuation, HEAD, DELETE), each refused unless its SigV4 signature verifies. |
|
cmd
|
|
|
publish
command
Command publish is the checkpoint-publication helper a job script runs on the compute node (spec 0001 D16): resolve before training, watch beside it, publish a save explicitly, prune, and probe an S3 store before a job is accepted.
|
Command publish is the checkpoint-publication helper a job script runs on the compute node (spec 0001 D16): resolve before training, watch beside it, publish a save explicitly, prune, and probe an S3 store before a job is accepted. |
|
Package conformance is the backend conformance suite: what every dispatch adapter must show at object level, checked through the SPI alone (spec 0003 D11).
|
Package conformance is the backend conformance suite: what every dispatch adapter must show at object level, checked through the SPI alone (spec 0003 D11). |
|
internal
|
|
|
mutants
Package mutants holds the knobs that each revert one fix the model checks found, so the simulator can show it finds that bug again (spec 0005 D9).
|
Package mutants holds the knobs that each revert one fix the model checks found, so the simulator can show it finds that bug again (spec 0005 D9). |
|
proxytest
Package proxytest is a forwarding HTTP proxy for tests, which records every request it carries.
|
Package proxytest is a forwarding HTTP proxy for tests, which records every request it carries. |
|
sim
Package sim is the deterministic simulator that gates spec 0004 (spec 0005).
|
Package sim is the deterministic simulator that gates spec 0004 (spec 0005). |
|
sim/crashfs
Package crashfs is the simulator's crash-model filesystem (spec 0005 D3): an afero.Fs over afero's in-memory filesystem that refuses what POSIX refuses, makes file data and directory entries durable only when they are synced, and models a process crash and a power cut.
|
Package crashfs is the simulator's crash-model filesystem (spec 0005 D3): an afero.Fs over afero's in-memory filesystem that refuses what POSIX refuses, makes file data and directory entries durable only when they are synced, and models a process crash and a power cut. |
|
sim/lint
Package lint checks the mechanical rules of spec 0005 D2 over the code on the simulated path: rule 1 (no goroutines started), rule 3 (no global random source, no package-level channel or WaitGroup), rule 4 (map ranges sorted or annotated) and rule 6's clauses on context.Background and context.WithoutCancel.
|
Package lint checks the mechanical rules of spec 0005 D2 over the code on the simulated path: rule 1 (no goroutines started), rule 3 (no global random source, no package-level channel or WaitGroup), rule 4 (map ranges sorted or annotated) and rule 6's clauses on context.Background and context.WithoutCancel. |
|
sim/locktable
Package locktable is the simulator's lock table (spec 0005 D3): the file store's Locker, with one view per simulated process, so that the simulator decides whose locks a crash releases, can grant owner.lock to several supervisors at once, and can revoke it from a live holder.
|
Package locktable is the simulator's lock table (spec 0005 D3): the file store's Locker, with one view per simulated process, so that the simulator decides whose locks a crash releases, can grant owner.lock to several supervisors at once, and can revoke it from a live holder. |
|
Package memory is an in-memory dispatch adapter in two shapes, Slurm and Kubeflow, that implements the whole SPI, including all five optional interfaces (spec 0003 D11).
|
Package memory is an in-memory dispatch adapter in two shapes, Slurm and Kubeflow, that implements the whole SPI, including all five optional interfaces (spec 0003 D11). |
|
Package model holds go/dispatch's value types: the job vocabulary, states and causes, holds, recovery policy and plans, actors, capabilities, and the adapter SPI's value types.
|
Package model holds go/dispatch's value types: the job vocabulary, states and causes, holds, recovery policy and plans, actors, capabilities, and the adapter SPI's value types. |
|
Package records is go/dispatch's typed view of a job record held in go/ledger (spec 0002, appendix).
|
Package records is go/dispatch's typed view of a job record held in go/ledger (spec 0002, appendix). |
|
ledgerstore
Package ledgerstore is records.Records over a go/ledger store: dispatch's typed record on the store that keeps state opaque (spec 0002 appendix).
|
Package ledgerstore is records.Records over a go/ledger store: dispatch's typed record on the store that keeps state opaque (spec 0002 appendix). |
|
recordstest
Package recordstest is an in-memory records.Records with ownership, for tests and for the simulator's early stages before the go/ledger file store drives it.
|
Package recordstest is an in-memory records.Records with ownership, for tests and for the simulator's early stages before the go/ledger file store drives it. |
|
Package supervise is the supervisor: it executes what dispatch.Next says should happen next, through conditional commits and gated launches (spec 0004).
|
Package supervise is the supervisor: it executes what dispatch.Next says should happen next, through conditional commits and gated launches (spec 0004). |