Skip to content

SQL Schema Atlas

SQL schema map

ChokaQ SQL Server storage is built around active work, immutable history, operator recovery, lifetime counters, rolling metrics, queue configuration, and schema migration metadata.

The default schema name is chokaq.

Tables

TablePurposeHot path?
JobsHotActive work: pending, fetched, processing, delayed retry.Yes
JobsArchiveSuccessful completed jobs.No
JobsDLQFailed, cancelled, zombie, or operator-held jobs.No
StatsSummaryLifetime counters per queue.No
MetricBucketsRecent throughput and failure-rate aggregates.No
QueuesQueue runtime configuration.Yes, read by workers
SchemaMigrationsApplied ChokaQ SQL schema versions.Startup/ops

JobsHot

JobsHot is the only table workers scan to find executable work. Keeping it small is the core performance decision behind the Three Pillars model.

Key columns:

ColumnMeaning
IdStable job identifier.
QueueQueue partition and operational control boundary.
TypePersisted job type key.
PayloadSerialized job payload.
TagsOptional operator/search metadata.
IdempotencyKeyOptional active-work dedupe key.
PriorityHigher values are fetched first.
StatusPending, fetched, or processing.
AttemptCountNumber of executions that reached Processing.
ScheduledAtUtcFuture eligibility time for delays and retries.
WorkerIdCurrent worker owner after fetch.
HeartbeatUtcProcessing liveness signal.

Important indexes:

IndexSupports
IX_JobsHot_FetchWorker fetch ordering by queue, priority, schedule, creation time.
IX_JobsHot_IdempotencyUnique active idempotency key.
IX_JobsHot_QueueStatsQueue dashboard counts.
IX_JobsHot_PendingLagQueue lag health checks.
IX_JobsHot_StatusCreatedActive job dashboard view.
IX_JobsHot_FetchedRecoveryAbandoned fetched-job recovery.
IX_JobsHot_ProcessingHeartbeatZombie detection.

JobsArchive

JobsArchive stores succeeded jobs after a successful final transition. It is not part of the worker fetch path.

Indexes support recent history, queue-specific history, and tag search. Archive can grow independently from active work because workers do not scan it for new jobs.

JobsDLQ

JobsDLQ stores failed, cancelled, zombie, and operator-held jobs. It powers inspection, edit, resurrection, bulk requeue, bulk purge, and failure grouping.

Important indexes:

IndexSupports
IX_JobsDLQ_DateRecent DLQ view.
IX_JobsDLQ_QueueQueue-scoped DLQ inspection.
IX_JobsDLQ_ReasonFailure taxonomy filtering.
IX_JobsDLQ_TypeType-key failure triage.
IX_JobsDLQ_CreatedAtAge-based cleanup and investigation.

StatsSummary

StatsSummary stores lifetime counters per queue:

  • succeeded total;
  • failed total;
  • retried total;
  • last activity timestamp.

It avoids recomputing lifetime counters by scanning Archive and DLQ.

MetricBuckets

MetricBuckets stores recent completion aggregates. ChokaQ updates buckets inside the same transaction that moves a job to Archive or DLQ.

This makes dashboard throughput cheap and stable:

  • Archive answers investigation questions;
  • DLQ answers recovery questions;
  • MetricBuckets answers recent-rate questions.

Queues

Queues is the runtime control table. It stores:

  • queue name;
  • paused/active flags;
  • per-queue zombie timeout;
  • optional max worker budget;
  • last update timestamp.

Workers read this table to avoid fetching paused queues and to enforce per-queue capacity.

SchemaMigrations

SchemaMigrations records applied ChokaQ schema versions. It turns first-start bootstrap into an auditable operation instead of relying only on idempotent CREATE TABLE IF MISSING logic.

Architecture Decision

Why this pattern?

The schema separates active work from historical evidence. That keeps worker queries small while preserving completed and failed jobs for operators.

Trade-offs

Moving between tables requires transaction integrity. The implementation must be careful to avoid copy-then-delete bugs. ChokaQ uses short SQL transactions and OUTPUT to make those moves atomic.

Alternatives considered

AlternativeBenefitCost
Single Jobs table with status columnSimple model.Hot path degrades as history grows.
Broker-only queueHigh throughput.Less transparent operational state without extra storage.
Archive outside SQLSmaller database.Harder debugging and split-brain evidence.

Additional Questions

Why not keep every job in one table?
Because active fetch queries would share indexes and storage with years of history, making the worker path sensitive to retention.

Why materialize MetricBuckets?
Because dashboard rate windows should not scan mutable history tables or change when an operator purges/requeues DLQ rows.

What is the main risk of this schema?
Cross-table moves must be atomic. That is why final transitions use database transactions and deleted-row capture.

Apache 2.0 Licensed