fix: harden queue lifecycle and publish gate
- Preserve phase results on partial retry and keep interrupted phase context after restart. - Avoid webhook bookkeeping crashes when retention deletes stale jobs. - Add deeper unit, integration, and e2e coverage around queue seams. - Require verify job to pass before publish runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
140
docs/jobqueue.c4
Normal file
140
docs/jobqueue.c4
Normal file
@@ -0,0 +1,140 @@
|
||||
specification {
|
||||
element actor {
|
||||
style {
|
||||
shape person
|
||||
}
|
||||
}
|
||||
|
||||
element system {
|
||||
style {
|
||||
shape rectangle
|
||||
}
|
||||
}
|
||||
|
||||
element container {
|
||||
style {
|
||||
shape rectangle
|
||||
}
|
||||
}
|
||||
|
||||
element component {
|
||||
style {
|
||||
shape component
|
||||
}
|
||||
}
|
||||
|
||||
element database {
|
||||
style {
|
||||
shape storage
|
||||
}
|
||||
}
|
||||
|
||||
relationship async {
|
||||
color amber
|
||||
line dotted
|
||||
}
|
||||
}
|
||||
|
||||
model {
|
||||
consumer = actor "Consumer application"
|
||||
webhookReceiver = system "Webhook receiver"
|
||||
|
||||
jobqueue = system "jobqueue" {
|
||||
api = container "Public API" {
|
||||
technology "ESM / TypeScript"
|
||||
description "JobQueue constructor plus enqueue, retry, cancel, query, stream, shutdown APIs"
|
||||
}
|
||||
|
||||
runtime = container "Runtime orchestrator" {
|
||||
technology "Node.js"
|
||||
description "Coordinates persistence, execution, retries, events, SSE, webhooks, and shutdown"
|
||||
|
||||
queue = component "JobQueue"
|
||||
storage = component "SqliteStorage"
|
||||
pool = component "WorkerPool"
|
||||
runner = component "PhaseRunner"
|
||||
retry = component "RetryStrategy"
|
||||
events = component "TypedEventBus"
|
||||
sse = component "SseSerializer"
|
||||
retention = component "RetentionScheduler"
|
||||
webhooks = component "WebhookDispatcher"
|
||||
|
||||
queue -> storage "persists job state"
|
||||
queue -> pool "dispatches runnable jobs"
|
||||
queue -> runner "executes phase pipeline"
|
||||
queue -> retry "classifies failures"
|
||||
queue -> events "emits typed queue events"
|
||||
queue -> sse "serializes SSE payloads"
|
||||
queue -> retention "runs stale/delete cycle"
|
||||
queue -[async]-> webhooks "dispatches outbound callbacks"
|
||||
}
|
||||
|
||||
sqlite = database "SQLite jobs database" {
|
||||
technology "better-sqlite3 + WAL"
|
||||
}
|
||||
|
||||
handlers = container "Registered phase handlers" {
|
||||
technology "Consumer-provided async functions"
|
||||
}
|
||||
|
||||
streams = container "SSE subscribers" {
|
||||
technology "ReadableStream consumers"
|
||||
}
|
||||
|
||||
api -> runtime.queue "constructs and invokes"
|
||||
runtime.storage -> sqlite "reads/writes rows"
|
||||
runtime.runner -> handlers "invokes phase handlers"
|
||||
runtime.events -> streams "pushes queue events"
|
||||
}
|
||||
|
||||
consumer -> jobqueue.api "enqueue / retry / cancel / inspect / subscribe"
|
||||
jobqueue.runtime.webhooks -[async]-> webhookReceiver "POST job events"
|
||||
}
|
||||
|
||||
views {
|
||||
view index {
|
||||
title "jobqueue landscape"
|
||||
include *
|
||||
autoLayout LeftRight
|
||||
}
|
||||
|
||||
view library of jobqueue {
|
||||
title "jobqueue containers"
|
||||
include *
|
||||
autoLayout LeftRight
|
||||
}
|
||||
|
||||
view runtime of jobqueue.runtime {
|
||||
title "jobqueue runtime components"
|
||||
include *
|
||||
autoLayout LeftRight
|
||||
}
|
||||
|
||||
dynamic view enqueue-to-complete {
|
||||
title "Enqueue to successful completion"
|
||||
consumer -> jobqueue.api "enqueue()"
|
||||
jobqueue.api -> jobqueue.runtime.queue "create job"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.storage "persist pending row"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.pool "schedule worker"
|
||||
jobqueue.runtime.pool -> jobqueue.runtime.runner "run phases"
|
||||
jobqueue.runtime.runner -> jobqueue.handlers "invoke handler(s)"
|
||||
jobqueue.runtime.runner -> jobqueue.runtime.storage "persist progress + phase results"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.events "emit queue events"
|
||||
jobqueue.runtime.events -> jobqueue.streams "push SSE"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.webhooks "send completion webhook"
|
||||
jobqueue.runtime.webhooks -> webhookReceiver "POST payload"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.storage "mark webhook_sent"
|
||||
}
|
||||
|
||||
dynamic view retry-flow {
|
||||
title "Failure and retry flow"
|
||||
jobqueue.runtime.runner -> jobqueue.handlers "invoke handler"
|
||||
jobqueue.handlers -> jobqueue.runtime.queue "throw recoverable error"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.retry "classify error"
|
||||
jobqueue.runtime.retry -> jobqueue.runtime.queue "retry with delay"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.storage "persist pending retry"
|
||||
jobqueue.runtime.queue -> jobqueue.runtime.events "emit job:retrying"
|
||||
jobqueue.runtime.events -> jobqueue.streams "push SSE"
|
||||
jobqueue.runtime.queue -[async]-> jobqueue.runtime.webhooks "dispatch retry webhook"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user