mysql-boss 0.1.0
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/LICENSE +21 -0
- package/README.md +688 -0
- package/dist/cjs/archive.d.ts +8 -0
- package/dist/cjs/archive.d.ts.map +1 -0
- package/dist/cjs/archive.js +82 -0
- package/dist/cjs/archive.js.map +1 -0
- package/dist/cjs/boss.d.ts +45 -0
- package/dist/cjs/boss.d.ts.map +1 -0
- package/dist/cjs/boss.js +205 -0
- package/dist/cjs/boss.js.map +1 -0
- package/dist/cjs/claim.d.ts +5 -0
- package/dist/cjs/claim.d.ts.map +1 -0
- package/dist/cjs/claim.js +47 -0
- package/dist/cjs/claim.js.map +1 -0
- package/dist/cjs/connection.d.ts +4 -0
- package/dist/cjs/connection.d.ts.map +1 -0
- package/dist/cjs/connection.js +26 -0
- package/dist/cjs/connection.js.map +1 -0
- package/dist/cjs/cron/next.d.ts +3 -0
- package/dist/cjs/cron/next.d.ts.map +1 -0
- package/dist/cjs/cron/next.js +179 -0
- package/dist/cjs/cron/next.js.map +1 -0
- package/dist/cjs/cron/parse.d.ts +11 -0
- package/dist/cjs/cron/parse.d.ts.map +1 -0
- package/dist/cjs/cron/parse.js +99 -0
- package/dist/cjs/cron/parse.js.map +1 -0
- package/dist/cjs/dlq.d.ts +11 -0
- package/dist/cjs/dlq.d.ts.map +1 -0
- package/dist/cjs/dlq.js +92 -0
- package/dist/cjs/dlq.js.map +1 -0
- package/dist/cjs/errors.d.ts +10 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +25 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/fail.d.ts +8 -0
- package/dist/cjs/fail.d.ts.map +1 -0
- package/dist/cjs/fail.js +19 -0
- package/dist/cjs/fail.js.map +1 -0
- package/dist/cjs/heartbeat.d.ts +4 -0
- package/dist/cjs/heartbeat.d.ts.map +1 -0
- package/dist/cjs/heartbeat.js +18 -0
- package/dist/cjs/heartbeat.js.map +1 -0
- package/dist/cjs/index.d.ts +70 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/migrate.d.ts +4 -0
- package/dist/cjs/migrate.d.ts.map +1 -0
- package/dist/cjs/migrate.js +14 -0
- package/dist/cjs/migrate.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/retry-util.d.ts +2 -0
- package/dist/cjs/retry-util.d.ts.map +1 -0
- package/dist/cjs/retry-util.js +31 -0
- package/dist/cjs/retry-util.js.map +1 -0
- package/dist/cjs/sql.d.ts +82 -0
- package/dist/cjs/sql.d.ts.map +1 -0
- package/dist/cjs/sql.js +396 -0
- package/dist/cjs/sql.js.map +1 -0
- package/dist/cjs/sweep.d.ts +4 -0
- package/dist/cjs/sweep.d.ts.map +1 -0
- package/dist/cjs/sweep.js +46 -0
- package/dist/cjs/sweep.js.map +1 -0
- package/dist/cjs/tick.d.ts +11 -0
- package/dist/cjs/tick.d.ts.map +1 -0
- package/dist/cjs/tick.js +82 -0
- package/dist/cjs/tick.js.map +1 -0
- package/dist/cjs/util.d.ts +2 -0
- package/dist/cjs/util.d.ts.map +1 -0
- package/dist/cjs/util.js +7 -0
- package/dist/cjs/util.js.map +1 -0
- package/dist/cjs/worker.d.ts +42 -0
- package/dist/cjs/worker.d.ts.map +1 -0
- package/dist/cjs/worker.js +232 -0
- package/dist/cjs/worker.js.map +1 -0
- package/dist/esm/archive.d.ts +8 -0
- package/dist/esm/archive.d.ts.map +1 -0
- package/dist/esm/archive.js +76 -0
- package/dist/esm/archive.js.map +1 -0
- package/dist/esm/boss.d.ts +45 -0
- package/dist/esm/boss.d.ts.map +1 -0
- package/dist/esm/boss.js +201 -0
- package/dist/esm/boss.js.map +1 -0
- package/dist/esm/claim.d.ts +5 -0
- package/dist/esm/claim.d.ts.map +1 -0
- package/dist/esm/claim.js +44 -0
- package/dist/esm/claim.js.map +1 -0
- package/dist/esm/connection.d.ts +4 -0
- package/dist/esm/connection.d.ts.map +1 -0
- package/dist/esm/connection.js +22 -0
- package/dist/esm/connection.js.map +1 -0
- package/dist/esm/cron/next.d.ts +3 -0
- package/dist/esm/cron/next.d.ts.map +1 -0
- package/dist/esm/cron/next.js +176 -0
- package/dist/esm/cron/next.js.map +1 -0
- package/dist/esm/cron/parse.d.ts +11 -0
- package/dist/esm/cron/parse.d.ts.map +1 -0
- package/dist/esm/cron/parse.js +96 -0
- package/dist/esm/cron/parse.js.map +1 -0
- package/dist/esm/dlq.d.ts +11 -0
- package/dist/esm/dlq.d.ts.map +1 -0
- package/dist/esm/dlq.js +87 -0
- package/dist/esm/dlq.js.map +1 -0
- package/dist/esm/errors.d.ts +10 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +19 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/fail.d.ts +8 -0
- package/dist/esm/fail.d.ts.map +1 -0
- package/dist/esm/fail.js +16 -0
- package/dist/esm/fail.js.map +1 -0
- package/dist/esm/heartbeat.d.ts +4 -0
- package/dist/esm/heartbeat.d.ts.map +1 -0
- package/dist/esm/heartbeat.js +15 -0
- package/dist/esm/heartbeat.js.map +1 -0
- package/dist/esm/index.d.ts +70 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/migrate.d.ts +4 -0
- package/dist/esm/migrate.d.ts.map +1 -0
- package/dist/esm/migrate.js +11 -0
- package/dist/esm/migrate.js.map +1 -0
- package/dist/esm/retry-util.d.ts +2 -0
- package/dist/esm/retry-util.d.ts.map +1 -0
- package/dist/esm/retry-util.js +28 -0
- package/dist/esm/retry-util.js.map +1 -0
- package/dist/esm/sql.d.ts +82 -0
- package/dist/esm/sql.d.ts.map +1 -0
- package/dist/esm/sql.js +392 -0
- package/dist/esm/sql.js.map +1 -0
- package/dist/esm/sweep.d.ts +4 -0
- package/dist/esm/sweep.d.ts.map +1 -0
- package/dist/esm/sweep.js +43 -0
- package/dist/esm/sweep.js.map +1 -0
- package/dist/esm/tick.d.ts +11 -0
- package/dist/esm/tick.d.ts.map +1 -0
- package/dist/esm/tick.js +77 -0
- package/dist/esm/tick.js.map +1 -0
- package/dist/esm/util.d.ts +2 -0
- package/dist/esm/util.d.ts.map +1 -0
- package/dist/esm/util.js +4 -0
- package/dist/esm/util.js.map +1 -0
- package/dist/esm/worker.d.ts +42 -0
- package/dist/esm/worker.d.ts.map +1 -0
- package/dist/esm/worker.js +228 -0
- package/dist/esm/worker.js.map +1 -0
- package/package.json +64 -0
package/dist/esm/sql.js
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// §1.1 — DDL
|
|
2
|
+
export const CREATE_JOBS = `
|
|
3
|
+
CREATE TABLE IF NOT EXISTS jobs (
|
|
4
|
+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
5
|
+
queue VARCHAR(128) NOT NULL,
|
|
6
|
+
state ENUM('available','active') NOT NULL DEFAULT 'available',
|
|
7
|
+
priority SMALLINT NOT NULL DEFAULT 0,
|
|
8
|
+
payload JSON NULL,
|
|
9
|
+
singleton_key VARCHAR(191) NULL,
|
|
10
|
+
retry_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
11
|
+
retry_limit INT UNSIGNED NOT NULL DEFAULT 2,
|
|
12
|
+
retry_delay_secs INT UNSIGNED NOT NULL DEFAULT 30,
|
|
13
|
+
retry_backoff TINYINT(1) NOT NULL DEFAULT 0,
|
|
14
|
+
run_at DATETIME(6) NOT NULL,
|
|
15
|
+
created_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)),
|
|
16
|
+
started_at DATETIME(6) NULL,
|
|
17
|
+
locked_by BINARY(16) NULL,
|
|
18
|
+
lease_expires_at DATETIME(6) NULL,
|
|
19
|
+
last_error JSON NULL,
|
|
20
|
+
PRIMARY KEY (id),
|
|
21
|
+
UNIQUE KEY uq_jobs_singleton (queue, singleton_key),
|
|
22
|
+
KEY ix_jobs_dequeue (queue, state, priority DESC, run_at, id),
|
|
23
|
+
KEY ix_jobs_lease (state, lease_expires_at)
|
|
24
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
25
|
+
`;
|
|
26
|
+
export const CREATE_JOBS_ARCHIVE = `
|
|
27
|
+
CREATE TABLE IF NOT EXISTS jobs_archive (
|
|
28
|
+
id BIGINT UNSIGNED NOT NULL,
|
|
29
|
+
queue VARCHAR(128) NOT NULL,
|
|
30
|
+
priority SMALLINT NOT NULL,
|
|
31
|
+
payload JSON NULL,
|
|
32
|
+
singleton_key VARCHAR(191) NULL,
|
|
33
|
+
retry_count INT UNSIGNED NOT NULL,
|
|
34
|
+
created_at DATETIME(6) NOT NULL,
|
|
35
|
+
started_at DATETIME(6) NOT NULL,
|
|
36
|
+
completed_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)),
|
|
37
|
+
duration_ms INT UNSIGNED NOT NULL,
|
|
38
|
+
PRIMARY KEY (id),
|
|
39
|
+
KEY ix_archive_queue_time (queue, completed_at),
|
|
40
|
+
KEY ix_archive_time (completed_at)
|
|
41
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
42
|
+
`;
|
|
43
|
+
export const CREATE_JOBS_DEAD = `
|
|
44
|
+
CREATE TABLE IF NOT EXISTS jobs_dead (
|
|
45
|
+
id BIGINT UNSIGNED NOT NULL,
|
|
46
|
+
queue VARCHAR(128) NOT NULL,
|
|
47
|
+
priority SMALLINT NOT NULL,
|
|
48
|
+
payload JSON NULL,
|
|
49
|
+
singleton_key VARCHAR(191) NULL,
|
|
50
|
+
retry_count INT UNSIGNED NOT NULL,
|
|
51
|
+
retry_limit INT UNSIGNED NOT NULL,
|
|
52
|
+
retry_delay_secs INT UNSIGNED NOT NULL,
|
|
53
|
+
retry_backoff TINYINT(1) NOT NULL,
|
|
54
|
+
created_at DATETIME(6) NOT NULL,
|
|
55
|
+
failed_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6)),
|
|
56
|
+
last_error JSON NULL,
|
|
57
|
+
PRIMARY KEY (id),
|
|
58
|
+
KEY ix_dead_queue_time (queue, failed_at)
|
|
59
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
60
|
+
`;
|
|
61
|
+
export const CREATE_SCHEDULES = `
|
|
62
|
+
CREATE TABLE IF NOT EXISTS schedules (
|
|
63
|
+
name VARCHAR(128) NOT NULL,
|
|
64
|
+
queue VARCHAR(128) NOT NULL,
|
|
65
|
+
cron VARCHAR(64) NOT NULL,
|
|
66
|
+
timezone VARCHAR(64) NOT NULL DEFAULT 'UTC',
|
|
67
|
+
payload JSON NULL,
|
|
68
|
+
job_options JSON NULL,
|
|
69
|
+
next_run_at DATETIME(6) NOT NULL,
|
|
70
|
+
last_enqueued_at DATETIME(6) NULL,
|
|
71
|
+
updated_at DATETIME(6) NOT NULL DEFAULT (UTC_TIMESTAMP(6))
|
|
72
|
+
ON UPDATE CURRENT_TIMESTAMP(6),
|
|
73
|
+
PRIMARY KEY (name),
|
|
74
|
+
KEY ix_schedules_due (next_run_at)
|
|
75
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
|
76
|
+
`;
|
|
77
|
+
// §1.2 — Enqueue
|
|
78
|
+
export const ENQUEUE = `
|
|
79
|
+
INSERT INTO jobs
|
|
80
|
+
(queue, priority, payload, singleton_key,
|
|
81
|
+
retry_limit, retry_delay_secs, retry_backoff, run_at)
|
|
82
|
+
VALUES
|
|
83
|
+
(?, ?, ?, ?, ?, ?, ?, COALESCE(?, UTC_TIMESTAMP(6)));
|
|
84
|
+
`;
|
|
85
|
+
export const ENQUEUE_SINGLETON = `
|
|
86
|
+
INSERT IGNORE INTO jobs
|
|
87
|
+
(queue, priority, payload, singleton_key,
|
|
88
|
+
retry_limit, retry_delay_secs, retry_backoff, run_at)
|
|
89
|
+
VALUES
|
|
90
|
+
(?, ?, ?, ?, ?, ?, ?, COALESCE(?, UTC_TIMESTAMP(6)));
|
|
91
|
+
`;
|
|
92
|
+
export const LAST_INSERT_ID = `
|
|
93
|
+
SELECT CAST(LAST_INSERT_ID() AS CHAR) AS id;
|
|
94
|
+
`;
|
|
95
|
+
// §1.2 — Dequeue / claim
|
|
96
|
+
export const CLAIM_SELECT = `
|
|
97
|
+
SELECT CAST(id AS CHAR) AS id, queue, payload, retry_count, retry_limit,
|
|
98
|
+
retry_delay_secs, retry_backoff
|
|
99
|
+
FROM jobs
|
|
100
|
+
WHERE queue = ?
|
|
101
|
+
AND state = 'available'
|
|
102
|
+
AND run_at <= UTC_TIMESTAMP(6)
|
|
103
|
+
ORDER BY priority DESC, run_at, id
|
|
104
|
+
LIMIT ?
|
|
105
|
+
FOR UPDATE SKIP LOCKED;
|
|
106
|
+
`;
|
|
107
|
+
export const CLAIM_UPDATE = `
|
|
108
|
+
UPDATE jobs
|
|
109
|
+
SET state = 'active',
|
|
110
|
+
started_at = UTC_TIMESTAMP(6),
|
|
111
|
+
locked_by = UUID_TO_BIN(?),
|
|
112
|
+
lease_expires_at = UTC_TIMESTAMP(6) + INTERVAL ? SECOND
|
|
113
|
+
WHERE id IN (?);
|
|
114
|
+
`;
|
|
115
|
+
// §1.2 — Complete (archive move)
|
|
116
|
+
export const COMPLETE_ARCHIVE = `
|
|
117
|
+
INSERT INTO jobs_archive
|
|
118
|
+
(id, queue, priority, payload, singleton_key, retry_count,
|
|
119
|
+
created_at, started_at, duration_ms)
|
|
120
|
+
SELECT id, queue, priority, payload, singleton_key, retry_count,
|
|
121
|
+
created_at, started_at,
|
|
122
|
+
TIMESTAMPDIFF(MICROSECOND, started_at, UTC_TIMESTAMP(6)) DIV 1000
|
|
123
|
+
FROM jobs
|
|
124
|
+
WHERE id = ? AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
125
|
+
`;
|
|
126
|
+
export const COMPLETE_DELETE = `
|
|
127
|
+
DELETE FROM jobs
|
|
128
|
+
WHERE id = ? AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
129
|
+
`;
|
|
130
|
+
// §2.2 — Fail with retries remaining
|
|
131
|
+
export const FAIL_RETRY = `
|
|
132
|
+
UPDATE jobs
|
|
133
|
+
SET state = 'available',
|
|
134
|
+
run_at = UTC_TIMESTAMP(6) + INTERVAL
|
|
135
|
+
LEAST(
|
|
136
|
+
86400,
|
|
137
|
+
IF(retry_backoff = 1,
|
|
138
|
+
retry_delay_secs * POW(2, retry_count),
|
|
139
|
+
retry_delay_secs)
|
|
140
|
+
+ FLOOR(RAND() * retry_delay_secs)
|
|
141
|
+
) SECOND,
|
|
142
|
+
retry_count = retry_count + 1,
|
|
143
|
+
locked_by = NULL,
|
|
144
|
+
lease_expires_at = NULL,
|
|
145
|
+
started_at = NULL,
|
|
146
|
+
last_error = ?
|
|
147
|
+
WHERE id = ?
|
|
148
|
+
AND locked_by = UUID_TO_BIN(?)
|
|
149
|
+
AND state = 'active'
|
|
150
|
+
AND retry_count < retry_limit;
|
|
151
|
+
`;
|
|
152
|
+
// §3.2 — Dead-letter move
|
|
153
|
+
export const DLQ_INSERT = `
|
|
154
|
+
INSERT INTO jobs_dead
|
|
155
|
+
(id, queue, priority, payload, singleton_key,
|
|
156
|
+
retry_count, retry_limit, retry_delay_secs, retry_backoff,
|
|
157
|
+
created_at, last_error)
|
|
158
|
+
SELECT id, queue, priority, payload, singleton_key,
|
|
159
|
+
retry_count + 1, retry_limit, retry_delay_secs, retry_backoff,
|
|
160
|
+
created_at, ?
|
|
161
|
+
FROM jobs
|
|
162
|
+
WHERE id = ? AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
163
|
+
`;
|
|
164
|
+
export const DLQ_DELETE = `
|
|
165
|
+
DELETE FROM jobs
|
|
166
|
+
WHERE id = ? AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
167
|
+
`;
|
|
168
|
+
// §3.2 — DLQ query
|
|
169
|
+
export const LIST_DEAD = `
|
|
170
|
+
SELECT CAST(id AS CHAR) AS id, queue, priority, payload, retry_count,
|
|
171
|
+
UNIX_TIMESTAMP(created_at) AS created_at_unix,
|
|
172
|
+
UNIX_TIMESTAMP(failed_at) AS failed_at_unix,
|
|
173
|
+
last_error
|
|
174
|
+
FROM jobs_dead
|
|
175
|
+
WHERE queue = ? AND failed_at BETWEEN ? AND ?
|
|
176
|
+
ORDER BY failed_at DESC
|
|
177
|
+
LIMIT ? OFFSET ?;
|
|
178
|
+
`;
|
|
179
|
+
// §3.2 — Replay
|
|
180
|
+
export const REPLAY_INSERT = `
|
|
181
|
+
INSERT INTO jobs
|
|
182
|
+
(queue, priority, payload, singleton_key,
|
|
183
|
+
retry_limit, retry_delay_secs, retry_backoff, run_at)
|
|
184
|
+
SELECT queue, priority, payload, singleton_key,
|
|
185
|
+
retry_limit, retry_delay_secs, retry_backoff, UTC_TIMESTAMP(6)
|
|
186
|
+
FROM jobs_dead
|
|
187
|
+
WHERE id IN (?)
|
|
188
|
+
FOR UPDATE;
|
|
189
|
+
`;
|
|
190
|
+
export const REPLAY_DELETE = `
|
|
191
|
+
DELETE FROM jobs_dead WHERE id IN (?);
|
|
192
|
+
`;
|
|
193
|
+
// §4.2 — Cron tick
|
|
194
|
+
export const DB_NOW = "SELECT UNIX_TIMESTAMP(UTC_TIMESTAMP(6)) AS db_now_unix;";
|
|
195
|
+
export const TICK_SELECT = `
|
|
196
|
+
SELECT name, queue, cron, timezone, payload, job_options,
|
|
197
|
+
next_run_at,
|
|
198
|
+
UNIX_TIMESTAMP(next_run_at) AS next_run_at_unix
|
|
199
|
+
FROM schedules
|
|
200
|
+
WHERE next_run_at <= UTC_TIMESTAMP(6)
|
|
201
|
+
ORDER BY next_run_at
|
|
202
|
+
LIMIT 20
|
|
203
|
+
FOR UPDATE SKIP LOCKED;
|
|
204
|
+
`;
|
|
205
|
+
export const TICK_ENQUEUE = `
|
|
206
|
+
INSERT IGNORE INTO jobs
|
|
207
|
+
(queue, priority, payload, singleton_key,
|
|
208
|
+
retry_limit, retry_delay_secs, retry_backoff, run_at)
|
|
209
|
+
VALUES
|
|
210
|
+
(?, ?, ?, CONCAT('cron:', ?, ':', DATE_FORMAT(?, '%Y%m%d%H%i%s')),
|
|
211
|
+
?, ?, ?, UTC_TIMESTAMP(6));
|
|
212
|
+
`;
|
|
213
|
+
export const TICK_ADVANCE = `
|
|
214
|
+
UPDATE schedules
|
|
215
|
+
SET next_run_at = ?, last_enqueued_at = UTC_TIMESTAMP(6)
|
|
216
|
+
WHERE name = ?;
|
|
217
|
+
`;
|
|
218
|
+
// §4 — Schedule CRUD
|
|
219
|
+
export const SCHEDULE_UPSERT = `
|
|
220
|
+
INSERT INTO schedules (name, queue, cron, timezone, payload, job_options, next_run_at)
|
|
221
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
222
|
+
ON DUPLICATE KEY UPDATE
|
|
223
|
+
queue = VALUES(queue),
|
|
224
|
+
cron = VALUES(cron),
|
|
225
|
+
timezone = VALUES(timezone),
|
|
226
|
+
payload = VALUES(payload),
|
|
227
|
+
job_options = VALUES(job_options),
|
|
228
|
+
next_run_at = VALUES(next_run_at);
|
|
229
|
+
`;
|
|
230
|
+
export const SCHEDULE_DELETE = `
|
|
231
|
+
DELETE FROM schedules WHERE name = ?;
|
|
232
|
+
`;
|
|
233
|
+
// §6.1 — Heartbeat
|
|
234
|
+
export const HEARTBEAT = `
|
|
235
|
+
UPDATE jobs
|
|
236
|
+
SET lease_expires_at = UTC_TIMESTAMP(6) + INTERVAL ? SECOND
|
|
237
|
+
WHERE id IN (?) AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
238
|
+
`;
|
|
239
|
+
export const HEARTBEAT_OWNED = `
|
|
240
|
+
SELECT CAST(id AS CHAR) AS id
|
|
241
|
+
FROM jobs
|
|
242
|
+
WHERE id IN (?) AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
243
|
+
`;
|
|
244
|
+
// §6.2 — Drain release
|
|
245
|
+
export const DRAIN_RELEASE = `
|
|
246
|
+
UPDATE jobs
|
|
247
|
+
SET state = 'available', run_at = UTC_TIMESTAMP(6),
|
|
248
|
+
locked_by = NULL, lease_expires_at = NULL, started_at = NULL
|
|
249
|
+
WHERE id IN (?) AND locked_by = UUID_TO_BIN(?) AND state = 'active';
|
|
250
|
+
`;
|
|
251
|
+
// §6.3 — Stale sweep
|
|
252
|
+
export const SWEEP_SELECT = `
|
|
253
|
+
SELECT CAST(id AS CHAR) AS id, retry_count, retry_limit
|
|
254
|
+
FROM jobs
|
|
255
|
+
WHERE state = 'active' AND lease_expires_at < UTC_TIMESTAMP(6)
|
|
256
|
+
ORDER BY lease_expires_at
|
|
257
|
+
LIMIT 100
|
|
258
|
+
FOR UPDATE SKIP LOCKED;
|
|
259
|
+
`;
|
|
260
|
+
export const SWEEP_RETRY = `
|
|
261
|
+
UPDATE jobs
|
|
262
|
+
SET state = 'available',
|
|
263
|
+
run_at = UTC_TIMESTAMP(6) + INTERVAL
|
|
264
|
+
LEAST(
|
|
265
|
+
86400,
|
|
266
|
+
IF(retry_backoff = 1,
|
|
267
|
+
retry_delay_secs * POW(2, retry_count),
|
|
268
|
+
retry_delay_secs)
|
|
269
|
+
+ FLOOR(RAND() * retry_delay_secs)
|
|
270
|
+
) SECOND,
|
|
271
|
+
retry_count = retry_count + 1,
|
|
272
|
+
locked_by = NULL, lease_expires_at = NULL, started_at = NULL,
|
|
273
|
+
last_error = JSON_OBJECT('message', 'lease expired', 'at', UTC_TIMESTAMP(6))
|
|
274
|
+
WHERE id IN (?) AND retry_count < retry_limit;
|
|
275
|
+
`;
|
|
276
|
+
export const SWEEP_DLQ_INSERT = `
|
|
277
|
+
INSERT INTO jobs_dead
|
|
278
|
+
(id, queue, priority, payload, singleton_key,
|
|
279
|
+
retry_count, retry_limit, retry_delay_secs, retry_backoff,
|
|
280
|
+
created_at, last_error)
|
|
281
|
+
SELECT id, queue, priority, payload, singleton_key,
|
|
282
|
+
retry_count + 1, retry_limit, retry_delay_secs, retry_backoff,
|
|
283
|
+
created_at, JSON_OBJECT('message', 'lease expired', 'at', UTC_TIMESTAMP(6))
|
|
284
|
+
FROM jobs
|
|
285
|
+
WHERE id IN (?);
|
|
286
|
+
`;
|
|
287
|
+
export const SWEEP_DLQ_DELETE = `
|
|
288
|
+
DELETE FROM jobs WHERE id IN (?);
|
|
289
|
+
`;
|
|
290
|
+
// §6.3 — Archive retention
|
|
291
|
+
export const ARCHIVE_PRUNE = `
|
|
292
|
+
DELETE FROM jobs_archive
|
|
293
|
+
WHERE completed_at < UTC_TIMESTAMP(6) - INTERVAL ? DAY
|
|
294
|
+
ORDER BY completed_at
|
|
295
|
+
LIMIT 5000;
|
|
296
|
+
`;
|
|
297
|
+
// Archive query
|
|
298
|
+
export const GET_ARCHIVED_JOB = `
|
|
299
|
+
SELECT CAST(id AS CHAR) AS id, queue, priority, payload, singleton_key, retry_count,
|
|
300
|
+
UNIX_TIMESTAMP(created_at) AS created_at_unix,
|
|
301
|
+
UNIX_TIMESTAMP(started_at) AS started_at_unix,
|
|
302
|
+
UNIX_TIMESTAMP(completed_at) AS completed_at_unix,
|
|
303
|
+
duration_ms
|
|
304
|
+
FROM jobs_archive
|
|
305
|
+
WHERE id = ?;
|
|
306
|
+
`;
|
|
307
|
+
export const LIST_ARCHIVE = `
|
|
308
|
+
SELECT CAST(id AS CHAR) AS id, queue, priority, payload, singleton_key, retry_count,
|
|
309
|
+
UNIX_TIMESTAMP(created_at) AS created_at_unix,
|
|
310
|
+
UNIX_TIMESTAMP(started_at) AS started_at_unix,
|
|
311
|
+
UNIX_TIMESTAMP(completed_at) AS completed_at_unix,
|
|
312
|
+
duration_ms
|
|
313
|
+
FROM jobs_archive
|
|
314
|
+
WHERE queue = ? AND completed_at < ?
|
|
315
|
+
ORDER BY completed_at DESC
|
|
316
|
+
LIMIT ?;
|
|
317
|
+
`;
|
|
318
|
+
// Connection setup
|
|
319
|
+
export const INIT_SESSION = `
|
|
320
|
+
SET SESSION transaction_isolation = 'READ-COMMITTED',
|
|
321
|
+
SESSION time_zone = '+00:00';
|
|
322
|
+
`;
|
|
323
|
+
export const DEFAULT_TABLE_PREFIX = "mysql_boss";
|
|
324
|
+
export const UNPREFIXED_SQL = {
|
|
325
|
+
CREATE_JOBS,
|
|
326
|
+
CREATE_JOBS_ARCHIVE,
|
|
327
|
+
CREATE_JOBS_DEAD,
|
|
328
|
+
CREATE_SCHEDULES,
|
|
329
|
+
ENQUEUE,
|
|
330
|
+
ENQUEUE_SINGLETON,
|
|
331
|
+
LAST_INSERT_ID,
|
|
332
|
+
CLAIM_SELECT,
|
|
333
|
+
CLAIM_UPDATE,
|
|
334
|
+
COMPLETE_ARCHIVE,
|
|
335
|
+
COMPLETE_DELETE,
|
|
336
|
+
FAIL_RETRY,
|
|
337
|
+
DLQ_INSERT,
|
|
338
|
+
DLQ_DELETE,
|
|
339
|
+
LIST_DEAD,
|
|
340
|
+
REPLAY_INSERT,
|
|
341
|
+
REPLAY_DELETE,
|
|
342
|
+
DB_NOW,
|
|
343
|
+
TICK_SELECT,
|
|
344
|
+
TICK_ENQUEUE,
|
|
345
|
+
TICK_ADVANCE,
|
|
346
|
+
SCHEDULE_UPSERT,
|
|
347
|
+
SCHEDULE_DELETE,
|
|
348
|
+
HEARTBEAT,
|
|
349
|
+
HEARTBEAT_OWNED,
|
|
350
|
+
DRAIN_RELEASE,
|
|
351
|
+
SWEEP_SELECT,
|
|
352
|
+
SWEEP_RETRY,
|
|
353
|
+
SWEEP_DLQ_INSERT,
|
|
354
|
+
SWEEP_DLQ_DELETE,
|
|
355
|
+
ARCHIVE_PRUNE,
|
|
356
|
+
GET_ARCHIVED_JOB,
|
|
357
|
+
LIST_ARCHIVE,
|
|
358
|
+
};
|
|
359
|
+
const TABLE_SUFFIXES = [
|
|
360
|
+
"jobs_archive",
|
|
361
|
+
"jobs_dead",
|
|
362
|
+
"schedules",
|
|
363
|
+
"jobs",
|
|
364
|
+
];
|
|
365
|
+
/**
|
|
366
|
+
* Builds the SQL used by one MysqlBoss instance.
|
|
367
|
+
*
|
|
368
|
+
* Prefixes are deliberately limited to portable identifier characters. This
|
|
369
|
+
* lets us quote every generated table name safely instead of interpolating an
|
|
370
|
+
* arbitrary SQL identifier.
|
|
371
|
+
*/
|
|
372
|
+
export function createSql(tablePrefix = DEFAULT_TABLE_PREFIX) {
|
|
373
|
+
if (!/^[A-Za-z0-9_]*$/.test(tablePrefix)) {
|
|
374
|
+
throw new Error("tablePrefix may contain only ASCII letters, numbers, and underscores");
|
|
375
|
+
}
|
|
376
|
+
if (tablePrefix.length > 50) {
|
|
377
|
+
throw new Error("tablePrefix must be 50 characters or fewer");
|
|
378
|
+
}
|
|
379
|
+
const tableName = (suffix) => `\`${tablePrefix ? `${tablePrefix}_${suffix}` : suffix}\``;
|
|
380
|
+
const prefixTables = (statement) => {
|
|
381
|
+
let result = statement;
|
|
382
|
+
for (const suffix of TABLE_SUFFIXES) {
|
|
383
|
+
result = result.replace(new RegExp(`\\b${suffix}\\b`, "g"), tableName(suffix));
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
};
|
|
387
|
+
return Object.fromEntries(Object.entries(UNPREFIXED_SQL).map(([name, statement]) => [
|
|
388
|
+
name,
|
|
389
|
+
prefixTables(statement),
|
|
390
|
+
]));
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,aAAa;AAEb,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuB1B,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;CAgBlC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;CAiB/B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;CAe/B,CAAC;AAEF,iBAAiB;AAEjB,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;CAMtB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;CAMhC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG;;CAE7B,CAAC;AAEF,yBAAyB;AAEzB,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;CAU3B,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;CAO3B,CAAC;AAEF,iCAAiC;AAEjC,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;CAS/B,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;;CAG9B,CAAC;AAEF,qCAAqC;AAErC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;CAoBzB,CAAC;AAEF,0BAA0B;AAE1B,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;CAUzB,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG;;;CAGzB,CAAC;AAEF,mBAAmB;AAEnB,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;CASxB,CAAC;AAEF,gBAAgB;AAEhB,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;CAS5B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;CAE5B,CAAC;AAEF,mBAAmB;AAEnB,MAAM,CAAC,MAAM,MAAM,GAAG,yDAAyD,CAAC;AAEhF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;CAS1B,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;CAO3B,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;CAI3B,CAAC;AAEF,qBAAqB;AAErB,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;CAU9B,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;CAE9B,CAAC;AAEF,mBAAmB;AAEnB,MAAM,CAAC,MAAM,SAAS,GAAG;;;;CAIxB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;;;;CAI9B,CAAC;AAEF,uBAAuB;AAEvB,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;CAK5B,CAAC;AAEF,qBAAqB;AAErB,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;CAO3B,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;CAe1B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;CAU/B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG;;CAE/B,CAAC;AAEF,2BAA2B;AAE3B,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;CAK5B,CAAC;AAEF,gBAAgB;AAEhB,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;CAQ/B,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;CAU3B,CAAC;AAEF,mBAAmB;AAEnB,MAAM,CAAC,MAAM,YAAY,GAAG;;;CAG3B,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,WAAW;IACX,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,OAAO;IACP,iBAAiB;IACjB,cAAc;IACd,YAAY;IACZ,YAAY;IACZ,gBAAgB;IAChB,eAAe;IACf,UAAU;IACV,UAAU;IACV,UAAU;IACV,SAAS;IACT,aAAa;IACb,aAAa;IACb,MAAM;IACN,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,eAAe;IACf,SAAS;IACT,eAAe;IACf,aAAa;IACb,YAAY;IACZ,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,gBAAgB;IAChB,YAAY;CACH,CAAC;AAMX,MAAM,cAAc,GAAG;IACtB,cAAc;IACd,WAAW;IACX,WAAW;IACX,MAAM;CACG,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,WAAW,GAAG,oBAAoB;IAC3D,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACd,sEAAsE,CACtE,CAAC;IACH,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,MAAuC,EAAU,EAAE,CACrE,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC;IAE5D,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAU,EAAE;QAClD,IAAI,MAAM,GAAG,SAAS,CAAC;QACvB,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;YACrC,MAAM,GAAG,MAAM,CAAC,OAAO,CACtB,IAAI,MAAM,CAAC,MAAM,MAAM,KAAK,EAAE,GAAG,CAAC,EAClC,SAAS,CAAC,MAAM,CAAC,CACjB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC,WAAW,CACxB,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC;QACzD,IAAI;QACJ,YAAY,CAAC,SAAS,CAAC;KACvB,CAAC,CACe,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep.d.ts","sourceRoot":"","sources":["../../src/sweep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAkC,MAAM,gBAAgB,CAAC;AAG3E,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,UAAU,CAAC;AAQ9D,wBAAsB,cAAc,CACnC,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,aAA8B,GACjC,OAAO,CAAC,MAAM,CAAC,CA0CjB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { acquireConnection } from "./connection.js";
|
|
2
|
+
import { withDeadlockRetry } from "./retry-util.js";
|
|
3
|
+
import { UNPREFIXED_SQL } from "./sql.js";
|
|
4
|
+
export async function sweepStaleJobs(pool, sql = UNPREFIXED_SQL) {
|
|
5
|
+
return withDeadlockRetry(async () => {
|
|
6
|
+
const conn = await acquireConnection(pool);
|
|
7
|
+
try {
|
|
8
|
+
await conn.beginTransaction();
|
|
9
|
+
const [rows] = await conn.query(sql.SWEEP_SELECT);
|
|
10
|
+
if (rows.length === 0) {
|
|
11
|
+
await conn.commit();
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
14
|
+
const retryable = [];
|
|
15
|
+
const exhausted = [];
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
if (row.retry_count < row.retry_limit) {
|
|
18
|
+
retryable.push(row.id);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
exhausted.push(row.id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (retryable.length > 0) {
|
|
25
|
+
await conn.query(sql.SWEEP_RETRY, [retryable]);
|
|
26
|
+
}
|
|
27
|
+
if (exhausted.length > 0) {
|
|
28
|
+
await conn.query(sql.SWEEP_DLQ_INSERT, [exhausted]);
|
|
29
|
+
await conn.query(sql.SWEEP_DLQ_DELETE, [exhausted]);
|
|
30
|
+
}
|
|
31
|
+
await conn.commit();
|
|
32
|
+
return rows.length;
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
await conn.rollback().catch(() => { });
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
conn.release();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=sweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep.js","sourceRoot":"","sources":["../../src/sweep.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAsB,cAAc,EAAE,MAAM,UAAU,CAAC;AAQ9D,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,IAAU,EACV,GAAG,GAAkB,cAAc;IAEnC,OAAO,iBAAiB,CAAC,KAAK,IAAI,EAAE;QACnC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAE9B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAa,GAAG,CAAC,YAAY,CAAC,CAAC;YAE9D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;gBACpB,OAAO,CAAC,CAAC;YACV,CAAC;YAED,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;YAE/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;oBACvC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACP,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACxB,CAAC;YACF,CAAC;YAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YAChD,CAAC;YAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBACpD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACrD,CAAC;YAED,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,MAAM,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtC,MAAM,GAAG,CAAC;QACX,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,OAAO,EAAE,CAAC;QAChB,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Pool } from "mysql2/promise";
|
|
2
|
+
import { type SqlStatements } from "./sql.js";
|
|
3
|
+
export declare function runTick(pool: Pool, sql?: SqlStatements): Promise<number>;
|
|
4
|
+
export declare function upsertSchedule(pool: Pool, name: string, queue: string, cron: string, timezone: string, payload: unknown, jobOptions: {
|
|
5
|
+
priority?: number;
|
|
6
|
+
retryLimit?: number;
|
|
7
|
+
retryDelaySecs?: number;
|
|
8
|
+
retryBackoff?: boolean;
|
|
9
|
+
} | null, sql?: SqlStatements): Promise<void>;
|
|
10
|
+
export declare function deleteSchedule(pool: Pool, name: string, sql?: SqlStatements): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=tick.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tick.d.ts","sourceRoot":"","sources":["../../src/tick.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAiB,MAAM,gBAAgB,CAAC;AAM1D,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,UAAU,CAAC;AAsB9D,wBAAsB,OAAO,CAC5B,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,aAA8B,GACjC,OAAO,CAAC,MAAM,CAAC,CAqDjB;AAED,wBAAsB,cAAc,CACnC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,IAAI,EACR,GAAG,GAAE,aAA8B,GACjC,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,wBAAsB,cAAc,CACnC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,aAA8B,GACjC,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
package/dist/esm/tick.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { acquireConnection, withConnection } from "./connection.js";
|
|
2
|
+
import { nextOccurrence } from "./cron/next.js";
|
|
3
|
+
import { parseCron } from "./cron/parse.js";
|
|
4
|
+
import { withDeadlockRetry } from "./retry-util.js";
|
|
5
|
+
import { UNPREFIXED_SQL } from "./sql.js";
|
|
6
|
+
import { toUtcString } from "./util.js";
|
|
7
|
+
export async function runTick(pool, sql = UNPREFIXED_SQL) {
|
|
8
|
+
return withDeadlockRetry(async () => {
|
|
9
|
+
const conn = await acquireConnection(pool);
|
|
10
|
+
try {
|
|
11
|
+
await conn.beginTransaction();
|
|
12
|
+
const [nowRows] = await conn.query(sql.DB_NOW);
|
|
13
|
+
const dbNow = new Date(Number(nowRows[0].db_now_unix) * 1000);
|
|
14
|
+
const [schedules] = await conn.query(sql.TICK_SELECT);
|
|
15
|
+
if (schedules.length === 0) {
|
|
16
|
+
await conn.commit();
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
let fired = 0;
|
|
20
|
+
for (const sched of schedules) {
|
|
21
|
+
const opts = sched.job_options ?? {};
|
|
22
|
+
const priority = opts.priority ?? 0;
|
|
23
|
+
const retryLimit = opts.retryLimit ?? 2;
|
|
24
|
+
const retryDelaySecs = opts.retryDelaySecs ?? 30;
|
|
25
|
+
const retryBackoff = opts.retryBackoff ? 1 : 0;
|
|
26
|
+
await conn.query(sql.TICK_ENQUEUE, [
|
|
27
|
+
sched.queue,
|
|
28
|
+
priority,
|
|
29
|
+
sched.payload != null ? JSON.stringify(sched.payload) : null,
|
|
30
|
+
sched.name,
|
|
31
|
+
toUtcString(new Date(Number(sched.next_run_at_unix) * 1000)),
|
|
32
|
+
retryLimit,
|
|
33
|
+
retryDelaySecs,
|
|
34
|
+
retryBackoff,
|
|
35
|
+
]);
|
|
36
|
+
const fields = parseCron(sched.cron);
|
|
37
|
+
const nextRunAt = new Date(Number(sched.next_run_at_unix) * 1000);
|
|
38
|
+
const base = dbNow > nextRunAt ? dbNow : nextRunAt;
|
|
39
|
+
const nextRun = nextOccurrence(fields, base, sched.timezone);
|
|
40
|
+
await conn.query(sql.TICK_ADVANCE, [toUtcString(nextRun), sched.name]);
|
|
41
|
+
fired++;
|
|
42
|
+
}
|
|
43
|
+
await conn.commit();
|
|
44
|
+
return fired;
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
await conn.rollback().catch(() => { });
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
conn.release();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export async function upsertSchedule(pool, name, queue, cron, timezone, payload, jobOptions, sql = UNPREFIXED_SQL) {
|
|
56
|
+
const fields = parseCron(cron);
|
|
57
|
+
await withConnection(pool, async (connection) => {
|
|
58
|
+
const [nowRows] = await connection.query(sql.DB_NOW);
|
|
59
|
+
const dbNow = new Date(Number(nowRows[0].db_now_unix) * 1000);
|
|
60
|
+
const nextRun = nextOccurrence(fields, dbNow, timezone);
|
|
61
|
+
await connection.query(sql.SCHEDULE_UPSERT, [
|
|
62
|
+
name,
|
|
63
|
+
queue,
|
|
64
|
+
cron,
|
|
65
|
+
timezone,
|
|
66
|
+
payload != null ? JSON.stringify(payload) : null,
|
|
67
|
+
jobOptions ? JSON.stringify(jobOptions) : null,
|
|
68
|
+
toUtcString(nextRun),
|
|
69
|
+
]);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export async function deleteSchedule(pool, name, sql = UNPREFIXED_SQL) {
|
|
73
|
+
await withConnection(pool, async (connection) => {
|
|
74
|
+
await connection.query(sql.SCHEDULE_DELETE, [name]);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=tick.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tick.js","sourceRoot":"","sources":["../../src/tick.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE9C"}
|
package/dist/esm/util.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,IAAU;IACrC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Pool } from "mysql2/promise";
|
|
2
|
+
import type { JobHandler } from "./index.js";
|
|
3
|
+
import type { SqlStatements } from "./sql.js";
|
|
4
|
+
interface WorkerConfig {
|
|
5
|
+
pool: Pool;
|
|
6
|
+
workerId: string;
|
|
7
|
+
pollIntervalMs: number;
|
|
8
|
+
batchSize: number;
|
|
9
|
+
concurrency: number;
|
|
10
|
+
leaseSeconds: number;
|
|
11
|
+
heartbeatSeconds: number;
|
|
12
|
+
sweepIntervalMs: number;
|
|
13
|
+
drainTimeoutMs: number;
|
|
14
|
+
onError: (err: unknown, context: string) => void;
|
|
15
|
+
sql: SqlStatements;
|
|
16
|
+
}
|
|
17
|
+
export declare class WorkerManager {
|
|
18
|
+
private config;
|
|
19
|
+
private registrations;
|
|
20
|
+
private inFlight;
|
|
21
|
+
private pollTimers;
|
|
22
|
+
private heartbeatTimer;
|
|
23
|
+
private sweepTimer;
|
|
24
|
+
private stopped;
|
|
25
|
+
constructor(config: WorkerConfig);
|
|
26
|
+
registerQueue(queue: string, handler: JobHandler): void;
|
|
27
|
+
private ensureTimers;
|
|
28
|
+
private startPolling;
|
|
29
|
+
private poll;
|
|
30
|
+
private schedulePoll;
|
|
31
|
+
private dispatch;
|
|
32
|
+
private executeJob;
|
|
33
|
+
private handleHandlerFailure;
|
|
34
|
+
private reportError;
|
|
35
|
+
private heartbeat;
|
|
36
|
+
private sweep;
|
|
37
|
+
stop(drainTimeoutMs?: number): Promise<void>;
|
|
38
|
+
get inFlightCount(): number;
|
|
39
|
+
get isStopped(): boolean;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAO3C,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,UAAU,YAAY;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,GAAG,EAAE,aAAa,CAAC;CACnB;AASD,qBAAa,aAAa;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,UAAU,CAAoD;IACtE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,MAAM,EAAE,YAAY,EAE/B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAOtD;IAED,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,YAAY;YAKN,IAAI;IA2ClB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,QAAQ;YAkBF,UAAU;YA2BV,oBAAoB;IA6BlC,OAAO,CAAC,WAAW;YAQL,SAAS;YAwBT,KAAK;IAQb,IAAI,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmEjD;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;CACD"}
|