Chain Model
Overview
Section titled “Overview”This document describes Queuert’s unified job model and the Promise-inspired chain abstraction.
Core Concepts
Section titled “Core Concepts”A Job is an individual unit of work with a lifecycle:
(blocked →) pending → running → completedEach job:
- Belongs to a Job Type that defines its input/output schema
- Contains typed input data and (when completed) output data
- Can
continueWithto create a linked follow-up job - Can depend on blockers (other chains that must complete first)
A Chain is a series of linked jobs where each job can continue to the next—just like a JavaScript Promise chain.
Job A → Job B → Job C → (completed)The chain completes when its final job completes without continuing.
The Promise Analogy
Section titled “The Promise Analogy”The design mirrors JavaScript Promises:
// JavaScript: A Promise chain IS the first promiseconst chain = fetch(url) // chain === this promise .then(processResponse) // continuation .then(formatResult); // continuation
// Queuert: A Chain IS its head jobconst chain = createChain(...) // chain.id === headJob.id .continueWith(processStep) // continuation .continueWith(formatStep); // continuationThe fundamental insight: the head job IS the chain. Chains work like Promises but persist across process restarts and distribute across workers.
Identity Model
Section titled “Identity Model”For the head job in a chain: job.id === job.chainId
This isn’t redundant—it’s a meaningful signal that identifies the chain starter. Continuation jobs have job.id !== job.chainId but share the same chainId as all other jobs in the chain. Each job also carries its position as job.chainIndex: 0 for the head job, incrementing by one for each continuation.
Unified Model Benefits
Section titled “Unified Model Benefits”Having the head job BE the chain (rather than a separate entity) provides:
Simplicity
Section titled “Simplicity”- No separate
chaintable — a chain’s own facts (type name, status, deduplication key, trace context, completion time) live on its head row in thejobtable, and continuations are further rows in the same table (plus ajob_blockerjunction table for dependencies) - One primary type, one set of operations
- No synchronization issues
Flexibility
Section titled “Flexibility”The head job can be:
- A lightweight “alias” that immediately continues to real work
- A full job that processes and completes the chain in one step
- Anything in between
Performance
Section titled “Performance”- No subqueries needed to find chains by type
- Efficient at scale (millions of jobs)
Execution Patterns
Section titled “Execution Patterns”Chains support various patterns via continueWith:
Linear
Section titled “Linear”A → B → C → doneBranched
Section titled “Branched”A → B1 (if condition) → B2 (else)A → A → A → doneGo-to (jump back)
Section titled “Go-to (jump back)”A → B → A → B → doneBlockers: Chain Dependencies
Section titled “Blockers: Chain Dependencies”Chains can depend on other chains to complete before starting:
┌──────────────┐│ Blocker A │───┐└──────────────┘ │ ├──→ Main Chain (blocked until A and B complete)┌──────────────┐ ││ Blocker B │───┘└──────────────┘Blockers are declared at the type level and provided via the blockers array when creating a chain. The main job starts as blocked if any blocker chain is still running (otherwise it starts pending) and transitions to pending when all blockers complete.
Consistent Terminology
Section titled “Consistent Terminology”Parallel entities use consistent lifecycle terminology to reduce cognitive load:
- Job:
blocked→pending→running→completed - Chain:
running→completed
Avoid asymmetric naming (e.g., started/finished vs created/completed) even if individual terms seem natural. Consistency across the API produces fewer questions and faster comprehension.
Summary
Section titled “Summary”The Chain model:
- Mirrors Promises: Familiar mental model for JavaScript developers
- Unified identity: The head job IS the chain—no separate entity
- Single table: Jobs and chains share storage;
chainIdlinks them - Flexible patterns: Linear, branched, looped, or jumping execution
- Distributed: Unlike Promises, chains persist and distribute across workers