You've already forked agentic-coding-workflow
feat(jobs): domain types + Handler interface + Config
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
// Package jobs implements the background job runner: a hybrid scheduler that
|
||||
// drives idempotent periodic runners (workspace fetch, worktree prune,
|
||||
// attachment cleanup, job reaper, job purge) directly via tickers, and routes
|
||||
// retry-sensitive event handlers (webhook delivery) through a DB-backed jobs
|
||||
// table with SELECT FOR UPDATE SKIP LOCKED dispatch.
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// JobType identifies a kind of work that a Handler knows how to execute.
|
||||
// Runners (periodic) are NOT job types — they tick directly. Handlers
|
||||
// (event-driven) are.
|
||||
type JobType string
|
||||
|
||||
const (
|
||||
// TypeWebhookDeliver delivers a notify.Message to the configured webhook URL.
|
||||
TypeWebhookDeliver JobType = "webhook.deliver"
|
||||
)
|
||||
|
||||
// JobStatus matches the CHECK constraint on jobs.status.
|
||||
type JobStatus string
|
||||
|
||||
const (
|
||||
StatusPending JobStatus = "pending"
|
||||
StatusRunning JobStatus = "running"
|
||||
StatusCompleted JobStatus = "completed"
|
||||
StatusDead JobStatus = "dead"
|
||||
)
|
||||
|
||||
// Job mirrors a row in the jobs table.
|
||||
type Job struct {
|
||||
ID uuid.UUID
|
||||
Type JobType
|
||||
Payload json.RawMessage
|
||||
Status JobStatus
|
||||
ScheduledAt time.Time
|
||||
Attempts int32
|
||||
MaxAttempts int32
|
||||
LeasedAt *time.Time
|
||||
LeasedBy *string
|
||||
LastError *string
|
||||
CompletedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// MaxPayloadBytes caps payload size at 64 KB. Enforced at enqueue time.
|
||||
const MaxPayloadBytes = 64 * 1024
|
||||
|
||||
// Handler executes a single Job. Returning a permanentError sends the job
|
||||
// straight to dead-letter; any other error triggers retry per the worker's
|
||||
// backoff schedule. Returning nil marks the job completed.
|
||||
type Handler interface {
|
||||
Type() JobType
|
||||
Handle(ctx context.Context, job Job) error
|
||||
}
|
||||
|
||||
// permanentError marks an error as non-retryable (4xx-equivalent).
|
||||
type permanentError struct{ err error }
|
||||
|
||||
// Permanent wraps err so the worker routes the job to dead-letter without
|
||||
// further retries.
|
||||
func Permanent(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &permanentError{err: err}
|
||||
}
|
||||
|
||||
func (e *permanentError) Error() string { return e.err.Error() }
|
||||
func (e *permanentError) Unwrap() error { return e.err }
|
||||
|
||||
// IsPermanent reports whether err was wrapped by Permanent.
|
||||
func IsPermanent(err error) bool {
|
||||
var pe *permanentError
|
||||
return errors.As(err, &pe)
|
||||
}
|
||||
|
||||
// RunnerConfig is the per-runner configuration loaded from config.yaml.
|
||||
// Zero Interval means "use the runner's documented default".
|
||||
type RunnerConfig struct {
|
||||
Enabled bool
|
||||
Interval time.Duration
|
||||
}
|
||||
|
||||
// WorktreePruneConfig extends RunnerConfig with prune-specific knobs.
|
||||
type WorktreePruneConfig struct {
|
||||
Enabled bool
|
||||
Interval time.Duration
|
||||
IdleThreshold time.Duration
|
||||
WarningLead time.Duration
|
||||
}
|
||||
|
||||
// AttachmentCleanupConfig extends RunnerConfig with retention.
|
||||
type AttachmentCleanupConfig struct {
|
||||
Enabled bool
|
||||
Interval time.Duration
|
||||
Retention time.Duration
|
||||
}
|
||||
|
||||
// JobReaperConfig extends RunnerConfig with the lease timeout used to detect
|
||||
// crashed workers.
|
||||
type JobReaperConfig struct {
|
||||
Enabled bool
|
||||
Interval time.Duration
|
||||
LeaseTimeout time.Duration
|
||||
}
|
||||
|
||||
// JobPurgeConfig extends RunnerConfig with completed/dead retention.
|
||||
type JobPurgeConfig struct {
|
||||
Enabled bool
|
||||
Interval time.Duration
|
||||
CompletedRetention time.Duration
|
||||
}
|
||||
|
||||
// Config is the full jobs.Module configuration.
|
||||
type Config struct {
|
||||
Enabled bool
|
||||
Workers int
|
||||
ShutdownGrace time.Duration
|
||||
WorkspaceFetch RunnerConfig
|
||||
WorktreePrune WorktreePruneConfig
|
||||
AttachmentCleanup AttachmentCleanupConfig
|
||||
JobReaper JobReaperConfig
|
||||
JobPurge JobPurgeConfig
|
||||
}
|
||||
Reference in New Issue
Block a user