bullmq 6.3.11 → 6.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/classes/queue-getters.js +3 -1
- package/dist/cjs/classes/queue.js +2 -1
- package/dist/cjs/classes/redis-connection.js +1 -1
- package/dist/cjs/classes/redis-queue-backend.js +1 -1
- package/dist/cjs/commands/includes/moveChildBackToParentDependencies.lua +20 -0
- package/dist/cjs/commands/moveJobsToWait-8.lua +3 -0
- package/dist/cjs/commands/reprocessJob-7.lua +2 -14
- package/dist/cjs/postgres/sql-loader.js +27 -41
- package/dist/cjs/postgres/sql-scripts.js +110 -0
- package/dist/cjs/scripts/moveJobsToWait-8.js +21 -0
- package/dist/cjs/scripts/reprocessJob-7.js +20 -13
- package/dist/cjs/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/classes/queue-getters.d.ts +3 -1
- package/dist/esm/classes/queue-getters.js +3 -1
- package/dist/esm/classes/queue.d.ts +2 -1
- package/dist/esm/classes/queue.js +2 -1
- package/dist/esm/classes/redis-connection.d.ts +1 -1
- package/dist/esm/classes/redis-connection.js +1 -1
- package/dist/esm/classes/redis-queue-backend.d.ts +1 -1
- package/dist/esm/classes/redis-queue-backend.js +1 -1
- package/dist/esm/commands/includes/moveChildBackToParentDependencies.lua +20 -0
- package/dist/esm/commands/moveJobsToWait-8.lua +3 -0
- package/dist/esm/commands/reprocessJob-7.lua +2 -14
- package/dist/esm/postgres/sql-loader.d.ts +6 -1
- package/dist/esm/postgres/sql-loader.js +27 -41
- package/dist/esm/postgres/sql-scripts.d.ts +10 -0
- package/dist/esm/postgres/sql-scripts.js +107 -0
- package/dist/esm/scripts/moveJobsToWait-8.js +21 -0
- package/dist/esm/scripts/reprocessJob-7.js +20 -13
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +10 -9
- package/dist/cjs/postgres/commands/add_flow.sql +0 -3
- package/dist/cjs/postgres/commands/add_job.sql +0 -5
- package/dist/cjs/postgres/commands/add_job_scheduler.sql +0 -6
- package/dist/cjs/postgres/commands/add_jobs_bulk.sql +0 -3
- package/dist/cjs/postgres/commands/add_log.sql +0 -14
- package/dist/cjs/postgres/commands/change_delay.sql +0 -3
- package/dist/cjs/postgres/commands/change_priority.sql +0 -3
- package/dist/cjs/postgres/commands/clean.sql +0 -3
- package/dist/cjs/postgres/commands/clear_logs.sql +0 -9
- package/dist/cjs/postgres/commands/collect_metrics.sql +0 -4
- package/dist/cjs/postgres/commands/delete_deduplication_key.sql +0 -6
- package/dist/cjs/postgres/commands/drain.sql +0 -2
- package/dist/cjs/postgres/commands/extend_lock.sql +0 -3
- package/dist/cjs/postgres/commands/extend_locks.sql +0 -25
- package/dist/cjs/postgres/commands/get_client_list.sql +0 -11
- package/dist/cjs/postgres/commands/get_counts.sql +0 -13
- package/dist/cjs/postgres/commands/get_counts_per_priority.sql +0 -14
- package/dist/cjs/postgres/commands/get_deduplication_job_id.sql +0 -7
- package/dist/cjs/postgres/commands/get_dependencies.sql +0 -8
- package/dist/cjs/postgres/commands/get_dependencies_page.sql +0 -7
- package/dist/cjs/postgres/commands/get_dependency_counts.sql +0 -9
- package/dist/cjs/postgres/commands/get_ignored_children_failures.sql +0 -7
- package/dist/cjs/postgres/commands/get_job_data.sql +0 -2
- package/dist/cjs/postgres/commands/get_job_logs_asc.sql +0 -6
- package/dist/cjs/postgres/commands/get_job_logs_count.sql +0 -2
- package/dist/cjs/postgres/commands/get_job_logs_desc.sql +0 -6
- package/dist/cjs/postgres/commands/get_job_scheduler.sql +0 -6
- package/dist/cjs/postgres/commands/get_job_schedulers_count.sql +0 -2
- package/dist/cjs/postgres/commands/get_job_schedulers_range.sql +0 -10
- package/dist/cjs/postgres/commands/get_metrics.sql +0 -14
- package/dist/cjs/postgres/commands/get_processed_children_values.sql +0 -8
- package/dist/cjs/postgres/commands/get_queue_meta.sql +0 -2
- package/dist/cjs/postgres/commands/get_queue_meta_field.sql +0 -2
- package/dist/cjs/postgres/commands/get_queue_meta_fields.sql +0 -3
- package/dist/cjs/postgres/commands/get_range.sql +0 -3
- package/dist/cjs/postgres/commands/get_rate_limit_ttl.sql +0 -3
- package/dist/cjs/postgres/commands/get_state.sql +0 -2
- package/dist/cjs/postgres/commands/has_queue_meta_field.sql +0 -4
- package/dist/cjs/postgres/commands/has_waiting_job.sql +0 -11
- package/dist/cjs/postgres/commands/is_finished.sql +0 -4
- package/dist/cjs/postgres/commands/is_job_in_state.sql +0 -6
- package/dist/cjs/postgres/commands/is_job_in_wait.sql +0 -15
- package/dist/cjs/postgres/commands/is_job_prioritized.sql +0 -6
- package/dist/cjs/postgres/commands/is_job_scheduler.sql +0 -4
- package/dist/cjs/postgres/commands/is_maxed.sql +0 -10
- package/dist/cjs/postgres/commands/listen_events.sql +0 -3
- package/dist/cjs/postgres/commands/listen_jobs.sql +0 -4
- package/dist/cjs/postgres/commands/move_active_to_wait.sql +0 -4
- package/dist/cjs/postgres/commands/move_stalled_jobs_to_wait.sql +0 -4
- package/dist/cjs/postgres/commands/move_to_active.sql +0 -4
- package/dist/cjs/postgres/commands/move_to_completed.sql +0 -4
- package/dist/cjs/postgres/commands/move_to_completed_fetch.sql +0 -9
- package/dist/cjs/postgres/commands/move_to_delayed.sql +0 -4
- package/dist/cjs/postgres/commands/move_to_failed.sql +0 -4
- package/dist/cjs/postgres/commands/move_to_failed_fetch.sql +0 -9
- package/dist/cjs/postgres/commands/move_to_waiting_children.sql +0 -3
- package/dist/cjs/postgres/commands/next_delay.sql +0 -2
- package/dist/cjs/postgres/commands/next_signal.sql +0 -3
- package/dist/cjs/postgres/commands/obliterate.sql +0 -4
- package/dist/cjs/postgres/commands/paginate_dependencies.sql +0 -19
- package/dist/cjs/postgres/commands/pause.sql +0 -2
- package/dist/cjs/postgres/commands/promote.sql +0 -3
- package/dist/cjs/postgres/commands/promote_jobs.sql +0 -3
- package/dist/cjs/postgres/commands/publish_event.sql +0 -3
- package/dist/cjs/postgres/commands/read_events.sql +0 -6
- package/dist/cjs/postgres/commands/read_events_max.sql +0 -3
- package/dist/cjs/postgres/commands/remove.sql +0 -3
- package/dist/cjs/postgres/commands/remove_child_dependency.sql +0 -4
- package/dist/cjs/postgres/commands/remove_deduplication_key.sql +0 -9
- package/dist/cjs/postgres/commands/remove_job_scheduler.sql +0 -3
- package/dist/cjs/postgres/commands/remove_queue_meta_fields.sql +0 -2
- package/dist/cjs/postgres/commands/remove_rate_limit.sql +0 -6
- package/dist/cjs/postgres/commands/remove_unprocessed_children.sql +0 -3
- package/dist/cjs/postgres/commands/reprocess_job.sql +0 -4
- package/dist/cjs/postgres/commands/retry_job.sql +0 -3
- package/dist/cjs/postgres/commands/retry_jobs.sql +0 -3
- package/dist/cjs/postgres/commands/set_queue_meta.sql +0 -4
- package/dist/cjs/postgres/commands/set_rate_limit.sql +0 -3
- package/dist/cjs/postgres/commands/trim_logs.sql +0 -3
- package/dist/cjs/postgres/commands/update_data.sql +0 -3
- package/dist/cjs/postgres/commands/update_job_scheduler.sql +0 -5
- package/dist/cjs/postgres/commands/update_progress.sql +0 -3
- package/dist/cjs/postgres/migrations/0001_schema.sql +0 -348
- package/dist/cjs/postgres/migrations/0002_functions.sql +0 -3473
- package/dist/cjs/postgres/migrations/0003_dedup_stale_key.sql +0 -147
- package/dist/esm/postgres/commands/add_flow.sql +0 -3
- package/dist/esm/postgres/commands/add_job.sql +0 -5
- package/dist/esm/postgres/commands/add_job_scheduler.sql +0 -6
- package/dist/esm/postgres/commands/add_jobs_bulk.sql +0 -3
- package/dist/esm/postgres/commands/add_log.sql +0 -14
- package/dist/esm/postgres/commands/change_delay.sql +0 -3
- package/dist/esm/postgres/commands/change_priority.sql +0 -3
- package/dist/esm/postgres/commands/clean.sql +0 -3
- package/dist/esm/postgres/commands/clear_logs.sql +0 -9
- package/dist/esm/postgres/commands/collect_metrics.sql +0 -4
- package/dist/esm/postgres/commands/delete_deduplication_key.sql +0 -6
- package/dist/esm/postgres/commands/drain.sql +0 -2
- package/dist/esm/postgres/commands/extend_lock.sql +0 -3
- package/dist/esm/postgres/commands/extend_locks.sql +0 -25
- package/dist/esm/postgres/commands/get_client_list.sql +0 -11
- package/dist/esm/postgres/commands/get_counts.sql +0 -13
- package/dist/esm/postgres/commands/get_counts_per_priority.sql +0 -14
- package/dist/esm/postgres/commands/get_deduplication_job_id.sql +0 -7
- package/dist/esm/postgres/commands/get_dependencies.sql +0 -8
- package/dist/esm/postgres/commands/get_dependencies_page.sql +0 -7
- package/dist/esm/postgres/commands/get_dependency_counts.sql +0 -9
- package/dist/esm/postgres/commands/get_ignored_children_failures.sql +0 -7
- package/dist/esm/postgres/commands/get_job_data.sql +0 -2
- package/dist/esm/postgres/commands/get_job_logs_asc.sql +0 -6
- package/dist/esm/postgres/commands/get_job_logs_count.sql +0 -2
- package/dist/esm/postgres/commands/get_job_logs_desc.sql +0 -6
- package/dist/esm/postgres/commands/get_job_scheduler.sql +0 -6
- package/dist/esm/postgres/commands/get_job_schedulers_count.sql +0 -2
- package/dist/esm/postgres/commands/get_job_schedulers_range.sql +0 -10
- package/dist/esm/postgres/commands/get_metrics.sql +0 -14
- package/dist/esm/postgres/commands/get_processed_children_values.sql +0 -8
- package/dist/esm/postgres/commands/get_queue_meta.sql +0 -2
- package/dist/esm/postgres/commands/get_queue_meta_field.sql +0 -2
- package/dist/esm/postgres/commands/get_queue_meta_fields.sql +0 -3
- package/dist/esm/postgres/commands/get_range.sql +0 -3
- package/dist/esm/postgres/commands/get_rate_limit_ttl.sql +0 -3
- package/dist/esm/postgres/commands/get_state.sql +0 -2
- package/dist/esm/postgres/commands/has_queue_meta_field.sql +0 -4
- package/dist/esm/postgres/commands/has_waiting_job.sql +0 -11
- package/dist/esm/postgres/commands/is_finished.sql +0 -4
- package/dist/esm/postgres/commands/is_job_in_state.sql +0 -6
- package/dist/esm/postgres/commands/is_job_in_wait.sql +0 -15
- package/dist/esm/postgres/commands/is_job_prioritized.sql +0 -6
- package/dist/esm/postgres/commands/is_job_scheduler.sql +0 -4
- package/dist/esm/postgres/commands/is_maxed.sql +0 -10
- package/dist/esm/postgres/commands/listen_events.sql +0 -3
- package/dist/esm/postgres/commands/listen_jobs.sql +0 -4
- package/dist/esm/postgres/commands/move_active_to_wait.sql +0 -4
- package/dist/esm/postgres/commands/move_stalled_jobs_to_wait.sql +0 -4
- package/dist/esm/postgres/commands/move_to_active.sql +0 -4
- package/dist/esm/postgres/commands/move_to_completed.sql +0 -4
- package/dist/esm/postgres/commands/move_to_completed_fetch.sql +0 -9
- package/dist/esm/postgres/commands/move_to_delayed.sql +0 -4
- package/dist/esm/postgres/commands/move_to_failed.sql +0 -4
- package/dist/esm/postgres/commands/move_to_failed_fetch.sql +0 -9
- package/dist/esm/postgres/commands/move_to_waiting_children.sql +0 -3
- package/dist/esm/postgres/commands/next_delay.sql +0 -2
- package/dist/esm/postgres/commands/next_signal.sql +0 -3
- package/dist/esm/postgres/commands/obliterate.sql +0 -4
- package/dist/esm/postgres/commands/paginate_dependencies.sql +0 -19
- package/dist/esm/postgres/commands/pause.sql +0 -2
- package/dist/esm/postgres/commands/promote.sql +0 -3
- package/dist/esm/postgres/commands/promote_jobs.sql +0 -3
- package/dist/esm/postgres/commands/publish_event.sql +0 -3
- package/dist/esm/postgres/commands/read_events.sql +0 -6
- package/dist/esm/postgres/commands/read_events_max.sql +0 -3
- package/dist/esm/postgres/commands/remove.sql +0 -3
- package/dist/esm/postgres/commands/remove_child_dependency.sql +0 -4
- package/dist/esm/postgres/commands/remove_deduplication_key.sql +0 -9
- package/dist/esm/postgres/commands/remove_job_scheduler.sql +0 -3
- package/dist/esm/postgres/commands/remove_queue_meta_fields.sql +0 -2
- package/dist/esm/postgres/commands/remove_rate_limit.sql +0 -6
- package/dist/esm/postgres/commands/remove_unprocessed_children.sql +0 -3
- package/dist/esm/postgres/commands/reprocess_job.sql +0 -4
- package/dist/esm/postgres/commands/retry_job.sql +0 -3
- package/dist/esm/postgres/commands/retry_jobs.sql +0 -3
- package/dist/esm/postgres/commands/set_queue_meta.sql +0 -4
- package/dist/esm/postgres/commands/set_rate_limit.sql +0 -3
- package/dist/esm/postgres/commands/trim_logs.sql +0 -3
- package/dist/esm/postgres/commands/update_data.sql +0 -3
- package/dist/esm/postgres/commands/update_job_scheduler.sql +0 -5
- package/dist/esm/postgres/commands/update_progress.sql +0 -3
- package/dist/esm/postgres/migrations/0001_schema.sql +0 -348
- package/dist/esm/postgres/migrations/0002_functions.sql +0 -3473
- package/dist/esm/postgres/migrations/0003_dedup_stale_key.sql +0 -147
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
-- Conditionally remove a deduplication key, only when the given job is still
|
|
2
|
-
-- its (live) winner (mirrors removeDeduplicationKeyIfNeededOnRemoval and the
|
|
3
|
-
-- job-instance Job.removeDeduplicationKey, where Redis GET returns nil for an
|
|
4
|
-
-- expired key). Params: $1 queue, $2 dedup_id, $3 job_id, $4 now (ms). Returns
|
|
5
|
-
-- the removed key (0 or 1 row).
|
|
6
|
-
DELETE FROM dedup
|
|
7
|
-
WHERE queue = $1 AND dedup_id = $2 AND job_id = $3
|
|
8
|
-
AND (expire_at_ms IS NULL OR expire_at_ms > $4)
|
|
9
|
-
RETURNING dedup_id;
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
-- Re-queue a finished job (failed/completed) back to wait. Params: $1 queue,
|
|
2
|
-
-- $2 id, $3 state, $4 lifo, $5 reset_attempts_made, $6 reset_attempts_started.
|
|
3
|
-
-- Returns 1 ok, -1 missing, -3 not in the expected state.
|
|
4
|
-
SELECT reprocess_job($1, $2, $3, $4, $5, $6) AS code;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
-- Advance an existing scheduler to its next iteration (no template change);
|
|
2
|
-
-- returns the new delayed job id, or NULL if the scheduler is gone.
|
|
3
|
-
-- Params: $1 queue, $2 scheduler_id, $3 next_millis, $4 template_data (jsonb),
|
|
4
|
-
-- $5 delayed_opts (jsonb), $6 now, $7 producer_id.
|
|
5
|
-
SELECT update_job_scheduler_next_millis($1, $2, $3, $4, $5, $6, $7) AS job_id;
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
-- BullMQ PostgreSQL backend — schema (types, sequences, tables, indexes).
|
|
2
|
-
--
|
|
3
|
-
-- Consolidated initial schema. All BullMQ objects live in a connection-level
|
|
4
|
-
-- schema (namespace); the operation functions live in 0002_functions.sql.
|
|
5
|
-
|
|
6
|
-
-- BullMQ PostgreSQL backend — initial schema (schema version 1).
|
|
7
|
-
--
|
|
8
|
-
-- This file is the *portable source of truth* for the schema. It uses only
|
|
9
|
-
-- standard SQL / PL-pgSQL so it can be shared verbatim with the future Elixir
|
|
10
|
-
-- and Python ports (which call the very same tables and functions).
|
|
11
|
-
--
|
|
12
|
-
-- All objects are created inside the backend's configured *schema* (the
|
|
13
|
-
-- connection-level namespace, default `bullmq`). The migration runner has
|
|
14
|
-
-- already created the schema and set `search_path` to it, so the unqualified
|
|
15
|
-
-- names below resolve into that schema. Unlike Redis — where a per-queue key
|
|
16
|
-
-- `prefix` namespaces every key — SQL uses the schema as the single namespace
|
|
17
|
-
-- for the whole connection, so there is no per-row/per-queue prefix.
|
|
18
|
-
--
|
|
19
|
-
-- The migration ledger table (`migration`) is bootstrapped by the
|
|
20
|
-
-- migration runner itself, so it is intentionally not created here.
|
|
21
|
-
--
|
|
22
|
-
-- v1 only establishes the foundation that is independent of the job-storage
|
|
23
|
-
-- model: per-queue metadata. Subsequent migrations add the job tables,
|
|
24
|
-
-- indexes and the atomic operation functions.
|
|
25
|
-
|
|
26
|
-
-- Per-queue metadata. Mirrors the Redis `<prefix>:<queue>:meta` hash: a small
|
|
27
|
-
-- key/value store keyed by (queue, field). Used for the queue version, global
|
|
28
|
-
-- concurrency, global rate limit, paused flag, etc.
|
|
29
|
-
CREATE TABLE IF NOT EXISTS meta (
|
|
30
|
-
queue text NOT NULL,
|
|
31
|
-
field text NOT NULL,
|
|
32
|
-
value text,
|
|
33
|
-
PRIMARY KEY (queue, field)
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
-- BullMQ PostgreSQL backend — core job schema (schema version 2).
|
|
37
|
-
--
|
|
38
|
-
-- Portable source of truth (standard SQL / PL-pgSQL) shared verbatim with the
|
|
39
|
-
-- future Elixir and Python ports. This migration establishes the *storage
|
|
40
|
-
-- model* (tables + indexes) for every BullMQ feature: FIFO/LIFO, priority,
|
|
41
|
-
-- delayed jobs, concurrency, locks/stalled detection, flows (parent/child
|
|
42
|
-
-- dependencies), deduplication, rate limiting, repeatable job schedulers, the
|
|
43
|
-
-- event stream and metrics. The atomic *operation* functions (addJob,
|
|
44
|
-
-- moveToActive, …) are layered on top in subsequent migrations.
|
|
45
|
-
--
|
|
46
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
47
|
-
-- Design overview
|
|
48
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
49
|
-
-- * Namespace = the connection's PostgreSQL *schema* (default `bullmq`), not a
|
|
50
|
-
-- per-queue key prefix. In Redis a `prefix` namespaces every key because the
|
|
51
|
-
-- keyspace is shared; in SQL the schema already isolates BullMQ from the
|
|
52
|
-
-- user's other tables, and a different schema (or database) gives a fully
|
|
53
|
-
-- independent BullMQ namespace. So there is no per-row/per-queue prefix — the
|
|
54
|
-
-- `queue` column alone discriminates within the schema.
|
|
55
|
-
--
|
|
56
|
-
-- * Single `job` table keyed by (queue, id). The Redis adapter spreads a
|
|
57
|
-
-- job's lifecycle across many keys (wait list, prioritized zset, delayed zset,
|
|
58
|
-
-- active list, completed/failed zsets, locks, …); here a single `state`
|
|
59
|
-
-- column plus a handful of promoted, indexable columns replaces all of them.
|
|
60
|
-
-- State transitions are plain `UPDATE`s; claiming uses `FOR UPDATE SKIP
|
|
61
|
-
-- LOCKED` so concurrent workers never block each other.
|
|
62
|
-
--
|
|
63
|
-
-- * Time is stored as `bigint` epoch-milliseconds (suffix `_ms`) to match
|
|
64
|
-
-- BullMQ's millisecond API exactly and avoid timezone math on hot paths.
|
|
65
|
-
--
|
|
66
|
-
-- * Partial indexes are state-scoped: each hot path (claim next ready job,
|
|
67
|
-
-- promote due delayed jobs, find stalled active jobs, clean finished jobs)
|
|
68
|
-
-- has a small index that only contains the rows in the relevant state.
|
|
69
|
-
--
|
|
70
|
-
-- * "paused" and "prioritized" are NOT physical states. Pausing is an O(1)
|
|
71
|
-
-- queue-level flag in `meta`; a prioritized job is simply a waiting job
|
|
72
|
-
-- with `priority > 0`. This avoids the bulk row rewrites Redis performs on
|
|
73
|
-
-- pause and removes the need for a separate prioritized structure — full
|
|
74
|
-
-- `ORDER BY (priority, seq)` does the job.
|
|
75
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
76
|
-
|
|
77
|
-
-- Physical job states. (Pause = meta flag; prioritized = waiting + priority>0.)
|
|
78
|
-
CREATE TYPE job_state AS ENUM (
|
|
79
|
-
'waiting',
|
|
80
|
-
'active',
|
|
81
|
-
'completed',
|
|
82
|
-
'failed',
|
|
83
|
-
'delayed',
|
|
84
|
-
'waiting-children'
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
-- Status of a parent→child dependency (flows).
|
|
88
|
-
CREATE TYPE dep_status AS ENUM (
|
|
89
|
-
'pending', -- child not finished yet
|
|
90
|
-
'processed', -- child completed; `value` holds its return value
|
|
91
|
-
'ignored', -- child failed but parent was told to ignore it; `value` = reason
|
|
92
|
-
'failed' -- child failed and the failure is recorded against the parent
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
-- Global monotonic ordering for FIFO. A single sequence gives a total order
|
|
96
|
-
-- across all queues; FIFO within a queue is preserved because `seq` increases
|
|
97
|
-
-- with insertion. LIFO jobs are stored with a *negative* `seq` (from the same
|
|
98
|
-
-- sequence, negated) so they sort ahead of all FIFO jobs and, among themselves,
|
|
99
|
-
-- most-recently-added first — reproducing "last in, first out" with one column.
|
|
100
|
-
CREATE SEQUENCE job_seq;
|
|
101
|
-
|
|
102
|
-
-- Global monotonic id for the event stream (the Postgres analogue of a Redis
|
|
103
|
-
-- stream entry id). Consumers page forward with `id > cursor`.
|
|
104
|
-
CREATE SEQUENCE event_seq;
|
|
105
|
-
|
|
106
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
107
|
-
-- Jobs
|
|
108
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
109
|
-
CREATE TABLE job (
|
|
110
|
-
queue text NOT NULL,
|
|
111
|
-
id text NOT NULL, -- custom id, or numeric counter as text
|
|
112
|
-
seq bigint NOT NULL, -- FIFO order (negative = LIFO); see sequence above
|
|
113
|
-
name text NOT NULL,
|
|
114
|
-
state job_state NOT NULL,
|
|
115
|
-
|
|
116
|
-
-- Payload / options. BullMQ serializes these as JSON strings; stored as jsonb
|
|
117
|
-
-- (always valid JSON) so they are queryable and compact.
|
|
118
|
-
data jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
119
|
-
opts jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
120
|
-
|
|
121
|
-
-- Promoted, frequently-read scalars (also live implicitly in `opts`, but are
|
|
122
|
-
-- broken out so they can be indexed / updated cheaply).
|
|
123
|
-
priority integer NOT NULL DEFAULT 0, -- 0 = no priority (FIFO); >0 = prioritized
|
|
124
|
-
delay_ms bigint NOT NULL DEFAULT 0,
|
|
125
|
-
max_attempts integer NOT NULL DEFAULT 1,
|
|
126
|
-
attempts_made integer NOT NULL DEFAULT 0,
|
|
127
|
-
attempts_started integer NOT NULL DEFAULT 0,
|
|
128
|
-
|
|
129
|
-
-- Progress / results.
|
|
130
|
-
progress jsonb, -- number or object
|
|
131
|
-
return_value jsonb, -- BullMQ `returnvalue`
|
|
132
|
-
failed_reason text,
|
|
133
|
-
stacktrace jsonb, -- array of frames
|
|
134
|
-
deferred_failure text,
|
|
135
|
-
processed_by text, -- worker id that processed it
|
|
136
|
-
|
|
137
|
-
-- Timestamps (epoch ms).
|
|
138
|
-
added_at_ms bigint NOT NULL, -- BullMQ `timestamp`
|
|
139
|
-
process_at_ms bigint, -- when a delayed job becomes ready
|
|
140
|
-
processed_at_ms bigint, -- BullMQ `processedOn`
|
|
141
|
-
finished_at_ms bigint, -- BullMQ `finishedOn`
|
|
142
|
-
|
|
143
|
-
-- Locking / stalled detection. An active job is "locked" until
|
|
144
|
-
-- `locked_until_ms`; once it elapses the job is considered stalled.
|
|
145
|
-
lock_token text,
|
|
146
|
-
locked_until_ms bigint,
|
|
147
|
-
stalled_count integer NOT NULL DEFAULT 0,
|
|
148
|
-
|
|
149
|
-
-- Deduplication / scheduler linkage.
|
|
150
|
-
dedup_id text, -- BullMQ deduplicationId / debounceId
|
|
151
|
-
scheduler_id text, -- id of the scheduler that produced this job (BullMQ repeatJobKey)
|
|
152
|
-
|
|
153
|
-
-- Flow (parent/child) linkage. A child stores its parent's coordinates; a
|
|
154
|
-
-- parent tracks how many children are still unfinished.
|
|
155
|
-
parent_queue text,
|
|
156
|
-
parent_id text,
|
|
157
|
-
parent_key text, -- denormalized "<queue>:<id>" (Redis-compatible "<prefix>:<queue>:<id>")
|
|
158
|
-
pending_deps integer NOT NULL DEFAULT 0,
|
|
159
|
-
-- Two-phase stalled mark/sweep: set on the first stalled pass, reclaimed on
|
|
160
|
-
-- the next (see move_stalled_jobs_to_wait in 0002_functions.sql).
|
|
161
|
-
stalled_marked boolean NOT NULL DEFAULT false,
|
|
162
|
-
|
|
163
|
-
PRIMARY KEY (queue, id)
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
-- Claim next ready job: waiting jobs ordered by (priority, seq). Covers both the
|
|
167
|
-
-- "wait" (priority = 0) and "prioritized" (priority > 0) views, and the FIFO/LIFO
|
|
168
|
-
-- ordering via the signed `seq`. Used with FOR UPDATE SKIP LOCKED.
|
|
169
|
-
CREATE INDEX job_ready_idx
|
|
170
|
-
ON job (queue, priority, seq)
|
|
171
|
-
WHERE state = 'waiting';
|
|
172
|
-
|
|
173
|
-
-- Promote due delayed jobs (process_at_ms <= now) and find the next wake-up.
|
|
174
|
-
CREATE INDEX job_delayed_idx
|
|
175
|
-
ON job (queue, process_at_ms)
|
|
176
|
-
WHERE state = 'delayed';
|
|
177
|
-
|
|
178
|
-
-- Active jobs: stalled scan by lock expiry, and O(1)-ish concurrency counting
|
|
179
|
-
-- (COUNT over the partial index for a queue).
|
|
180
|
-
CREATE INDEX job_active_idx
|
|
181
|
-
ON job (queue, locked_until_ms)
|
|
182
|
-
WHERE state = 'active';
|
|
183
|
-
|
|
184
|
-
-- Finished jobs: range listing and age/count-based retention + cleaning.
|
|
185
|
-
CREATE INDEX job_finished_idx
|
|
186
|
-
ON job (queue, state, finished_at_ms)
|
|
187
|
-
WHERE state IN ('completed', 'failed');
|
|
188
|
-
|
|
189
|
-
-- Parents blocked on children.
|
|
190
|
-
CREATE INDEX job_waiting_children_idx
|
|
191
|
-
ON job (queue, seq)
|
|
192
|
-
WHERE state = 'waiting-children';
|
|
193
|
-
|
|
194
|
-
-- Reverse lookup from a child to its parent (e.g. orphan/repair scans).
|
|
195
|
-
CREATE INDEX job_parent_idx
|
|
196
|
-
ON job (parent_queue, parent_id)
|
|
197
|
-
WHERE parent_id IS NOT NULL;
|
|
198
|
-
|
|
199
|
-
-- Locate the job(s) produced by a given scheduler.
|
|
200
|
-
CREATE INDEX job_scheduler_idx
|
|
201
|
-
ON job (queue, scheduler_id)
|
|
202
|
-
WHERE scheduler_id IS NOT NULL;
|
|
203
|
-
|
|
204
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
205
|
-
-- Job logs (append-only, per-job ordered lines)
|
|
206
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
207
|
-
CREATE TABLE job_log (
|
|
208
|
-
queue text NOT NULL,
|
|
209
|
-
job_id text NOT NULL,
|
|
210
|
-
idx bigint NOT NULL, -- per-job ordinal (0-based); range reads + trimming
|
|
211
|
-
row text NOT NULL,
|
|
212
|
-
PRIMARY KEY (queue, job_id, idx),
|
|
213
|
-
FOREIGN KEY (queue, job_id)
|
|
214
|
-
REFERENCES job (queue, id) ON DELETE CASCADE
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
218
|
-
-- Flow dependencies (parent → child)
|
|
219
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
220
|
-
-- Owned by the parent. Reproduces BullMQ's processed/ignored/failed/unprocessed
|
|
221
|
-
-- child sets with a single `status` column, and stores each processed child's
|
|
222
|
-
-- return value (or failure reason) in `value`. `pending_deps` on the parent job
|
|
223
|
-
-- is the count of rows here still in 'pending'.
|
|
224
|
-
CREATE TABLE job_dependency (
|
|
225
|
-
parent_queue text NOT NULL,
|
|
226
|
-
parent_id text NOT NULL,
|
|
227
|
-
child_queue text NOT NULL,
|
|
228
|
-
child_id text NOT NULL,
|
|
229
|
-
child_key text NOT NULL, -- denormalized "<queue>:<id>" (Redis-compatible "<prefix>:<queue>:<id>")
|
|
230
|
-
status dep_status NOT NULL DEFAULT 'pending',
|
|
231
|
-
value jsonb,
|
|
232
|
-
PRIMARY KEY (parent_queue, parent_id, child_key),
|
|
233
|
-
FOREIGN KEY (parent_queue, parent_id)
|
|
234
|
-
REFERENCES job (queue, id) ON DELETE CASCADE
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
-- Paginate a parent's children by category (processed / pending / …).
|
|
238
|
-
CREATE INDEX dep_parent_status_idx
|
|
239
|
-
ON job_dependency (parent_queue, parent_id, status);
|
|
240
|
-
|
|
241
|
-
-- Resolve dependencies from the child side (when a child finishes, or when a
|
|
242
|
-
-- child dependency is explicitly removed).
|
|
243
|
-
CREATE INDEX dep_child_idx
|
|
244
|
-
ON job_dependency (child_queue, child_id);
|
|
245
|
-
|
|
246
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
247
|
-
-- Event stream (QueueEvents)
|
|
248
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
249
|
-
-- The Postgres analogue of the Redis events stream. `id` is globally monotonic;
|
|
250
|
-
-- consumers page forward with `id > cursor`. Blocking reads are delivered via
|
|
251
|
-
-- LISTEN/NOTIFY (the operation layer NOTIFYs on insert); trimming deletes the
|
|
252
|
-
-- lowest ids beyond the configured max length.
|
|
253
|
-
CREATE TABLE event (
|
|
254
|
-
queue text NOT NULL,
|
|
255
|
-
id bigint NOT NULL DEFAULT nextval('event_seq'),
|
|
256
|
-
event text NOT NULL,
|
|
257
|
-
data jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
258
|
-
created_at_ms bigint NOT NULL,
|
|
259
|
-
PRIMARY KEY (queue, id)
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
CREATE TABLE metrics (
|
|
263
|
-
queue text NOT NULL,
|
|
264
|
-
kind text NOT NULL, -- 'completed' | 'failed'
|
|
265
|
-
count bigint NOT NULL DEFAULT 0, -- cumulative finished jobs
|
|
266
|
-
prev_ts bigint, -- ts of the last data point
|
|
267
|
-
prev_count bigint NOT NULL DEFAULT 0, -- count at the last data point
|
|
268
|
-
data bigint[] NOT NULL DEFAULT '{}',-- per-minute deltas, newest first
|
|
269
|
-
PRIMARY KEY (queue, kind)
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
273
|
-
-- Rate limiting (token window per queue)
|
|
274
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
275
|
-
-- One row per queue holding the current limiter window: `points` counts the
|
|
276
|
-
-- jobs consumed in the window, which ends at `expire_at_ms`. Mirrors the single
|
|
277
|
-
-- Redis `<prefix>:<queue>:limiter` counter key (with its PTTL). The global
|
|
278
|
-
-- limiter is the only mode the open-source backend supports (per-group rate
|
|
279
|
-
-- limiting was removed in BullMQ 3.0).
|
|
280
|
-
CREATE TABLE rate_limit (
|
|
281
|
-
queue text NOT NULL,
|
|
282
|
-
points bigint NOT NULL DEFAULT 0,
|
|
283
|
-
expire_at_ms bigint NOT NULL,
|
|
284
|
-
PRIMARY KEY (queue)
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
288
|
-
-- Deduplication keys
|
|
289
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
290
|
-
CREATE TABLE dedup (
|
|
291
|
-
queue text NOT NULL,
|
|
292
|
-
dedup_id text NOT NULL,
|
|
293
|
-
job_id text NOT NULL,
|
|
294
|
-
expire_at_ms bigint, -- NULL = no expiry; otherwise lazily reclaimed
|
|
295
|
-
PRIMARY KEY (queue, dedup_id)
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
-- Expired dedup keys are reclaimed lazily by operation functions in
|
|
299
|
-
-- 0002_functions.sql (for example deduplicate_job and
|
|
300
|
-
-- dedup_finalize). This index keeps those lookups/deletes efficient.
|
|
301
|
-
CREATE INDEX dedup_expire_idx
|
|
302
|
-
ON dedup (queue, expire_at_ms)
|
|
303
|
-
WHERE expire_at_ms IS NOT NULL;
|
|
304
|
-
|
|
305
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
306
|
-
-- Job schedulers (repeatable jobs)
|
|
307
|
-
--
|
|
308
|
-
-- A scheduler is a job factory: it stores a template (data/opts) plus a repeat
|
|
309
|
-
-- spec (cron `pattern` or fixed `every` ms) and, on each upsert, produces the
|
|
310
|
-
-- next delayed job `repeat:<schedulerId>:<nextMillis>`. For cron the caller
|
|
311
|
-
-- computes nextMillis (JS cron-parser); for `every` the backend computes it,
|
|
312
|
-
-- honouring `offset_ms` (the phase offset from `start_date`).
|
|
313
|
-
-- ──────────────────────────────────────────────────────────────────────────
|
|
314
|
-
CREATE TABLE scheduler (
|
|
315
|
-
queue text NOT NULL,
|
|
316
|
-
scheduler_id text NOT NULL,
|
|
317
|
-
name text,
|
|
318
|
-
next_run_ms bigint, -- next iteration's due time
|
|
319
|
-
pattern text, -- cron expression (mutually exclusive with every_ms)
|
|
320
|
-
every_ms bigint, -- fixed interval in ms
|
|
321
|
-
tz text, -- timezone for cron evaluation
|
|
322
|
-
start_date_ms bigint,
|
|
323
|
-
end_date_ms bigint,
|
|
324
|
-
limit_count integer, -- max number of iterations (NULL = unlimited)
|
|
325
|
-
iteration_count integer NOT NULL DEFAULT 0,
|
|
326
|
-
template_data jsonb, -- payload template reused for every iteration
|
|
327
|
-
template_opts jsonb, -- options template reused for every iteration
|
|
328
|
-
producer_id text,
|
|
329
|
-
offset_ms bigint, -- 'every' phase offset from start_date
|
|
330
|
-
PRIMARY KEY (queue, scheduler_id)
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
-- Find schedulers whose next iteration is due, and order schedulers by next run.
|
|
334
|
-
CREATE INDEX scheduler_next_idx
|
|
335
|
-
ON scheduler (queue, next_run_ms);
|
|
336
|
-
|
|
337
|
-
-- ── keepLastIfActive proto-next storage ──────────────────────────────────
|
|
338
|
-
-- When a job is deduplicated while its winner is *active* and keepLastIfActive
|
|
339
|
-
-- is set, the new job's payload is stashed here (Redis `dn:<id>` hash) and the
|
|
340
|
-
-- dedup key is persisted (no expiry). When the active winner finishes, the
|
|
341
|
-
-- stashed payload is turned into a real job (the new winner). At most one
|
|
342
|
-
-- proto-next exists per id; a later add while active overwrites it.
|
|
343
|
-
CREATE TABLE dedup_next (
|
|
344
|
-
queue text NOT NULL,
|
|
345
|
-
dedup_id text NOT NULL,
|
|
346
|
-
payload jsonb NOT NULL, -- { name, data, opts, jobId }
|
|
347
|
-
PRIMARY KEY (queue, dedup_id)
|
|
348
|
-
);
|