feat(11-01): run job workers in serve and queue:work, add queue:clear
- Manager gains the apparatus JobManager surface: StartJob, UpdateJobState, UpdateMetadata, FailJob, CancelJob (is_canceled + STOPPED + River JobCancel), StopJob (STOPPED only), CheckIfCanceled and GetMetadata, all raw column writes so updated_at is untouched - serve starts the in-process worker unless queue.work_in_serve is false and stops it on shutdown; an app without jobs gets an idle worker - queue:work runs a foreground worker with repeatable --queue filters; queue:clear deletes available, scheduled and retryable jobs of one queue - the generated main appends conga.RuntimeCommands; summer delegates queue:work and queue:clear; make:job scaffolds a conga.Job
This commit is contained in:
@@ -28,6 +28,8 @@ type WorkerOptions struct {
|
||||
pollInterval time.Duration
|
||||
// pollOnly builds a driver without the LISTEN pool (tests only).
|
||||
pollOnly bool
|
||||
// retryPolicy overrides River's retry schedule (tests only).
|
||||
retryPolicy river.ClientRetryPolicy
|
||||
}
|
||||
|
||||
// Worker is a running River work client. Stop it with Stop.
|
||||
@@ -92,6 +94,13 @@ func StartWorker(ctx context.Context, app *backpack.App, plugins []party.Plugin,
|
||||
return nil, fmt.Errorf("conga: register %s: %w", j.kind(), err)
|
||||
}
|
||||
}
|
||||
if len(m.jobs) == 0 {
|
||||
// River refuses to start a client without workers; an app with no
|
||||
// jobs yet still gets a worker that starts and idles.
|
||||
if err := river.AddWorkerSafely[idleArgs](workers, idleWorker{}); err != nil {
|
||||
return nil, fmt.Errorf("conga: register idle worker: %w", err)
|
||||
}
|
||||
}
|
||||
cfg := baseConfig(s, log)
|
||||
cfg.Workers = workers
|
||||
cfg.Queues = map[string]river.QueueConfig{}
|
||||
@@ -101,6 +110,9 @@ func StartWorker(ctx context.Context, app *backpack.App, plugins []party.Plugin,
|
||||
if o.pollInterval > 0 {
|
||||
cfg.FetchPollInterval = o.pollInterval
|
||||
}
|
||||
if o.retryPolicy != nil {
|
||||
cfg.RetryPolicy = o.retryPolicy
|
||||
}
|
||||
|
||||
var listener *pgxpool.Pool
|
||||
var driver *riverdatabasesql.Driver
|
||||
@@ -126,6 +138,16 @@ func StartWorker(ctx context.Context, app *backpack.App, plugins []party.Plugin,
|
||||
return &Worker{m: m, client: client, listener: listener, queues: sortedQueueNames(queues)}, nil
|
||||
}
|
||||
|
||||
// StartServeWorker starts the in-process worker of the serve command on
|
||||
// every known queue. It returns nil, nil when queue.work_in_serve is false,
|
||||
// for deployments that run a separate queue:work process.
|
||||
func StartServeWorker(ctx context.Context, app *backpack.App, plugins []party.Plugin) (*Worker, error) {
|
||||
if !settingsFromApp(app).workInServe {
|
||||
return nil, nil
|
||||
}
|
||||
return StartWorker(ctx, app, plugins, WorkerOptions{})
|
||||
}
|
||||
|
||||
// Stop stops fetching jobs and waits for running jobs to finish. When ctx
|
||||
// ends first, running jobs are cancelled. The listener pool is closed.
|
||||
func (w *Worker) Stop(ctx context.Context) error {
|
||||
@@ -242,3 +264,15 @@ func summerJobID(row *rivertype.JobRow) (uint, bool) {
|
||||
}
|
||||
return meta.SummerJobID, true
|
||||
}
|
||||
|
||||
// idleArgs is the kind of the placeholder worker registered when an app has
|
||||
// no jobs. Nothing inserts it.
|
||||
type idleArgs struct{}
|
||||
|
||||
func (idleArgs) Kind() string { return "summercms_conga_idle" }
|
||||
|
||||
type idleWorker struct {
|
||||
river.WorkerDefaults[idleArgs]
|
||||
}
|
||||
|
||||
func (idleWorker) Work(context.Context, *river.Job[idleArgs]) error { return nil }
|
||||
|
||||
Reference in New Issue
Block a user