queen-mq 1.2.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "queen-mq",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Partitioned message queue on PostgreSQL — broker client + fluent streaming SDK (windows, joins, gates) in one package",
6
6
  "main": "client-v2/index.js",
@@ -1,6 +1,27 @@
1
1
 
2
- // To pass this test the server needs to
3
- // be started with RETENTION_INTERVAL=2000
2
+ // The sweep is periodic AND backs off, so the wait below is a bound, not a guess.
3
+ //
4
+ // retention.rs gives a partition a strike for every visit that deletes nothing
5
+ // and skips it for BACKOFF_BASE_CYCLES (8) cycles after the first one. The
6
+ // sweep that runs while these 100 messages are still younger than
7
+ // `retentionSeconds` is exactly such a fruitless visit, so the partition is
8
+ // parked for 8 * RETENTION_INTERVAL before anything can be deleted:
9
+ //
10
+ // worst case = retentionSeconds + BACKOFF_BASE_CYCLES * RETENTION_INTERVAL
11
+ // = 10s + 8 * 5s (the default)
12
+ // = 50s
13
+ //
14
+ // The old wait was 15s, taken from a comment that assumed the broker ran with
15
+ // RETENTION_INTERVAL=2000 -- which no stack in test/compose has ever set. It
16
+ // could not pass once the backoff landed (751435f5), and it is why `suites (js)`
17
+ // was 172/173 red on master for weeks. Measured on the single stack: still
18
+ // present at 15s/25s/35s, deleted by 50s.
19
+ //
20
+ // Do NOT turn this into a poll loop: pop() CONSUMES, so a poll that pops early
21
+ // empties the queue itself and the next read returns 0 whether retention ran or
22
+ // not -- a test that passes for the wrong reason.
23
+ const RETENTION_SWEEP_WAIT_MS = 65000
24
+
4
25
  export async function retentionTest(client) {
5
26
  const queueName = 'test-queue-retention-01';
6
27
  const queue = await client
@@ -20,7 +41,7 @@ export async function retentionTest(client) {
20
41
  .push([{ data: { message: i } }])
21
42
  }
22
43
 
23
- await new Promise(resolve => setTimeout(resolve, 15000))
44
+ await new Promise(resolve => setTimeout(resolve, RETENTION_SWEEP_WAIT_MS))
24
45
 
25
46
  const messages = await client
26
47
  .queue(queueName)
@@ -32,12 +53,12 @@ export async function retentionTest(client) {
32
53
  return { success: true, message: 'Messages cleaned up' }
33
54
  }
34
55
 
35
- return { success: false }
56
+ return { success: false, message: `retention left ${messages.length}/100 messages after ${RETENTION_SWEEP_WAIT_MS / 1000}s` }
36
57
  }
37
58
 
38
59
 
39
- // To pass this test the server needs to
40
- // be started with RETENTION_INTERVAL=2000
60
+ // maxWaitTimeSeconds eviction, which is a different retention phase with its own
61
+ // backoff map -- so it is not subject to the bound above and 15s has held.
41
62
  export async function retentionTestMaxTime(client) {
42
63
  const queueName = 'test-queue-retention-02';
43
64
  const queue = await client
@@ -65,5 +86,5 @@ export async function retentionTestMaxTime(client) {
65
86
  return { success: true, message: 'Messages cleaned up' }
66
87
  }
67
88
 
68
- return { success: false }
89
+ return { success: false, message: `max-wait eviction left ${messages.length}/100 messages` }
69
90
  }
@@ -149,12 +149,16 @@ export async function tumblingAggregateAllStats(client) {
149
149
  await client.queue(src).create()
150
150
  await client.queue(sink).create()
151
151
 
152
- // Push 5 values into the SAME window (all within 1 second), then idle
153
- // flush will close it. Sum = 50, count = 5, avg = 10, min = 2, max = 30.
154
- const values = [10, 5, 30, 2, 3]
155
- for (const v of values) {
156
- await client.queue(src).partition('p').push([{ data: { v } }])
157
- }
152
+ // One batched push = one segment = one timestamp, so the five values cannot
153
+ // straddle a 3-second window boundary. Pushing them "within 1 second" of each
154
+ // other does NOT put them in one window: the bucket is absolute-aligned
155
+ // (floor(ts / 3000) * 3000), so two pushes milliseconds apart still split when
156
+ // a boundary falls between them, and the assertion below reads only the FIRST
157
+ // emit. Sum = 50, count = 5, avg = 10, min = 2, max = 30.
158
+ await client.queue(src).partition('p').push([
159
+ { data: { v: 10 } }, { data: { v: 5 } }, { data: { v: 30 } },
160
+ { data: { v: 2 } }, { data: { v: 3 } },
161
+ ])
158
162
 
159
163
  const handle = await Stream
160
164
  .from(client.queue(src))