lonnymq 1.1.0 → 1.2.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/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { randomUUID } from "node:crypto";
1
+ const INTEGER_MAX = 2147483647;
2
+ //#endregion
2
3
  //#region src/util/sql.ts
3
4
  const value = (value) => ({
4
5
  nodeType: "VALUE",
@@ -54,54 +55,57 @@ const dedent = (value) => {
54
55
  return lines.map((line) => line.slice(minIndent)).join("\n").trim();
55
56
  };
56
57
  //#endregion
57
- //#region src/command/channel-policy/clear.ts
58
- var Clear = class {
58
+ //#region src/command/channel/release.ts
59
+ var Release = class {
59
60
  schema;
60
- channelId;
61
+ channel;
61
62
  createdAt;
62
63
  constructor(params) {
63
64
  this.schema = params.schema;
64
- this.channelId = params.channelId;
65
+ this.channel = params.channel;
65
66
  this.createdAt = /* @__PURE__ */ new Date();
66
67
  }
67
68
  async execute(databaseClient) {
68
- await databaseClient.query(fragment`
69
- SELECT 1 FROM ${ref(this.schema)}."channel_policy_clear"(
69
+ const result = await databaseClient.query(fragment`
70
+ SELECT * FROM ${ref(this.schema)}."channel_release"(
70
71
  $1
71
72
  )
72
- `.value, [this.channelId]);
73
+ `.value, [this.channel]).then((res) => res.rows[0]);
74
+ if (result.result_code === 9) return { resultType: "CHANNEL_NOT_FOUND" };
75
+ else if (result.result_code === 10) return { resultType: "CHANNEL_RELEASED" };
76
+ else throw new Error("Unexpected result");
73
77
  }
74
78
  };
75
79
  //#endregion
76
- //#region src/command/channel-policy/set.ts
80
+ //#region src/command/channel/set.ts
77
81
  var Set = class {
78
82
  schema;
79
- channelId;
83
+ channel;
80
84
  maxConcurrency;
81
85
  maxSize;
82
86
  releaseIntervalMs;
83
87
  createdAt;
84
88
  constructor(params) {
85
89
  this.schema = params.schema;
86
- this.channelId = params.channelId;
87
- const maxConcurrency = params.maxConcurrency ?? null;
88
- this.maxConcurrency = maxConcurrency !== null ? Math.max(1, maxConcurrency) : null;
89
- const maxSize = params.maxSize ?? null;
90
- this.maxSize = maxSize !== null ? Math.max(1, maxSize) : null;
91
- const releaseIntervalMs = params.releaseIntervalMs ?? null;
92
- this.releaseIntervalMs = releaseIntervalMs !== null ? Math.max(0, releaseIntervalMs) : null;
90
+ this.channel = params.channel;
91
+ if (!Number.isInteger(params.maxConcurrency) || params.maxConcurrency < 1 || params.maxConcurrency > 2147483647) throw new Error(`maxConcurrency must be an integer between 1 and ${INTEGER_MAX}`);
92
+ if (!Number.isInteger(params.maxSize) || params.maxSize < 1 || params.maxSize > 2147483647) throw new Error(`maxSize must be an integer between 1 and ${INTEGER_MAX}`);
93
+ if (!Number.isInteger(params.releaseIntervalMs) || params.releaseIntervalMs < 0 || params.releaseIntervalMs > 2147483647) throw new Error(`releaseIntervalMs must be an integer between 0 and ${INTEGER_MAX}`);
94
+ this.maxConcurrency = params.maxConcurrency;
95
+ this.maxSize = params.maxSize;
96
+ this.releaseIntervalMs = params.releaseIntervalMs;
93
97
  this.createdAt = /* @__PURE__ */ new Date();
94
98
  }
95
99
  async execute(databaseClient) {
96
100
  await databaseClient.query(fragment`
97
- SELECT 1 FROM ${ref(this.schema)}."channel_policy_set"(
101
+ SELECT 1 FROM ${ref(this.schema)}."channel_set"(
98
102
  $1,
99
103
  $2::INTEGER,
100
104
  $3::INTEGER,
101
105
  $4::INTEGER
102
106
  )
103
107
  `.value, [
104
- this.channelId,
108
+ this.channel,
105
109
  this.maxConcurrency,
106
110
  this.maxSize,
107
111
  this.releaseIntervalMs
@@ -112,12 +116,12 @@ var Set = class {
112
116
  //#region src/command/message/create.ts
113
117
  var Create = class {
114
118
  schema;
115
- channelId;
119
+ channel;
116
120
  content;
117
121
  dequeueAt;
118
122
  constructor(params) {
119
123
  this.schema = params.schema;
120
- this.channelId = params.channelId;
124
+ this.channel = params.channel;
121
125
  this.content = params.content;
122
126
  this.dequeueAt = params.dequeueAt;
123
127
  }
@@ -132,16 +136,16 @@ var Create = class {
132
136
  $3::BIGINT
133
137
  )
134
138
  `.value, [
135
- this.channelId,
139
+ this.channel,
136
140
  this.content,
137
141
  this.dequeueAt
138
142
  ]).then((res) => res.rows[0]);
139
143
  if (result.result_code === 0) return {
140
144
  resultType: "MESSAGE_CREATED",
141
- id: BigInt(result.metadata.id),
142
- channelSize: result.metadata.channel_size
145
+ id: BigInt(result.metadata.id)
143
146
  };
144
147
  else if (result.result_code === 1) return { resultType: "MESSAGE_DROPPED" };
148
+ else if (result.result_code === 9) return { resultType: "CHANNEL_NOT_FOUND" };
145
149
  else throw new Error("Unexpected result code");
146
150
  }
147
151
  };
@@ -167,7 +171,6 @@ var Dequeue = class {
167
171
  else if (result.result_code === 3) return {
168
172
  resultType: "MESSAGE_DEQUEUED",
169
173
  id: BigInt(result.metadata.id),
170
- channelId: result.metadata.channel_id,
171
174
  isUnlocked: result.metadata.is_unlocked,
172
175
  content: result.content,
173
176
  state: result.state,
@@ -177,8 +180,8 @@ var Dequeue = class {
177
180
  }
178
181
  };
179
182
  //#endregion
180
- //#region src/command/message/retire.ts
181
- var Retire = class {
183
+ //#region src/command/message/complete.ts
184
+ var Complete = class {
182
185
  schema;
183
186
  id;
184
187
  numAttempts;
@@ -189,14 +192,14 @@ var Retire = class {
189
192
  }
190
193
  async execute(databaseClient) {
191
194
  const result = await databaseClient.query(fragment`
192
- SELECT * FROM ${ref(this.schema)}."message_retire"(
195
+ SELECT * FROM ${ref(this.schema)}."message_complete"(
193
196
  $1::BIGINT,
194
197
  $2::BIGINT
195
198
  )
196
199
  `.value, [this.id.toString(), this.numAttempts]).then((res) => res.rows[0]);
197
200
  if (result.result_code === 4) return { resultType: "MESSAGE_NOT_FOUND" };
198
201
  else if (result.result_code === 5) return { resultType: "STATE_INVALID" };
199
- else if (result.result_code === 6) return { resultType: "MESSAGE_RETIRED" };
202
+ else if (result.result_code === 6) return { resultType: "MESSAGE_COMPLETED" };
200
203
  else throw new Error("Unexpected result");
201
204
  }
202
205
  };
@@ -284,70 +287,89 @@ const epoch = (params) => {
284
287
  `];
285
288
  };
286
289
  //#endregion
287
- //#region src/install/function/channel-policy/clear.ts
288
- const clear = (params) => {
290
+ //#region src/install/function/channel/release.ts
291
+ const release = (params) => {
289
292
  return [fragment`
290
- CREATE FUNCTION ${ref(params.schema)}."channel_policy_clear" (
291
- p_id TEXT
292
- ) RETURNS VOID AS $$
293
+ CREATE FUNCTION ${ref(params.schema)}."channel_release" (
294
+ p_text TEXT
295
+ ) RETURNS TABLE (
296
+ result_code INTEGER
297
+ ) AS $$
293
298
  DECLARE
294
- v_channel_state RECORD;
299
+ v_channel RECORD;
295
300
  BEGIN
296
- DELETE FROM ${ref(params.schema)}."channel_policy"
297
- WHERE "id" = p_id;
298
-
299
301
  SELECT
300
- "channel_state"."id",
301
- "channel_state"."current_size"
302
- FROM ${ref(params.schema)}."channel_state"
303
- WHERE "id" = p_id
302
+ "channel"."id",
303
+ "channel"."current_size"
304
+ FROM ${ref(params.schema)}."channel"
305
+ WHERE "name" = p_text
306
+ AND NOT "released"
304
307
  FOR UPDATE
305
- INTO v_channel_state;
308
+ INTO v_channel;
306
309
 
307
- IF v_channel_state."current_size" = 0 THEN
308
- DELETE FROM ${ref(params.schema)}."channel_state"
309
- WHERE "id" = v_channel_state."id";
310
+ IF v_channel."id" IS NULL THEN
311
+ RETURN QUERY SELECT
312
+ ${value(9)};
313
+ RETURN;
314
+ END IF;
315
+
316
+ IF v_channel."current_size" = 0 THEN
317
+ DELETE FROM ${ref(params.schema)}."channel"
318
+ WHERE "id" = v_channel."id";
310
319
  ELSE
311
- UPDATE ${ref(params.schema)}."channel_state" SET
312
- "max_concurrency" = NULL,
313
- "release_interval_ms" = NULL
314
- WHERE "id" = p_id;
320
+ UPDATE ${ref(params.schema)}."channel" SET
321
+ "released" = TRUE
322
+ WHERE "id" = v_channel."id";
315
323
  END IF;
324
+
325
+ RETURN QUERY SELECT
326
+ ${value(10)};
327
+ RETURN;
316
328
  END;
317
329
  $$ LANGUAGE plpgsql;
318
330
  `];
319
331
  };
320
332
  //#endregion
321
- //#region src/install/function/channel-policy/set.ts
333
+ //#region src/install/function/channel/set.ts
322
334
  const set = (params) => {
323
335
  return [fragment`
324
- CREATE FUNCTION ${ref(params.schema)}."channel_policy_set" (
325
- p_id TEXT,
336
+ CREATE FUNCTION ${ref(params.schema)}."channel_set" (
337
+ p_text TEXT,
326
338
  p_max_concurrency INTEGER,
327
339
  p_max_size INTEGER,
328
340
  p_release_interval_ms INTEGER
329
341
  ) RETURNS VOID AS $$
330
342
  BEGIN
331
- INSERT INTO ${ref(params.schema)}."channel_policy" (
332
- "id",
343
+ INSERT INTO ${ref(params.schema)}."channel" (
344
+ "name",
333
345
  "max_concurrency",
334
346
  "max_size",
335
- "release_interval_ms"
347
+ "current_concurrency",
348
+ "current_size",
349
+ "release_interval_ms",
350
+ "released",
351
+ "dequeue_prev_at"
336
352
  ) VALUES (
337
- p_id,
353
+ p_text,
338
354
  p_max_concurrency,
339
355
  p_max_size,
340
- p_release_interval_ms
341
- ) ON CONFLICT ("id") DO UPDATE SET
356
+ 0,
357
+ 0,
358
+ p_release_interval_ms,
359
+ FALSE,
360
+ 0
361
+ ) ON CONFLICT ("name") WHERE NOT "released" DO UPDATE SET
342
362
  "max_concurrency" = EXCLUDED."max_concurrency",
343
363
  "max_size" = EXCLUDED."max_size",
344
- "release_interval_ms" = EXCLUDED."release_interval_ms";
345
-
346
- UPDATE ${ref(params.schema)}."channel_state" SET
347
- "max_concurrency" = p_max_concurrency,
348
- "max_size" = p_max_size,
349
- "release_interval_ms" = p_release_interval_ms
350
- WHERE "id" = p_id;
364
+ "release_interval_ms" = EXCLUDED."release_interval_ms",
365
+ "released" = EXCLUDED."released",
366
+ "dequeue_next_at" = CASE
367
+ WHEN "channel"."message_id" IS NULL THEN NULL
368
+ ELSE GREATEST(
369
+ "channel"."message_dequeue_at",
370
+ "channel"."dequeue_prev_at" + EXCLUDED."release_interval_ms"
371
+ )
372
+ END;
351
373
  END;
352
374
  $$ LANGUAGE plpgsql;
353
375
  `];
@@ -367,68 +389,36 @@ const create = (params) => {
367
389
  DECLARE
368
390
  v_now BIGINT;
369
391
  v_dequeue_at BIGINT;
370
- v_channel_policy RECORD;
371
- v_channel_state RECORD;
392
+ v_channel RECORD;
372
393
  v_message RECORD;
373
394
  BEGIN
374
395
  v_now := ${ref(params.schema)}."epoch"();
375
396
  v_dequeue_at := COALESCE(p_dequeue_at, v_now);
376
397
 
377
398
  SELECT
378
- "id",
379
- "current_concurrency",
380
- "current_size",
381
- "max_concurrency",
382
- "max_size",
383
- "release_interval_ms",
384
- "dequeue_prev_at",
385
- "message_id",
386
- "message_dequeue_at"
387
- FROM ${ref(params.schema)}."channel_state"
388
- WHERE "id" = p_channel
399
+ "channel"."id",
400
+ "channel"."current_concurrency",
401
+ "channel"."current_size",
402
+ "channel"."max_concurrency",
403
+ "channel"."max_size",
404
+ "channel"."release_interval_ms",
405
+ "channel"."dequeue_prev_at",
406
+ "channel"."message_id",
407
+ "channel"."message_dequeue_at"
408
+ FROM ${ref(params.schema)}."channel"
409
+ WHERE "name" = p_channel
410
+ AND NOT "released"
389
411
  FOR UPDATE
390
- INTO v_channel_state;
391
-
392
- IF v_channel_state."id" IS NULL THEN
393
- SELECT
394
- "channel_policy"."max_concurrency",
395
- "channel_policy"."max_size",
396
- "channel_policy"."release_interval_ms"
397
- FROM ${ref(params.schema)}."channel_policy"
398
- WHERE "id" = p_channel
399
- FOR SHARE
400
- INTO v_channel_policy;
412
+ INTO v_channel;
401
413
 
402
- INSERT INTO ${ref(params.schema)}."channel_state" (
403
- "id",
404
- "current_concurrency",
405
- "current_size",
406
- "max_concurrency",
407
- "max_size",
408
- "release_interval_ms",
409
- "dequeue_prev_at"
410
- ) VALUES (
411
- p_channel,
412
- 0,
413
- 0,
414
- v_channel_policy."max_concurrency",
415
- v_channel_policy."max_size",
416
- v_channel_policy."release_interval_ms",
417
- v_now
418
- ) RETURNING
419
- "id",
420
- "current_concurrency",
421
- "current_size",
422
- "max_concurrency",
423
- "max_size",
424
- "release_interval_ms",
425
- "dequeue_prev_at",
426
- "message_id",
427
- "message_dequeue_at"
428
- INTO v_channel_state;
414
+ IF v_channel."id" IS NULL THEN
415
+ RETURN QUERY SELECT
416
+ ${value(9)},
417
+ NULL::JSON;
418
+ RETURN;
429
419
  END IF;
430
420
 
431
- IF v_channel_state."current_size" >= v_channel_state."max_size" THEN
421
+ IF v_channel."current_size" >= v_channel."max_size" THEN
432
422
  RETURN QUERY SELECT
433
423
  ${value(1)},
434
424
  NULL::JSON;
@@ -438,13 +428,11 @@ const create = (params) => {
438
428
  INSERT INTO ${ref(params.schema)}."message" (
439
429
  "channel_id",
440
430
  "content",
441
- "is_locked",
442
431
  "num_attempts",
443
432
  "dequeue_at"
444
433
  ) VALUES (
445
- p_channel,
434
+ v_channel."id",
446
435
  p_content,
447
- FALSE,
448
436
  0,
449
437
  v_dequeue_at
450
438
  ) RETURNING
@@ -452,23 +440,23 @@ const create = (params) => {
452
440
  INTO v_message;
453
441
 
454
442
  IF
455
- v_channel_state."message_id" IS NULL OR
456
- v_dequeue_at < v_channel_state."message_dequeue_at" OR
457
- v_dequeue_at = v_channel_state."message_dequeue_at" AND v_message."id" < v_channel_state."message_id"
443
+ v_channel."message_id" IS NULL OR
444
+ v_dequeue_at < v_channel."message_dequeue_at" OR
445
+ v_dequeue_at = v_channel."message_dequeue_at" AND v_message."id" < v_channel."message_id"
458
446
  THEN
459
- UPDATE ${ref(params.schema)}."channel_state" SET
460
- "current_size" = v_channel_state."current_size" + 1,
447
+ UPDATE ${ref(params.schema)}."channel" SET
448
+ "current_size" = v_channel."current_size" + 1,
461
449
  "message_id" = v_message."id",
462
450
  "message_dequeue_at" = v_dequeue_at,
463
451
  "dequeue_next_at" = GREATEST(
464
- v_channel_state."dequeue_prev_at" + COALESCE(v_channel_state."release_interval_ms", 0),
452
+ v_channel."dequeue_prev_at" + v_channel."release_interval_ms",
465
453
  v_dequeue_at
466
454
  )
467
- WHERE "id" = v_channel_state."id";
455
+ WHERE "id" = v_channel."id";
468
456
  ELSE
469
- UPDATE ${ref(params.schema)}."channel_state" SET
470
- "current_size" = v_channel_state."current_size" + 1
471
- WHERE "id" = v_channel_state."id";
457
+ UPDATE ${ref(params.schema)}."channel" SET
458
+ "current_size" = v_channel."current_size" + 1
459
+ WHERE "id" = v_channel."id";
472
460
  END IF;
473
461
 
474
462
  IF ${value(params.eventChannel !== null)} THEN
@@ -482,12 +470,11 @@ const create = (params) => {
482
470
  );
483
471
  END IF;
484
472
 
485
- RETURN QUERY SELECT
486
- ${value(0)},
487
- JSON_BUILD_OBJECT(
488
- 'id', v_message."id"::TEXT,
489
- 'channel_size', v_channel_state."current_size" + 1
490
- );
473
+ RETURN QUERY SELECT
474
+ ${value(0)},
475
+ JSON_BUILD_OBJECT(
476
+ 'id', v_message."id"::TEXT
477
+ );
491
478
  END;
492
479
  $$ LANGUAGE plpgsql;
493
480
  `];
@@ -507,7 +494,7 @@ const defer = (params) => {
507
494
  ) AS $$
508
495
  DECLARE
509
496
  v_now BIGINT;
510
- v_channel_state RECORD;
497
+ v_channel RECORD;
511
498
  v_message RECORD;
512
499
  v_dequeue_at BIGINT;
513
500
  BEGIN
@@ -517,7 +504,7 @@ const defer = (params) => {
517
504
  "message"."id",
518
505
  "message"."channel_id",
519
506
  "message"."num_attempts",
520
- "message"."is_locked"
507
+ "message"."unlock_at"
521
508
  FROM ${ref(params.schema)}."message"
522
509
  WHERE "id" = p_id
523
510
  FOR UPDATE
@@ -527,48 +514,48 @@ const defer = (params) => {
527
514
  RETURN QUERY SELECT
528
515
  ${value(4)};
529
516
  RETURN;
530
- ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
517
+ ELSIF v_message."unlock_at" IS NULL OR v_message."num_attempts" <> p_num_attempts THEN
531
518
  RETURN QUERY SELECT
532
519
  ${value(5)};
533
520
  RETURN;
534
521
  END IF;
535
522
 
536
523
  SELECT
537
- "channel_state"."current_concurrency",
538
- "channel_state"."release_interval_ms",
539
- "channel_state"."message_id",
540
- "channel_state"."message_dequeue_at",
541
- "channel_state"."dequeue_prev_at"
542
- FROM ${ref(params.schema)}."channel_state"
524
+ "channel"."current_concurrency",
525
+ "channel"."release_interval_ms",
526
+ "channel"."message_id",
527
+ "channel"."message_dequeue_at",
528
+ "channel"."dequeue_prev_at"
529
+ FROM ${ref(params.schema)}."channel"
543
530
  WHERE "id" = v_message."channel_id"
544
531
  FOR UPDATE
545
- INTO v_channel_state;
532
+ INTO v_channel;
546
533
 
547
534
  v_dequeue_at := COALESCE(p_dequeue_at, v_now);
548
535
 
549
536
  IF
550
- v_channel_state."message_id" IS NULL OR
551
- v_dequeue_at < v_channel_state."message_dequeue_at" OR
552
- v_dequeue_at = v_channel_state."message_dequeue_at" AND v_message."id" < v_channel_state."message_id"
537
+ v_channel."message_id" IS NULL OR
538
+ v_dequeue_at < v_channel."message_dequeue_at" OR
539
+ v_dequeue_at = v_channel."message_dequeue_at" AND v_message."id" < v_channel."message_id"
553
540
  THEN
554
- UPDATE ${ref(params.schema)}."channel_state" SET
555
- "current_concurrency" = v_channel_state."current_concurrency" - 1,
541
+ UPDATE ${ref(params.schema)}."channel" SET
542
+ "current_concurrency" = v_channel."current_concurrency" - 1,
556
543
  "message_id" = v_message."id",
557
544
  "message_dequeue_at" = v_dequeue_at,
558
545
  "dequeue_next_at" = GREATEST(
559
- v_channel_state."dequeue_prev_at" + COALESCE(v_channel_state."release_interval_ms", 0),
546
+ v_channel."dequeue_prev_at" + v_channel."release_interval_ms",
560
547
  v_dequeue_at
561
548
  )
562
549
  WHERE "id" = v_message."channel_id";
563
550
  ELSE
564
- UPDATE ${ref(params.schema)}."channel_state" SET
565
- "current_concurrency" = v_channel_state."current_concurrency" - 1
551
+ UPDATE ${ref(params.schema)}."channel" SET
552
+ "current_concurrency" = v_channel."current_concurrency" - 1
566
553
  WHERE "id" = v_message."channel_id";
567
554
  END IF;
568
555
 
569
556
  UPDATE ${ref(params.schema)}."message" SET
570
557
  "state" = p_state,
571
- "is_locked" = FALSE,
558
+ "unlock_at" = NULL,
572
559
  "dequeue_at" = v_dequeue_at
573
560
  WHERE "id" = p_id;
574
561
 
@@ -601,21 +588,21 @@ const queryNextLockedMessage = (params) => fragment`
601
588
  "message"."unlock_at",
602
589
  "message"."num_attempts"
603
590
  FROM ${ref(params.schema)}."message"
604
- WHERE "is_locked"
591
+ WHERE "unlock_at" IS NOT NULL
605
592
  AND "unlock_at" <= ${params.now}
606
593
  ORDER BY "unlock_at" ASC
607
594
  `;
608
595
  const queryNextChannel = (params) => fragment`
609
596
  SELECT
610
- "channel_state"."id",
611
- "channel_state"."release_interval_ms",
612
- "channel_state"."message_id",
613
- "channel_state"."dequeue_next_at",
614
- "channel_state"."dequeue_prev_at",
615
- "channel_state"."current_concurrency"
616
- FROM ${ref(params.schema)}."channel_state"
597
+ "channel"."id",
598
+ "channel"."release_interval_ms",
599
+ "channel"."message_id",
600
+ "channel"."dequeue_next_at",
601
+ "channel"."dequeue_prev_at",
602
+ "channel"."current_concurrency"
603
+ FROM ${ref(params.schema)}."channel"
617
604
  WHERE "message_id" IS NOT NULL
618
- AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency")
605
+ AND "current_concurrency" < "max_concurrency"
619
606
  ORDER BY "dequeue_next_at" ASC
620
607
  `;
621
608
  const queryNextChannelMessage = (params) => fragment`
@@ -623,8 +610,8 @@ const queryNextChannelMessage = (params) => fragment`
623
610
  "message"."id",
624
611
  "message"."dequeue_at"
625
612
  FROM ${ref(params.schema)}."message"
626
- WHERE NOT "is_locked"
627
- AND "channel_id" = ${params.channelId}
613
+ WHERE "unlock_at" IS NULL
614
+ AND "channel_id" = ${params.channel}
628
615
  ORDER BY "dequeue_at" ASC, "id" ASC
629
616
  `;
630
617
  const dequeue = (params) => {
@@ -633,7 +620,7 @@ const dequeue = (params) => {
633
620
  schema: params.schema
634
621
  });
635
622
  const nextChannelMessage = queryNextChannelMessage({
636
- channelId: fragment`v_channel_state."id"`,
623
+ channel: fragment`v_channel."id"`,
637
624
  schema: params.schema
638
625
  });
639
626
  const nextChannel = queryNextChannel({ schema: params.schema });
@@ -647,12 +634,12 @@ const dequeue = (params) => {
647
634
  state BYTEA,
648
635
  metadata JSON
649
636
  ) AS $$
650
- DECLARE
651
- v_now BIGINT;
652
- v_channel_state RECORD;
653
- v_message_locked RECORD;
654
- v_message_dequeue RECORD;
655
- v_message_next RECORD;
637
+ DECLARE
638
+ v_now BIGINT;
639
+ v_channel RECORD;
640
+ v_message_locked RECORD;
641
+ v_message_dequeue RECORD;
642
+ v_message_next RECORD;
656
643
  BEGIN
657
644
  v_now := ${ref(params.schema)}."epoch"();
658
645
 
@@ -675,7 +662,6 @@ const dequeue = (params) => {
675
662
  JSON_BUILD_OBJECT(
676
663
  'id', v_message_locked."id",
677
664
  'is_unlocked', TRUE,
678
- 'channel', v_message_locked."channel_id",
679
665
  'num_attempts', v_message_locked."num_attempts" + 1
680
666
  );
681
667
  RETURN;
@@ -683,16 +669,16 @@ const dequeue = (params) => {
683
669
 
684
670
  ${nextChannel}
685
671
  FOR UPDATE
686
- SKIP LOCKED
687
- LIMIT 1
688
- INTO v_channel_state;
672
+ SKIP LOCKED
673
+ LIMIT 1
674
+ INTO v_channel;
689
675
 
690
- IF v_channel_state."id" IS NULL OR v_channel_state."dequeue_next_at" > v_now THEN
691
- RETURN QUERY SELECT
692
- ${value(2)},
693
- NULL::BYTEA,
694
- NULL::BYTEA,
695
- NULL::JSON;
676
+ IF v_channel."id" IS NULL OR v_channel."dequeue_next_at" > v_now THEN
677
+ RETURN QUERY SELECT
678
+ ${value(2)},
679
+ NULL::BYTEA,
680
+ NULL::BYTEA,
681
+ NULL::JSON;
696
682
  RETURN;
697
683
  END IF;
698
684
 
@@ -701,39 +687,38 @@ const dequeue = (params) => {
701
687
  "message"."channel_id",
702
688
  "message"."content",
703
689
  "message"."num_attempts",
704
- "message"."state"
705
- FROM ${ref(params.schema)}."message"
706
- WHERE "id" = v_channel_state."message_id"
707
- INTO v_message_dequeue;
690
+ "message"."state"
691
+ FROM ${ref(params.schema)}."message"
692
+ WHERE "id" = v_channel."message_id"
693
+ INTO v_message_dequeue;
708
694
 
709
695
  UPDATE ${ref(params.schema)}."message" SET
710
- "is_locked" = TRUE,
711
696
  "num_attempts" = v_message_dequeue."num_attempts" + 1,
712
697
  "unlock_at" = v_now + p_lock_ms
713
698
  WHERE "id" = v_message_dequeue."id";
714
699
 
715
700
  ${nextChannelMessage}
716
701
  LIMIT 1
717
- INTO v_message_next;
702
+ INTO v_message_next;
718
703
 
719
- IF v_message_next."id" IS NULL THEN
720
- UPDATE ${ref(params.schema)}."channel_state" SET
721
- "current_concurrency" = v_channel_state."current_concurrency" + 1,
722
- "dequeue_prev_at" = v_now,
723
- "message_id" = NULL
724
- WHERE "id" = v_channel_state."id";
725
- ELSE
726
- UPDATE ${ref(params.schema)}."channel_state" SET
727
- "current_concurrency" = v_channel_state."current_concurrency" + 1,
728
- "message_id" = v_message_next."id",
729
- "message_dequeue_at" = v_message_next."dequeue_at",
730
- "dequeue_prev_at" = v_now,
731
- "dequeue_next_at" = GREATEST(
732
- v_message_next."dequeue_at",
733
- v_now + COALESCE(v_channel_state."release_interval_ms", 0)
734
- )
735
- WHERE "id" = v_channel_state."id";
736
- END IF;
704
+ IF v_message_next."id" IS NULL THEN
705
+ UPDATE ${ref(params.schema)}."channel" SET
706
+ "current_concurrency" = v_channel."current_concurrency" + 1,
707
+ "dequeue_prev_at" = v_now,
708
+ "message_id" = NULL
709
+ WHERE "id" = v_channel."id";
710
+ ELSE
711
+ UPDATE ${ref(params.schema)}."channel" SET
712
+ "current_concurrency" = v_channel."current_concurrency" + 1,
713
+ "message_id" = v_message_next."id",
714
+ "message_dequeue_at" = v_message_next."dequeue_at",
715
+ "dequeue_prev_at" = v_now,
716
+ "dequeue_next_at" = GREATEST(
717
+ v_message_next."dequeue_at",
718
+ v_now + v_channel."release_interval_ms"
719
+ )
720
+ WHERE "id" = v_channel."id";
721
+ END IF;
737
722
 
738
723
  RETURN QUERY SELECT
739
724
  ${value(3)},
@@ -742,7 +727,6 @@ const dequeue = (params) => {
742
727
  JSON_BUILD_OBJECT(
743
728
  'id', v_message_dequeue."id"::TEXT,
744
729
  'is_unlocked', FALSE,
745
- 'channel_id', v_message_dequeue."channel_id",
746
730
  'num_attempts', v_message_dequeue."num_attempts" + 1
747
731
  );
748
732
  RETURN;
@@ -771,7 +755,6 @@ const heartbeat = (params) => {
771
755
  SELECT
772
756
  "message"."id",
773
757
  "message"."num_attempts",
774
- "message"."is_locked",
775
758
  "message"."unlock_at"
776
759
  FROM ${ref(params.schema)}."message"
777
760
  WHERE "id" = p_id
@@ -782,7 +765,7 @@ const heartbeat = (params) => {
782
765
  RETURN QUERY SELECT
783
766
  ${value(4)};
784
767
  RETURN;
785
- ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
768
+ ELSIF v_message."unlock_at" IS NULL OR v_message."num_attempts" <> p_num_attempts THEN
786
769
  RETURN QUERY SELECT
787
770
  ${value(5)};
788
771
  RETURN;
@@ -803,10 +786,10 @@ const heartbeat = (params) => {
803
786
  `];
804
787
  };
805
788
  //#endregion
806
- //#region src/install/function/message/retire.ts
807
- const retire = (params) => {
789
+ //#region src/install/function/message/complete.ts
790
+ const complete = (params) => {
808
791
  return [fragment`
809
- CREATE FUNCTION ${ref(params.schema)}."message_retire" (
792
+ CREATE FUNCTION ${ref(params.schema)}."message_complete" (
810
793
  p_id BIGINT,
811
794
  p_num_attempts BIGINT
812
795
  )
@@ -814,15 +797,14 @@ const retire = (params) => {
814
797
  result_code INTEGER
815
798
  ) AS $$
816
799
  DECLARE
817
- v_channel_policy RECORD;
818
- v_channel_state RECORD;
800
+ v_channel RECORD;
819
801
  v_message RECORD;
820
802
  BEGIN
821
803
  SELECT
822
804
  "message"."id",
823
805
  "message"."channel_id",
824
806
  "message"."num_attempts",
825
- "message"."is_locked"
807
+ "message"."unlock_at"
826
808
  FROM ${ref(params.schema)}."message"
827
809
  WHERE "id" = p_id
828
810
  FOR UPDATE
@@ -832,36 +814,30 @@ const retire = (params) => {
832
814
  RETURN QUERY SELECT
833
815
  ${value(4)};
834
816
  RETURN;
835
- ELSIF NOT v_message."is_locked" OR v_message."num_attempts" <> p_num_attempts THEN
817
+ ELSIF v_message."unlock_at" IS NULL OR v_message."num_attempts" <> p_num_attempts THEN
836
818
  RETURN QUERY SELECT
837
819
  ${value(5)};
838
820
  RETURN;
839
821
  END IF;
840
822
 
841
823
  SELECT
842
- "channel_policy"."id"
843
- FROM ${ref(params.schema)}."channel_policy"
844
- WHERE "id" = v_message."channel_id"
845
- FOR SHARE
846
- INTO v_channel_policy;
847
-
848
- SELECT
849
- "channel_state"."id",
850
- "channel_state"."current_size",
851
- "channel_state"."current_concurrency"
852
- FROM ${ref(params.schema)}."channel_state"
824
+ "channel"."id",
825
+ "channel"."released",
826
+ "channel"."current_size",
827
+ "channel"."current_concurrency"
828
+ FROM ${ref(params.schema)}."channel"
853
829
  WHERE "id" = v_message."channel_id"
854
830
  FOR UPDATE
855
- INTO v_channel_state;
831
+ INTO v_channel;
856
832
 
857
- IF v_channel_policy."id" IS NULL AND v_channel_state."current_size" = 1 THEN
858
- DELETE FROM ${ref(params.schema)}."channel_state"
859
- WHERE "id" = v_channel_state."id";
833
+ IF v_channel."released" AND v_channel."current_size" = 1 THEN
834
+ DELETE FROM ${ref(params.schema)}."channel"
835
+ WHERE "id" = v_channel."id";
860
836
  ELSE
861
- UPDATE ${ref(params.schema)}."channel_state" SET
862
- "current_concurrency" = v_channel_state."current_concurrency" - 1,
863
- "current_size" = v_channel_state."current_size" - 1
864
- WHERE "id" = v_channel_state."id";
837
+ UPDATE ${ref(params.schema)}."channel" SET
838
+ "current_concurrency" = v_channel."current_concurrency" - 1,
839
+ "current_size" = v_channel."current_size" - 1
840
+ WHERE "id" = v_channel."id";
865
841
  END IF;
866
842
 
867
843
  IF ${value(params.eventChannel !== null)} THEN
@@ -885,28 +861,19 @@ const retire = (params) => {
885
861
  `];
886
862
  };
887
863
  //#endregion
888
- //#region src/install/table/channel-policy.ts
889
- const channelPolicy = (params) => {
890
- return [fragment`
891
- CREATE TABLE ${ref(params.schema)}."channel_policy" (
892
- "id" TEXT NOT NULL,
893
- "max_concurrency" INTEGER,
894
- "max_size" INTEGER,
895
- "release_interval_ms" INTEGER,
896
- PRIMARY KEY ("id")
897
- );
898
- `];
899
- };
900
- //#endregion
901
- //#region src/install/table/channel-state.ts
902
- const channelState = (params) => {
903
- const dequeueIndex = [params.schema, "channel_state_dequeue_ix"].join("_");
904
- return [fragment`
905
- CREATE TABLE ${ref(params.schema)}."channel_state" (
906
- "id" TEXT NOT NULL,
907
- "max_concurrency" INTEGER,
908
- "max_size" INTEGER,
909
- "release_interval_ms" INTEGER,
864
+ //#region src/install/table/channel.ts
865
+ const channel = (params) => {
866
+ const dequeueIndex = [params.schema, "channel_dequeue_ix"].join("_");
867
+ const nameIndex = [params.schema, "channel_name_ix"].join("_");
868
+ return [
869
+ fragment`
870
+ CREATE TABLE ${ref(params.schema)}."channel" (
871
+ "id" BIGSERIAL NOT NULL,
872
+ "name" TEXT NOT NULL,
873
+ "released" BOOLEAN NOT NULL,
874
+ "max_concurrency" INTEGER NOT NULL,
875
+ "max_size" INTEGER NOT NULL,
876
+ "release_interval_ms" INTEGER NOT NULL,
910
877
  "current_size" INTEGER NOT NULL,
911
878
  "current_concurrency" INTEGER NOT NULL,
912
879
  "message_id" BIGINT,
@@ -915,13 +882,21 @@ const channelState = (params) => {
915
882
  "dequeue_next_at" BIGINT NULL,
916
883
  PRIMARY KEY ("id")
917
884
  );
918
- `, fragment`
885
+ `,
886
+ fragment`
887
+ CREATE UNIQUE INDEX ${ref(nameIndex)}
888
+ ON ${ref(params.schema)}."channel" (
889
+ "name"
890
+ ) WHERE NOT "released";
891
+ `,
892
+ fragment`
919
893
  CREATE INDEX ${ref(dequeueIndex)}
920
- ON ${ref(params.schema)}."channel_state" (
894
+ ON ${ref(params.schema)}."channel" (
921
895
  "dequeue_next_at" ASC
922
896
  ) WHERE "message_id" IS NOT NULL
923
- AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
924
- `];
897
+ AND "current_concurrency" < "max_concurrency";
898
+ `
899
+ ];
925
900
  };
926
901
  //#endregion
927
902
  //#region src/install/table/message.ts
@@ -930,10 +905,9 @@ const message = (params) => {
930
905
  fragment`
931
906
  CREATE TABLE ${ref(params.schema)}."message" (
932
907
  "id" BIGSERIAL NOT NULL,
933
- "channel_id" TEXT NOT NULL,
908
+ "channel_id" BIGINT NOT NULL,
934
909
  "content" BYTEA NOT NULL,
935
910
  "state" BYTEA,
936
- "is_locked" BOOLEAN NOT NULL,
937
911
  "num_attempts" BIGINT NOT NULL,
938
912
  "dequeue_at" BIGINT NOT NULL,
939
913
  "unlock_at" BIGINT,
@@ -946,100 +920,69 @@ const message = (params) => {
946
920
  "channel_id",
947
921
  "dequeue_at" ASC,
948
922
  "id" ASC
949
- ) WHERE NOT "is_locked";
923
+ ) WHERE "unlock_at" IS NULL;
950
924
  `,
951
925
  fragment`
952
926
  CREATE INDEX "message_locked_dequeue_ix"
953
927
  ON ${ref(params.schema)}."message" (
954
928
  "unlock_at" ASC
955
- ) WHERE "is_locked";
929
+ ) WHERE "unlock_at" IS NOT NULL;
956
930
  `
957
931
  ];
958
932
  };
959
933
  //#endregion
960
934
  //#region src/queue/module/channel/message.ts
961
- var Message$2 = class {
935
+ var Message$1 = class {
962
936
  schema;
963
- channelId;
937
+ channel;
964
938
  adaptor;
965
939
  constructor(params) {
966
940
  this.schema = params.schema;
967
941
  this.adaptor = params.adaptor;
968
- this.channelId = params.channelId;
942
+ this.channel = params.channel;
969
943
  }
970
944
  async create(params) {
971
945
  const adaptedClient = this.adaptor(params.databaseClient);
972
946
  return await new Create({
973
947
  schema: this.schema,
974
- channelId: this.channelId,
948
+ channel: this.channel,
975
949
  content: params.content,
976
950
  dequeueAt: params.dequeueAt ?? null
977
951
  }).execute(adaptedClient);
978
952
  }
979
953
  };
980
954
  //#endregion
981
- //#region src/queue/module/channel/policy.ts
982
- var Policy = class {
955
+ //#region src/queue/module/channel/index.ts
956
+ var Channel = class {
983
957
  schema;
984
958
  adaptor;
985
- channelId;
959
+ channel;
960
+ message;
986
961
  constructor(params) {
987
962
  this.schema = params.schema;
988
963
  this.adaptor = params.adaptor;
989
- this.channelId = params.channelId;
964
+ this.channel = params.channel;
965
+ this.message = new Message$1({
966
+ schema: params.schema,
967
+ adaptor: params.adaptor,
968
+ channel: params.channel
969
+ });
990
970
  }
991
971
  set(params) {
992
972
  const adaptedClient = this.adaptor(params.databaseClient);
993
973
  return new Set({
994
974
  schema: this.schema,
995
- channelId: this.channelId,
996
- maxConcurrency: params.maxConcurrency,
997
- maxSize: params.maxSize,
998
- releaseIntervalMs: params.releaseIntervalMs
975
+ channel: this.channel,
976
+ maxConcurrency: params.maxConcurrency ?? 2147483647,
977
+ maxSize: params.maxSize ?? 2147483647,
978
+ releaseIntervalMs: params.releaseIntervalMs ?? 0
999
979
  }).execute(adaptedClient);
1000
980
  }
1001
- clear(params) {
1002
- const adaptedClient = this.adaptor(params.databaseClient);
1003
- return new Clear({
1004
- schema: this.schema,
1005
- channelId: this.channelId
1006
- }).execute(adaptedClient);
1007
- }
1008
- };
1009
- //#endregion
1010
- //#region src/queue/module/channel/index.ts
1011
- var Channel = class {
1012
- policy;
1013
- message;
1014
- constructor(params) {
1015
- this.message = new Message$2({
1016
- schema: params.schema,
1017
- adaptor: params.adaptor,
1018
- channelId: params.channelId
1019
- });
1020
- this.policy = new Policy({
1021
- schema: params.schema,
1022
- adaptor: params.adaptor,
1023
- channelId: params.channelId
1024
- });
1025
- }
1026
- };
1027
- //#endregion
1028
- //#region src/queue/module/message.ts
1029
- var Message$1 = class {
1030
- schema;
1031
- adaptor;
1032
- constructor(params) {
1033
- this.schema = params.schema;
1034
- this.adaptor = params.adaptor;
1035
- }
1036
- async create(params) {
981
+ release(params) {
1037
982
  const adaptedClient = this.adaptor(params.databaseClient);
1038
- return await new Create({
983
+ return new Release({
1039
984
  schema: this.schema,
1040
- content: params.content,
1041
- dequeueAt: params.dequeueAt ?? null,
1042
- channelId: randomUUID()
985
+ channel: this.channel
1043
986
  }).execute(adaptedClient);
1044
987
  }
1045
988
  };
@@ -1053,7 +996,7 @@ const decode = (payload) => {
1053
996
  dequeueAt: Number(parsed.dequeue_at)
1054
997
  };
1055
998
  else if (parsed.type === 6) return {
1056
- eventType: "MESSAGE_RETIRED",
999
+ eventType: "MESSAGE_COMPLETED",
1057
1000
  id: parsed.id
1058
1001
  };
1059
1002
  else if (parsed.type === 7) return {
@@ -1070,7 +1013,6 @@ var Message = class {
1070
1013
  adaptor;
1071
1014
  id;
1072
1015
  isUnlocked;
1073
- channelId;
1074
1016
  content;
1075
1017
  state;
1076
1018
  numAttempts;
@@ -1078,7 +1020,6 @@ var Message = class {
1078
1020
  this.schema = params.schema;
1079
1021
  this.adaptor = params.adaptor;
1080
1022
  this.id = params.id;
1081
- this.channelId = params.channelId;
1082
1023
  this.isUnlocked = params.isUnlocked;
1083
1024
  this.content = params.content;
1084
1025
  this.state = params.state;
@@ -1094,9 +1035,9 @@ var Message = class {
1094
1035
  state: params.state ?? null
1095
1036
  }).execute(adaptedClient);
1096
1037
  }
1097
- async retire(params) {
1038
+ async complete(params) {
1098
1039
  const adaptedClient = this.adaptor(params.databaseClient);
1099
- return new Retire({
1040
+ return new Complete({
1100
1041
  schema: this.schema,
1101
1042
  numAttempts: this.numAttempts,
1102
1043
  id: this.id
@@ -1116,20 +1057,17 @@ var Message = class {
1116
1057
  //#region src/queue/index.ts
1117
1058
  var Queue = class {
1118
1059
  schema;
1119
- message;
1060
+ lockMs;
1120
1061
  adaptor;
1121
1062
  constructor(params) {
1122
1063
  this.schema = params.schema;
1064
+ this.lockMs = params.lockMs;
1123
1065
  this.adaptor = params.adaptor ? params.adaptor : (x) => x;
1124
- this.message = new Message$1({
1125
- schema: this.schema,
1126
- adaptor: this.adaptor
1127
- });
1128
1066
  }
1129
1067
  async dequeue(params) {
1130
1068
  const dequeue = new Dequeue({
1131
1069
  schema: this.schema,
1132
- lockMs: params.lockMs
1070
+ lockMs: this.lockMs
1133
1071
  });
1134
1072
  const adaptedClient = this.adaptor(params.databaseClient);
1135
1073
  const result = await dequeue.execute(adaptedClient);
@@ -1139,7 +1077,6 @@ var Queue = class {
1139
1077
  schema: this.schema,
1140
1078
  adaptor: this.adaptor,
1141
1079
  id: result.id,
1142
- channelId: result.channelId,
1143
1080
  isUnlocked: result.isUnlocked,
1144
1081
  content: result.content,
1145
1082
  state: result.state,
@@ -1148,24 +1085,23 @@ var Queue = class {
1148
1085
  };
1149
1086
  else return result;
1150
1087
  }
1151
- channel(channelId) {
1088
+ channel(channel) {
1152
1089
  return new Channel({
1153
1090
  adaptor: this.adaptor,
1154
1091
  schema: this.schema,
1155
- channelId
1092
+ channel
1156
1093
  });
1157
1094
  }
1158
1095
  install(params = {}) {
1159
1096
  return [
1160
- channelPolicy,
1161
- channelState,
1097
+ channel,
1162
1098
  message,
1163
1099
  epoch,
1164
1100
  set,
1165
- clear,
1101
+ release,
1166
1102
  create,
1167
1103
  dequeue,
1168
- retire,
1104
+ complete,
1169
1105
  defer,
1170
1106
  heartbeat
1171
1107
  ].flatMap((install) => install({