Documentation
¶
Overview ¶
Package engine wires all Dispatch subsystems together and provides the primary application-level API for registering and enqueuing work.
The engine package exists to break a fundamental import cycle: the root dispatch package defines Entity (imported by job, workflow, cron, etc.) and therefore cannot import those packages back. Engine sits above all subsystem packages and below the application layer.
Building an Engine ¶
d, err := dispatch.New(
dispatch.WithStore(pgStore),
dispatch.WithConcurrency(20),
)
eng := engine.Build(d,
engine.WithExtension(myExtension),
engine.WithMiddleware(middleware.Logging(logger)),
engine.WithBackoff(backoff.Exponential()),
engine.WithQueueConfig(queue.Config{
Name: "critical",
RateLimit: 100,
}),
)
Registering Work ¶
// Jobs
engine.Register(eng, SendEmail)
// Workflows
engine.RegisterWorkflow(eng, ProcessOrder)
// Crons
engine.RegisterCron(ctx, eng, "daily-report", "0 9 * * *", GenerateReport, ReportInput{})
Enqueuing Jobs ¶
engine.Enqueue(ctx, eng, SendEmail, EmailInput{To: "user@example.com"})
// With options
engine.EnqueueAt(ctx, eng, SendEmail, input, time.Now().Add(5*time.Minute))
engine.EnqueueWithPriority(ctx, eng, SendEmail, input, 10)
Options ¶
- WithExtension — register a lifecycle extension
- WithMiddleware — add a middleware to the execution chain
- WithBackoff — set the retry backoff strategy
- WithQueueConfig — configure per-queue rate limits and concurrency
- WithTracerProvider — set the OpenTelemetry tracer provider
- WithMetricFactory — set the metrics factory
Package engine wires all Dispatch subsystems together. It creates the extension registry, job registry, middleware chain, worker pool, and provides Register/Enqueue operations.
This package exists to break the import cycle: the root dispatch package defines Entity (imported by job, workflow, etc.) and so cannot import those packages back. The engine package sits above all subsystem packages and below the application layer.
Index ¶
- Variables
- func Bind(name string, ref artifact.Ref) job.Option
- func Enqueue[T any](ctx context.Context, eng *Engine, name string, payload T, opts ...job.Option) (*job.Job, error)
- func Register[T any](eng *Engine, def *job.Definition[T])
- func RegisterAll(eng *Engine, defs ...job.Registrable) error
- func RegisterChecked[T any](eng *Engine, def *job.Definition[T]) error
- func RegisterCron[T any](ctx context.Context, eng *Engine, def *cron.Definition[T]) error
- func RegisterWorkflow[T any](eng *Engine, def *workflow.Definition[T])
- func StartWorkflow[T any](ctx context.Context, eng *Engine, name string, input T) (*workflow.Run, error)
- type Engine
- func (eng *Engine) ArtifactCache() *cache.Cache
- func (eng *Engine) Artifacts() *artifact.Service
- func (eng *Engine) BeginDurableDrain(ctx context.Context, request drt.DrainRequest) (drt.DrainHandle, error)
- func (eng *Engine) CancelJob(ctx context.Context, jobID id.JobID) (*job.Job, error)
- func (eng *Engine) ClusterStore() cluster.Store
- func (eng *Engine) CompleteDurableActivity(ctx context.Context, request drt.AsyncCompletionRequest) (durable.Receipt, error)
- func (eng *Engine) CountDLQPurge(ctx context.Context, before time.Time) (int64, error)
- func (eng *Engine) CronStore() cron.Store
- func (eng *Engine) DLQService() *dlq.Service
- func (eng *Engine) DeleteCron(ctx context.Context, cronID id.CronID) error
- func (eng *Engine) DeleteDLQ(ctx context.Context, entryID id.DLQID) error
- func (eng *Engine) DisableCron(ctx context.Context, cronID id.CronID) (*cron.Entry, error)
- func (eng *Engine) Dispatcher() *dispatch.Dispatcher
- func (eng *Engine) DurableReadiness(ctx context.Context) (drt.WorkerReadiness, error)
- func (eng *Engine) DurableStatus() (drt.WorkerStatus, error)
- func (eng *Engine) DurableWorker() *drt.Worker
- func (eng *Engine) EnableCron(ctx context.Context, cronID id.CronID) (*cron.Entry, error)
- func (eng *Engine) EnqueueRaw(ctx context.Context, name string, payload []byte, opts ...job.Option) (*job.Job, error)
- func (eng *Engine) EventBus() *event.Bus
- func (eng *Engine) Executors() *exec.Registry
- func (eng *Engine) Extensions() *ext.Registry
- func (eng *Engine) Health(ctx context.Context) error
- func (eng *Engine) HeartbeatDurableActivity(ctx context.Context, request drt.AsyncHeartbeatRequest) (durable.Receipt, error)
- func (eng *Engine) Inspect() Inspection
- func (eng *Engine) MaxWorkerCapacity(ctx context.Context) resource.Set
- func (eng *Engine) PlanWorkflowReplay(ctx context.Context, runID id.RunID, fromStep string) (*workflow.ReplayPlan, error)
- func (eng *Engine) PurgeDLQ(ctx context.Context, before time.Time) (int64, error)
- func (eng *Engine) QueryDurableWorkflow(ctx context.Context, request drt.QueryRequest) (drt.QueryResult, error)
- func (eng *Engine) QueueManager() *queue.Manager
- func (eng *Engine) Registry() *job.Registry
- func (eng *Engine) ReplayAllDLQ(ctx context.Context, opts ReplayAllOpts) (ReplayAllResult, error)
- func (eng *Engine) ReplayDLQ(ctx context.Context, entryID id.DLQID) (*job.Job, error)
- func (eng *Engine) ReplayWorkflowFrom(ctx context.Context, runID id.RunID, fromStep string) (*workflow.ReplayPlan, error)
- func (eng *Engine) ReplayWorkflowFromGeneration(ctx context.Context, runID id.RunID, fromStep string, generation int64) (*workflow.ReplayPlan, error)
- func (eng *Engine) RequestCancelDurableWorkflow(ctx context.Context, request durable.CancelExecutionRequest) (durable.CancelExecutionReceipt, error)
- func (eng *Engine) Resources() resource.Manager
- func (eng *Engine) RetryJob(ctx context.Context, jobID id.JobID) (*job.Job, error)
- func (eng *Engine) Scheduler() *cron.Scheduler
- func (eng *Engine) SignalDurableWorkflow(ctx context.Context, request durable.SignalRequest) (durable.SignalReceipt, error)
- func (eng *Engine) SignalWithStartDurableWorkflow(ctx context.Context, request durable.SignalWithStartRequest) (durable.SignalReceipt, error)
- func (eng *Engine) Start(ctx context.Context) (startErr error)
- func (eng *Engine) StartDurableWorkflow(ctx context.Context, request durable.StartRequest) (durable.Receipt, error)
- func (eng *Engine) Stop(ctx context.Context) error
- func (eng *Engine) StopWorkers(ctx context.Context) error
- func (eng *Engine) StreamBroker() *stream.Broker
- func (eng *Engine) Telemetry() *telemetry.Telemetry
- func (eng *Engine) TriggerCron(ctx context.Context, cronID id.CronID) (*job.Job, error)
- func (eng *Engine) ValidateArtifactInputs(name string, specs []artifact.InputSpec) error
- func (eng *Engine) WaitDurableDrain(ctx context.Context, handle drt.DrainHandle) (drt.DrainResult, error)
- func (eng *Engine) WorkerID() id.WorkerID
- func (eng *Engine) WorkflowRunner() *workflow.Runner
- type Inspection
- type Option
- func WithArtifacts(svc *artifact.Service, c *cache.Cache) Option
- func WithBackoff(b backoff.Strategy) Option
- func WithDurableWorkflows(options drt.Options) Option
- func WithEstimator(e resource.Estimator) Option
- func WithExecutor(e exec.Executor) Option
- func WithExtension(e ext.Extension) Option
- func WithMetricFactory(factory gu.MetricFactory) Option
- func WithMiddleware(m mw.Middleware) Option
- func WithQueueConfig(configs ...queue.Config) Option
- func WithResourceDefaults(global resource.Set, perQueue map[string]resource.Set) Option
- func WithResourceManager(m resource.Manager) Option
- func WithScratchRoot(dir string) Option
- func WithStreamBroker(opts ...stream.BrokerOption) Option
- func WithTracerProvider(tp trace.TracerProvider) Option
- func WithWorkerCapacity(c resource.Set) Option
- func WithWorkerCustomKeys(keys []string) Option
- type ReplayAllOpts
- type ReplayAllResult
Constants ¶
This section is empty.
Variables ¶
var ( ErrDurableUnsupported = errors.New("dispatch: store does not implement durable.Store") ErrDurableDisabled = errors.New("dispatch: durable workflows are not enabled") )
Durable configuration errors prevent fallback to checkpoint execution.
Functions ¶
func Bind ¶
Bind attaches an artifact to a declared input at enqueue.
Bindings are validated against the job's declarations before the job is persisted, so an oversized or undeclared input is a caller error rather than a job that fails on a worker.
func Enqueue ¶
func Enqueue[T any](ctx context.Context, eng *Engine, name string, payload T, opts ...job.Option) (*job.Job, error)
Enqueue creates and enqueues a job.
func Register ¶
func Register[T any](eng *Engine, def *job.Definition[T])
Register registers a typed job definition with the engine.
Use RegisterChecked when the definition declares artifact inputs, or an execution policy (job.WithExecution), and you want either validated — the staging budget for the former, and for the latter, that some configured executor can actually satisfy it. Register itself performs neither check: a definition declaring exec.Isolate(exec.LevelProcess) with no rung configured to provide it registers cleanly here and only fails the first time a worker actually tries to run it, which is exactly the startup-error-versus-silently-hung-job choice RegisterChecked exists to take out of a caller's hands.
func RegisterAll ¶
func RegisterAll(eng *Engine, defs ...job.Registrable) error
RegisterAll registers a set of definitions.
It takes job.Registrable rather than a typed definition so a single handler list can be shared between the worker and an out-of-process entrypoint, which cannot be handed an engine.
func RegisterChecked ¶
func RegisterChecked[T any](eng *Engine, def *job.Definition[T]) error
RegisterChecked registers a definition and validates its artifact declarations and execution policy, so a job that could never be staged or could never be isolated as it requires fails here — at registration, on a developer's machine — rather than on every worker that picks it up.
func RegisterCron ¶
RegisterCron registers a typed cron definition with the engine. It validates the schedule expression, computes the initial NextRunAt, and persists the entry. Re-registration of the same name is idempotent.
func RegisterWorkflow ¶
func RegisterWorkflow[T any](eng *Engine, def *workflow.Definition[T])
RegisterWorkflow registers a typed workflow definition with the engine.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine wraps a Dispatcher with typed subsystem access. Use Build() to create one from a Dispatcher.
func Build ¶
func Build(d *dispatch.Dispatcher, opts ...Option) (*Engine, error)
Build creates an Engine from an existing Dispatcher. The Dispatcher's store must implement job.Store.
func (*Engine) ArtifactCache ¶
ArtifactCache returns the staging cache, or nil when the plane is off.
func (*Engine) BeginDurableDrain ¶
func (eng *Engine) BeginDurableDrain(ctx context.Context, request drt.DrainRequest) (drt.DrainHandle, error)
BeginDurableDrain closes polling with an explicit operation-owned deadline. Publisher delivery, authorized queries and storage remain available.
func (*Engine) CancelJob ¶
CancelJob moves a pending, retrying or running job to cancelled.
A pending or retrying job is cancelled on the spot and JobCancelled fires before CancelJob returns. A running job is written cancelled while its worker still holds it, and the worker finds out through the lease: its next RenewLease, or its terminal write, comes back job.ErrLeaseLost because the row is no longer running. The pool then cancels the handler's context and the runner reports JobCancelled instead of JobFailed once it reads the row and sees why. Any other state wraps dispatch.ErrInvalidState.
The running write is fenced on the holder and epoch read here, through job.LeaseStore.UpdateLeasedJob, rather than a plain UpdateJob. A plain write landing just after the worker completed the job would overwrite completed with cancelled. The fenced one is refused instead, and CancelJob reads the row again and decides on what it finds. A store without the lease capability, or a running job claimed without a lease, falls back to UpdateJob: there is no fence to write through.
The returned job is the row as written. One OperatorAction of kind ext.ActionJobCancelled is emitted on success.
func (*Engine) ClusterStore ¶
ClusterStore returns the cluster store for worker management.
func (*Engine) CompleteDurableActivity ¶
func (eng *Engine) CompleteDurableActivity(ctx context.Context, request drt.AsyncCompletionRequest) (durable.Receipt, error)
CompleteDurableActivity publishes an asynchronous result or failure. Authorize callers before exposing this trusted Go API through a remote endpoint.
func (*Engine) CountDLQPurge ¶
CountDLQPurge is PurgeDLQ's dry run: how many entries a purge with the same before would delete, counted on the same boundary. It changes nothing and emits nothing. A zero before is refused, as PurgeDLQ refuses it.
func (*Engine) DLQService ¶
DLQService returns the engine's DLQ service for replay and inspection.
func (*Engine) DeleteCron ¶
DeleteCron removes a cron entry. dispatch.ErrCronNotFound when it does not exist.
func (*Engine) DeleteDLQ ¶
DeleteDLQ removes one dead letter entry for good. Unknown entries return dispatch.ErrDLQNotFound. One OperatorAction of kind ext.ActionDLQDeleted is emitted on success.
func (*Engine) DisableCron ¶
DisableCron turns a cron entry off and returns it as stored. Its next_run_at is left as it was. dispatch.ErrCronNotFound when the entry does not exist.
func (*Engine) Dispatcher ¶
func (eng *Engine) Dispatcher() *dispatch.Dispatcher
Dispatcher returns the underlying Dispatcher.
func (*Engine) DurableReadiness ¶
DurableReadiness reports polling and persisted deployment compatibility.
func (*Engine) DurableStatus ¶
func (eng *Engine) DurableStatus() (drt.WorkerStatus, error)
DurableStatus reports worker readiness independently of Health's database ping.
func (*Engine) DurableWorker ¶
DurableWorker returns the configured runtime, or nil when disabled. Engine owns its polling lifecycle.
func (*Engine) EnableCron ¶
EnableCron turns a cron entry on and returns it as stored.
The next fire time is computed from now, so an entry that was off past its old next_run_at does not fire a catch-up the moment it comes back. For an @every schedule that restarts the interval, and enabling an entry that is already on does the same. A schedule with no fire time in the next five years (30 February) is refused with a wrapped dispatch.ErrInvalidState, since the scheduler would have nothing to wait for. dispatch.ErrCronNotFound when the entry does not exist.
func (*Engine) EnqueueRaw ¶
func (eng *Engine) EnqueueRaw(ctx context.Context, name string, payload []byte, opts ...job.Option) (*job.Job, error)
EnqueueRaw enqueues a job with a pre-serialized payload.
func (*Engine) Extensions ¶
Extensions returns the extension registry.
func (*Engine) Health ¶
Health checks the health of the engine by pinging the dispatcher's store. It also fails while the durable worker has stopped or its claims go unanswered, since durable work isn't being picked up then.
func (*Engine) HeartbeatDurableActivity ¶
func (eng *Engine) HeartbeatDurableActivity(ctx context.Context, request drt.AsyncHeartbeatRequest) (durable.Receipt, error)
HeartbeatDurableActivity persists progress for a deferred activity. The caller supplies a stable request ID and the next consecutive heartbeat sequence.
func (*Engine) Inspect ¶
func (eng *Engine) Inspect() Inspection
Inspect returns detached configuration and effective subsystem settings.
func (*Engine) MaxWorkerCapacity ¶
MaxWorkerCapacity returns the per-key maximum capacity the unschedulable check may compare a job against, or an empty Set when the check is off.
An empty result disables the check rather than rejecting everything.
It is empty unless an operator called WithWorkerCapacity, and that gate is the whole correctness argument. The registry cannot supply the fleet maximum on its own: a worker's row carries only the capacity that worker declared through WithWorkerCapacity (and the k8s provider does not persist it at all), so a fleet whose largest worker has 64 GiB but declared nothing reads back as a fleet of workers with no capacity. Deriving the ceiling from whatever this process happens to know therefore does not converge on the truth; it converges on THIS process, and rejects at enqueue every job bigger than the pod that enqueued it. Requiring the declaration also keeps the common path free of a ListWorkers round trip per enqueue.
When the declaration is present the registry is still consulted, so the ceiling can only rise toward the real fleet maximum on a backend that carries capacity. "Live" means both an active state and a recent heartbeat. State alone is not enough: nothing in Dispatch ever writes WorkerDead — a worker registers active and is either deregistered on clean shutdown or its row is deleted by DeleteStaleWorkers. A worker killed by SIGKILL, an OOM or a pod eviction therefore stays "active" in the registry until something sweeps it, and counting its capacity would admit jobs no live worker can run.
func (*Engine) PlanWorkflowReplay ¶
func (eng *Engine) PlanWorkflowReplay(ctx context.Context, runID id.RunID, fromStep string) (*workflow.ReplayPlan, error)
PlanWorkflowReplay reports what ReplayWorkflowFrom would do for the run and step, without changing anything. See workflow.Runner.PlanReplay for the refusals; a running run plans fine and its State says a replay would be refused.
func (*Engine) PurgeDLQ ¶
PurgeDLQ deletes every dead letter entry that failed strictly before before, and returns how many went. A zero before is refused: it matches nothing, so it can only be a caller that forgot to set it. One OperatorAction of kind ext.ActionDLQPurged, with Count set, is emitted on success, including when nothing matched. A store that reports partial progress with an error retains that count and emits it before returning.
func (*Engine) QueryDurableWorkflow ¶
func (eng *Engine) QueryDurableWorkflow(ctx context.Context, request drt.QueryRequest) (drt.QueryResult, error)
QueryDurableWorkflow selects and reconstructs one run without claiming tasks or writing history. Authorize callers before exposing this trusted Go API remotely.
func (*Engine) QueueManager ¶
QueueManager returns the queue manager, or nil if no queue configs were provided.
func (*Engine) ReplayAllDLQ ¶
func (eng *Engine) ReplayAllDLQ(ctx context.Context, opts ReplayAllOpts) (ReplayAllResult, error)
ReplayAllDLQ replays every entry nobody has replayed yet, newest first, up to opts.Limit, each one exactly as ReplayDLQ would. It is not a transaction: entries are claimed one at a time, so a concurrent replay of one of them shows up as a Conflict, and a failure partway leaves the earlier replays in place.
It returns an error only when the entries cannot be listed or ctx ends, together with what it had done by then. One OperatorAction records the committed replay count, including when a later read fails. A completed operation with no replays also emits a zero count. An interrupted operation that changed no entries emits nothing.
func (*Engine) ReplayDLQ ¶
ReplayDLQ turns a dead letter entry back into work: a new pending job carrying everything the failed one ran with, under a fresh ID.
The entry is claimed before the job is enqueued (dlq.Service.ReplayEntry), so of two concurrent replays exactly one creates a job and the other gets an error wrapping dispatch.ErrDLQAlreadyReplayed, as does a replay of an entry whose job was already retried. A failed enqueue releases the claim. Unknown entries return dispatch.ErrDLQNotFound, and a job too big for every worker wraps resource.ErrUnschedulable.
One OperatorAction of kind ext.ActionDLQReplayed is emitted on success, naming the entry, the failed job and the new one.
func (*Engine) ReplayWorkflowFrom ¶
func (eng *Engine) ReplayWorkflowFrom(ctx context.Context, runID id.RunID, fromStep string) (*workflow.ReplayPlan, error)
ReplayWorkflowFrom re-runs a finished run from a step on the run's stamped version, keeping the checkpoints up to and including fromStep. It returns once the replay has started on the runner's background launcher; Stop waits for it. Refusals are workflow.Runner.ReplayFrom's: dispatch.ErrRunNotFound, dispatch.ErrInvalidState (running, claimed by another replay, no checkpoint for the step, version not registered), or workflow.ErrRunnerShutdown after Stop.
On success it emits one ext.ActionWorkflowReplayed with the run and step.
func (*Engine) ReplayWorkflowFromGeneration ¶
func (eng *Engine) ReplayWorkflowFromGeneration(ctx context.Context, runID id.RunID, fromStep string, generation int64) (*workflow.ReplayPlan, error)
ReplayWorkflowFromGeneration starts replay only if the reviewed generation still matches. It emits an operator action only after a successful launch.
func (*Engine) RequestCancelDurableWorkflow ¶
func (eng *Engine) RequestCancelDurableWorkflow(ctx context.Context, request durable.CancelExecutionRequest) (durable.CancelExecutionReceipt, error)
RequestCancelDurableWorkflow records cancellation acceptance for an explicit or current run. Authorize callers before exposing this trusted API remotely.
func (*Engine) Resources ¶
Resources returns the shared admission ledger, or nil when the resource model is off. It is the instance the staging cache must have been built with.
func (*Engine) RetryJob ¶
RetryJob puts a failed job back to pending, as if it had just been enqueued: no retries spent, no error, no owner. Any other state wraps dispatch.ErrInvalidState.
A failed job usually has a dead letter entry, and replaying that entry would run the same failure a second time. So when one exists the retry claims it first, with the retried job as the claim's job, and a claim that is already taken refuses the retry with dispatch.ErrDLQAlreadyReplayed. If the job write then fails the claim is released, so the entry can be replayed or retried later. A failed job with no entry retries without a claim.
Without an entry there is nothing to serialise two concurrent retries on. Both write the same pending row, which is harmless unless a worker claims the job between the two writes.
The returned job is the row as written. One OperatorAction of kind ext.ActionJobRetried is emitted on success, carrying the claimed entry's ID when there was one.
func (*Engine) SignalDurableWorkflow ¶
func (eng *Engine) SignalDurableWorkflow(ctx context.Context, request durable.SignalRequest) (durable.SignalReceipt, error)
SignalDurableWorkflow accepts a message for an explicit or current open run. You must authorize callers before exposing this trusted Go method remotely.
func (*Engine) SignalWithStartDurableWorkflow ¶
func (eng *Engine) SignalWithStartDurableWorkflow(ctx context.Context, request durable.SignalWithStartRequest) (durable.SignalReceipt, error)
SignalWithStartDurableWorkflow accepts a message and creates a run when needed. Reuse the whole request to recover its original target after a lost response.
func (*Engine) Start ¶
Start begins job processing by starting the worker pool and cron scheduler. It also resumes any workflow runs left in "running" state (crash recovery).
func (*Engine) StartDurableWorkflow ¶
func (eng *Engine) StartDurableWorkflow(ctx context.Context, request durable.StartRequest) (durable.Receipt, error)
StartDurableWorkflow persists the run and its initial task. Calls may precede Engine.Start. The caller supplies stable IDs so ambiguous outcomes are retryable.
func (*Engine) Stop ¶
Stop confirms worker quiescence before attempting publisher drain and storage closure. A timed-out caller leaves one quiescence task running and can retry.
func (*Engine) StopWorkers ¶
StopWorkers permanently stops this engine's execution producers and waits for both worker pools to finish. Publisher delivery, final extension hooks and storage remain active. This is a terminal worker stop, not a resumable pause. Concurrent Stop/StopWorkers calls share the same quiescence task. A caller's timeout never establishes completion and can be retried with a fresh context.
func (*Engine) StreamBroker ¶
StreamBroker returns the real-time event broker, or nil if not enabled.
func (*Engine) Telemetry ¶
Telemetry returns the spans and metrics the engine records through, built from WithMetricFactory and WithTracerProvider. The Forge extension hands it to the outbox publisher and artifact sweeper it builds; do the same for any subsystem you assemble beside the engine.
func (*Engine) TriggerCron ¶
TriggerCron enqueues the entry's job now, with its payload and queue, the same job a scheduled fire makes. The schedule is untouched: next_run_at and last_run_at stay as they were, so the entry still fires at its next scheduled time. A disabled entry can be triggered too, since running it by hand is exactly what an operator asks for here. Hooks see CronFired with the new job, as for a scheduled fire, and then the operator action. dispatch.ErrCronNotFound when the entry does not exist.
func (*Engine) ValidateArtifactInputs ¶
ValidateArtifactInputs checks a definition's declarations against the engine's staging capacity.
A definition whose declared inputs could never fit the cache budget is rejected here, at registration, rather than failing every time such a job runs. The point is to surface the mistake on a developer's machine instead of at 3am in production.
func (*Engine) WaitDurableDrain ¶
func (eng *Engine) WaitDurableDrain(ctx context.Context, handle drt.DrainHandle) (drt.DrainResult, error)
WaitDurableDrain only waits. Its context never shortens an accepted drain.
func (*Engine) WorkflowRunner ¶
WorkflowRunner returns the workflow runner.
type Inspection ¶
type Inspection struct {
Config dispatch.Config
Pool worker.Settings
Cron cron.Settings
ResourceDefaults resource.Set
QueueResources map[string]resource.Set
WorkerCapacity resource.Set
WorkerCustomKeys []string
ResourceManagerEnabled, EstimatorConfigured bool
ScratchRoot string
WakeNotifierSupported bool
WorkerHeartbeatInterval, WorkerStaleThreshold time.Duration
}
Inspection describes the serving process. It makes no fleet-wide health claim.
type Option ¶
type Option func(*Engine)
Option configures an Engine.
func WithArtifacts ¶
WithArtifacts enables the artifact plane.
The staging middleware is appended to the chain, so declared inputs are materialised before a handler runs and every cache lease is released afterwards. Without this option Dispatch behaves exactly as it did before artifacts existed.
func WithBackoff ¶
WithBackoff sets the retry backoff strategy for the engine. If not set, backoff.DefaultStrategy() (exponential with jitter) is used.
func WithDurableWorkflows ¶
WithDurableWorkflows enables the history-based runtime alongside the existing checkpoint workflow API. Its store must explicitly implement durable.Store.
func WithEstimator ¶
WithEstimator installs the resource estimator consulted at enqueue.
The estimator sits above a definition's static declaration and below a per-enqueue override. It receives the declaration in the request and may return it unchanged, so installing one is an explicit opt-in to letting inference override declaration. An estimator that errors is ignored: it must never fail an enqueue.
func WithExecutor ¶
WithExecutor registers an additional executor, making a stronger isolation level available to job definitions that ask for it.
The in-process executor is always present as the default, so a deployment that adds nothing behaves exactly as it always has.
func WithExtension ¶
WithExtension registers an extension with the engine.
func WithMetricFactory ¶
func WithMetricFactory(factory gu.MetricFactory) Option
WithMetricFactory sets the go-utils MetricFactory for engine-level metrics. Use fapp.Metrics() in forge applications to integrate with the forge metrics system. If not set, a default metrics collector is used for the legacy counters and the labelled dispatch_* metrics (see the telemetry package) are off: the default collector keys series by name alone and would fold their labels.
func WithMiddleware ¶
func WithMiddleware(m mw.Middleware) Option
WithMiddleware adds middleware to the engine's chain.
func WithQueueConfig ¶
WithQueueConfig registers queue-level rate limiting and concurrency configurations. Queues not listed have no limits.
func WithResourceDefaults ¶
WithResourceDefaults sets the fleet-wide default requirement and any per-queue overrides. Both are the lowest-precedence sources, below a definition's own declaration.
func WithResourceManager ¶
WithResourceManager installs the admission ledger the worker pool admits jobs against.
The manager passed here MUST be the same instance the staging cache was built with (cache.WithManager). One ledger is the whole design: the cache holds a lease per cached entry and registers itself as the manager's disk reclaimer, and the pool's dequeue budget offers disk as free PLUS what that reclaimer could evict. Give the cache a private manager — which it constructs for itself when none is supplied — and the pool's Reclaimable() is permanently zero, staged bytes are never offered back to the budget, and the disk path quietly does nothing. It presents as a worker that went quiet, not as an error.
Leaving this unset is the supported default: the pool passes an unbounded DequeueOpts, every backend skips its fit predicate, and no leases are taken. That is exactly how Dispatch behaved before the resource model existed.
Installing a manager does NOT declare a fleet capacity, and does not turn the enqueue-time unschedulable check on. See WithWorkerCapacity for why that is opt-in and separate.
Leases compose with this. The grant travels on job.DequeueOpts (WorkerID and LeaseUntil), so a claim that takes a lease is an ordinary claim that also writes the lease columns — it carries the budget, the custom-key containment and the locality preference like any other. There is one dequeue path per backend and it is the one this manager constrains. Turning leases and resources on together is the natural upgrade, and it is a supported one.
func WithScratchRoot ¶
WithScratchRoot sets the root directory an out-of-process attempt's scratch OutputDir is created under (worker.Runner.WithArtifacts).
It only has an effect once the artifact plane is also configured (WithArtifacts): Build only ever passes this value to the Runner alongside eng.artifacts (see Build's own runner.WithArtifacts call), so with no artifact plane configured this sets a value Build never reads and the Runner's scratch directories fall back to os.TempDir() regardless. That is not because scratch-dir creation or its startup sweep are themselves gated on the artifact plane — they are not, and both run for any out-of-process attempt either way (see worker.Runner.prepareOutputDir and Reclaim) — it is purely this option's own value never reaching the Runner. The extension package's resolveExecutionOptions logs a startup warning for exactly this configuration (a configured scratch_dir with the artifact plane off), so that stays a deliberate, visible choice rather than a silent one.
Leaving it unset defaults to os.TempDir(), exactly as worker.Runner does on its own.
func WithStreamBroker ¶
func WithStreamBroker(opts ...stream.BrokerOption) Option
WithStreamBroker enables the real-time stream broker for event pub/sub. The broker is automatically registered as an extension so it receives all job and workflow lifecycle events. It is required for DWP (Dispatch Wire Protocol) real-time subscriptions.
func WithTracerProvider ¶
func WithTracerProvider(tp trace.TracerProvider) Option
WithTracerProvider sets a custom OTel TracerProvider for the engine. When set, every span the engine and its subsystems start (job attempts, enqueues, cron fires, workflow runs, durable tasks, the tracing middleware) uses this provider instead of the global one. If not set, the global otel.GetTracerProvider() is used.
func WithWorkerCapacity ¶
WithWorkerCapacity declares the largest single-worker capacity in the fleet, and is the ONLY thing that turns the enqueue-time unschedulable check on. Leave it unset — the default — and Enqueue never rejects a job for being too big for any worker.
It is a fleet-wide statement, not a description of this process, and the distinction is the whole reason the check is opt-in. Declare it and a job requiring more than this on any dimension fails Enqueue with ErrUnschedulable, wherever it was enqueued from: a light API pod that declared its own 2 GiB would hard-reject the tessellation job the heavy tier runs perfectly well.
The check cannot derive the fleet maximum for itself. Every store backend now persists cluster.Worker.Capacity (the k8s provider still drops it), but a worker's row carries only what that worker passed here, so a fleet in which nobody declared reads back with no capacity at all. MaxWorkerCapacity therefore starts from this value and lets live workers' published capacities raise it, never lower it. Declaring the ceiling is the operator's job, or the check stays off.
It is deliberately NOT defaulted from WithResourceManager's capacity. That default read as a convenience and behaved as a silent rescope of a fleet-wide question to one process.
func WithWorkerCustomKeys ¶
WithWorkerCustomKeys narrows the custom resource keys this worker advertises at dequeue.
The default — every custom key the manager has capacity for — is usually right. This exists to shrink it, so a worker draining a device can stop attracting work for it without being reconfigured. Keep the list a subset of the manager's custom capacity: dequeue matches custom keys by containment and never by quantity, so a key advertised here with no capacity behind it passes the store's filter and is then refused locally, on every attempt.
type ReplayAllOpts ¶
type ReplayAllOpts struct {
// Queue limits the replay to one queue. Empty means every queue.
Queue string
// Limit caps how many entries are tried. Zero or less means 1000.
Limit int
}
ReplayAllOpts selects what ReplayAllDLQ replays.
type ReplayAllResult ¶
type ReplayAllResult struct {
// Replayed is how many entries became new jobs.
Replayed int
// Conflicts is how many were claimed by somebody else between the
// listing and their own replay.
Conflicts int
// Failed is how many could not be replayed for any other reason.
// Each of those entries was released and can be replayed again.
Failed int
// Errors holds a message for the first failures, "<entry id>: <error>",
// at most 20 of them.
Errors []string
}
ReplayAllResult counts what ReplayAllDLQ did with each entry it tried.