lonnymq 0.0.7 → 0.0.8
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/README.md +138 -63
- package/dist/index.cjs +48 -36
- package/dist/index.d.ts +3 -1
- package/dist/index.js +48 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# LonnyMQ
|
|
2
2
|
|
|
3
|
-
A high
|
|
3
|
+
A high-performance, multi-tenant Postgres message queue implementation for Node.js/TypeScript.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
- High throughput
|
|
8
|
+
- Multi-tenant concurrency and capacity constraints
|
|
9
|
+
- Durable message processing
|
|
10
|
+
- Flexible message processing retries/deferrals
|
|
11
|
+
- Message de-duplication
|
|
12
|
+
- Queue actions as part of *existing* database transactions
|
|
13
|
+
- Database client agnostic
|
|
14
|
+
- Instant reactivity via `LISTEN/NOTIFY`
|
|
15
|
+
- Zero dependencies
|
|
16
16
|
|
|
17
|
-
N.B.
|
|
17
|
+
N.B. Unlike other queue implementations, LonnyMQ provides direct access to queue methods rather than providing batteries-included Worker/Processor daemons.
|
|
18
18
|
|
|
19
19
|
## Quick Look
|
|
20
20
|
|
|
@@ -27,59 +27,84 @@ databaseClient satisfies DatabaseClient
|
|
|
27
27
|
|
|
28
28
|
const queue = new Queue({ schema: "lonny" })
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// Run migrations first
|
|
31
|
+
for (const migration of queue.migrations()) {
|
|
32
|
+
for (const sql of migration.sql) {
|
|
33
|
+
await databaseClient.query(sql, [])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create messages
|
|
38
|
+
for (let ix = 0; ix < 500; ix += 1) {
|
|
39
|
+
await queue
|
|
32
40
|
.channel("myChannel")
|
|
33
41
|
.message
|
|
34
42
|
.create({
|
|
35
|
-
content: "Hello",
|
|
43
|
+
content: Buffer.from("Hello"),
|
|
36
44
|
databaseClient,
|
|
37
45
|
})
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
// Process messages
|
|
49
|
+
while (true) {
|
|
41
50
|
const dequeueResult = await queue.dequeue({ databaseClient })
|
|
42
|
-
if(dequeueResult.resultType === "MESSAGE_NOT_AVAILABLE") {
|
|
43
|
-
|
|
51
|
+
if (dequeueResult.resultType === "MESSAGE_NOT_AVAILABLE") {
|
|
52
|
+
const sleepMs = Math.min(1000, dequeueResult.retryMs ?? 5000)
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, sleepMs))
|
|
44
54
|
continue
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
console.log(dequeueResult.message.content)
|
|
57
|
+
console.log(dequeueResult.message.content.toString())
|
|
48
58
|
await dequeueResult.message.delete({ databaseClient })
|
|
49
59
|
}
|
|
50
60
|
```
|
|
51
61
|
|
|
52
62
|
## Setup & Installation
|
|
53
63
|
|
|
54
|
-
LonnyMQ can be installed from npm
|
|
64
|
+
LonnyMQ can be installed from npm:
|
|
55
65
|
|
|
56
66
|
```bash
|
|
57
67
|
npm install lonnymq
|
|
58
68
|
```
|
|
59
69
|
|
|
60
|
-
Once the package is installed,
|
|
70
|
+
Once the package is installed, you need to install the requisite database machinery. LonnyMQ is agnostic to database client and migration process, providing users with an ordered list of "Migrations" - each containing a unique name and SQL fragments to be executed.
|
|
61
71
|
|
|
62
72
|
```typescript
|
|
63
73
|
const queue = new Queue({ schema: "lonny" })
|
|
64
74
|
const migrations = queue.migrations()
|
|
75
|
+
|
|
76
|
+
// Execute migrations (in a transaction for safety)
|
|
77
|
+
await databaseClient.query("BEGIN")
|
|
78
|
+
try {
|
|
79
|
+
for (const migration of migrations) {
|
|
80
|
+
for (const sql of migration.sql) {
|
|
81
|
+
await databaseClient.query(sql, [])
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
await databaseClient.query("COMMIT")
|
|
85
|
+
} catch (error) {
|
|
86
|
+
await databaseClient.query("ROLLBACK")
|
|
87
|
+
throw error
|
|
88
|
+
}
|
|
65
89
|
```
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
**Note:** Migration SQL is not idempotent and should be executed within a transaction that can be rolled back if needed.
|
|
68
92
|
|
|
69
93
|
## Channels
|
|
70
94
|
|
|
71
|
-
Channels
|
|
95
|
+
Channels provide LonnyMQ's multi-tenancy support. They can be considered lightweight sub-queues that are read from in a round-robin fashion. There is no performance penalty associated with using large numbers of channels, so they can be assigned on a highly granular basis (e.g., per-user) to ensure work is scheduled fairly.
|
|
72
96
|
|
|
73
|
-
Channels can be configured with concurrency and
|
|
97
|
+
Channels can be configured with concurrency, capacity, and rate limits by setting their "channel policy":
|
|
74
98
|
|
|
75
99
|
```typescript
|
|
76
100
|
await queue
|
|
77
101
|
.channel("my-channel")
|
|
78
102
|
.policy
|
|
79
103
|
.set({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
104
|
+
databaseClient,
|
|
105
|
+
maxConcurrency: 1,
|
|
106
|
+
maxSize: 100,
|
|
107
|
+
releaseIntervalMs: 1000
|
|
83
108
|
})
|
|
84
109
|
|
|
85
110
|
// Remove all constraints:
|
|
@@ -91,7 +116,7 @@ await queue
|
|
|
91
116
|
|
|
92
117
|
## Message creation
|
|
93
118
|
|
|
94
|
-
|
|
119
|
+
You can add a message to the queue (and assign it to a particular channel) using the `create` function:
|
|
95
120
|
|
|
96
121
|
```typescript
|
|
97
122
|
await queue
|
|
@@ -99,82 +124,132 @@ await queue
|
|
|
99
124
|
.message
|
|
100
125
|
.create({
|
|
101
126
|
databaseClient,
|
|
102
|
-
content: "Hello, world"
|
|
127
|
+
content: Buffer.from("Hello, world"),
|
|
128
|
+
name: "optional-dedup-key",
|
|
129
|
+
lockMs: 30000, // 30 seconds
|
|
130
|
+
delayMs: 5000 // 5 second delay
|
|
103
131
|
})
|
|
104
132
|
```
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
The `name` argument can be provided for de-duplication purposes: if a message that has *never* been dequeued exists with the same name within the same channel, no new message will be created.
|
|
135
|
+
|
|
136
|
+
## Message Processing
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
Messages can be fetched for processing by calling `dequeue` on the `Queue` - this locks the message. Once processing has completed, messages must be "finalized" via **deletion** or **deferral** (for further processing in the future).
|
|
109
139
|
|
|
110
|
-
|
|
140
|
+
```typescript
|
|
141
|
+
const dequeueResult = await queue.dequeue({ databaseClient })
|
|
142
|
+
|
|
143
|
+
if (dequeueResult.resultType === "MESSAGE_DEQUEUED") {
|
|
144
|
+
const { message } = dequeueResult
|
|
145
|
+
console.log(`Processing message: ${message.id}`)
|
|
146
|
+
console.log(`Content: ${message.content.toString()}`)
|
|
147
|
+
console.log(`Attempts: ${message.numAttempts}`)
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Process the message...
|
|
151
|
+
await processMessage(message.content)
|
|
152
|
+
|
|
153
|
+
// Delete on success
|
|
154
|
+
await message.delete({ databaseClient })
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// Defer for retry with updated state
|
|
157
|
+
await message.defer({
|
|
158
|
+
databaseClient,
|
|
159
|
+
delays: 30000, // Retry in 30 seconds
|
|
160
|
+
state: Buffer.from(JSON.stringify({ error: error.message }))
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
111
165
|
|
|
112
|
-
When deferring a message,
|
|
166
|
+
When deferring a message, you can optionally specify `delayMs` and `state` arguments. The `delayMs` parameter tells the queue how long to wait before allowing the message to be re-processed, and `state` allows you to "save your work" and implement durable and/or repeating/scheduled tasks.
|
|
113
167
|
|
|
114
|
-
### Graceful
|
|
168
|
+
### Graceful Shutdowns and Message Sweeping
|
|
115
169
|
|
|
116
|
-
If your program ends unexpectedly, messages that are
|
|
170
|
+
If your program ends unexpectedly, messages that are currently being processed may become "orphaned" in a locked state - causing channel blockages and reducing throughput. To mitigate this problem, it's imperative that you gracefully shutdown by catching unhandled exceptions and signals (i.e., `SIGINT`/`SIGTERM`) and finalizing all outstanding messages prior to exiting.
|
|
117
171
|
|
|
118
|
-
That said, despite our best efforts,
|
|
172
|
+
That said, despite our best efforts, if we run out of memory, suffer a loss of power, or receive a `SIGKILL`, we will be unable to finalize messages that are currently locked. To mitigate this, we set a `lockMs` during message creation (by default this is 1 hour) which limits the maximum amount of time a message can be locked before being available again for dequeue. This facility ensures that no matter the nature of the shutdown, the queue will always recover automatically.
|
|
119
173
|
|
|
120
|
-
## Improvements on
|
|
174
|
+
## Improvements on Polling
|
|
121
175
|
|
|
122
|
-
The simplest approach for processing messages is to call `dequeue` in a loop,
|
|
176
|
+
The simplest approach for processing messages is to call `dequeue` in a loop, backing off with a sleep when no messages are available. The downside with this approach is that we lose reactivity as we increase the polling timeout interval.
|
|
123
177
|
|
|
124
|
-
To improve reactivity,
|
|
178
|
+
To improve reactivity, you can use the `retryMs` returned when failing to dequeue a message. This will either be `null` or tell you how long until the next message is available for processing (a message might be deferred for a period of time). Thus, you can tune your sleep to use the minimum of your `retryMs` and a default poll timeout.
|
|
125
179
|
|
|
126
|
-
Unfortunately, this doesn't help
|
|
180
|
+
Unfortunately, this doesn't help in situations where a message is created/deferred while a worker is sleeping. However, if you deploy LonnyMQ with the `useWake` parameter enabled, message creations and deferrals will trigger a payload to the `queue.wakeChannel()` Postgres channel with the number of milliseconds until said message becomes available for processing encoded as a string payload.
|
|
127
181
|
|
|
128
182
|
```typescript
|
|
129
183
|
const queue = new Queue({ schema: "lonny" })
|
|
130
184
|
const migrations = queue.migrations({ useWake: true })
|
|
131
185
|
|
|
186
|
+
// Run migrations with wake enabled...
|
|
187
|
+
|
|
132
188
|
// LISTEN/NOTIFY only works with a single connection - not on a connection pool.
|
|
133
|
-
const client = await
|
|
134
|
-
await client.query(`LISTEN "${
|
|
189
|
+
const client = await databaseClient.connect()
|
|
190
|
+
await client.query(`LISTEN "${queue.wakeChannel()}"`)
|
|
135
191
|
client.on("notification", (msg) => {
|
|
136
192
|
if (msg.channel === queue.wakeChannel()) {
|
|
137
193
|
const deferMs = parseInt(msg.payload as string, 10)
|
|
138
194
|
console.log(`Should wake in ${deferMs} ms`)
|
|
195
|
+
// Wake up your worker loop here
|
|
139
196
|
}
|
|
140
197
|
})
|
|
141
198
|
```
|
|
142
199
|
|
|
143
200
|
## Deadlocks
|
|
144
201
|
|
|
145
|
-
If all queue actions are isolated to their own transaction, there is
|
|
202
|
+
If all queue actions are isolated to their own transaction, there is zero risk of deadlocks occurring. That being said, it is *possible* to safely bulk-perform the following actions within a single transaction if we ensure they are performed in a consistent lexicographical ordering with respect to channel name and message name (if provided):
|
|
146
203
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
- Message create
|
|
205
|
+
- Channel policy set
|
|
206
|
+
- Channel policy clear
|
|
150
207
|
|
|
151
208
|
Beyond the actions specified above, it is manifestly **unsafe** to bulk-perform any of the remaining actions within a single transaction. Each of these actions should be isolated within their **own** transaction:
|
|
152
209
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
210
|
+
- Message dequeue
|
|
211
|
+
- Message defer
|
|
212
|
+
- Message delete
|
|
213
|
+
|
|
214
|
+
## Batching Operations
|
|
156
215
|
|
|
157
|
-
|
|
216
|
+
When you need to perform multiple safe operations (message creation and channel policy changes) within a single transaction, LonnyMQ provides a batching mechanism that automatically handles proper ordering to prevent deadlocks.
|
|
217
|
+
|
|
218
|
+
The batch interface mirrors the main queue interface - you call `queue.batch()` to create a batch, then use the same `.channel(name).message.create()` and `.channel(name).policy.set/clear()` methods you're already familiar with. The key difference is that batch operations are queued up and don't execute immediately.
|
|
219
|
+
|
|
220
|
+
The batch system ensures that all operations are executed in a consistent lexicographical order based on channel name and message name, eliminating the possibility of deadlocks when multiple workers are performing bulk operations simultaneously.
|
|
158
221
|
|
|
159
222
|
```typescript
|
|
160
223
|
const batch = queue.batch()
|
|
161
|
-
```
|
|
162
224
|
|
|
163
|
-
|
|
225
|
+
batch.channel("user-123").message.create({
|
|
226
|
+
content: Buffer.from("Welcome email")
|
|
227
|
+
})
|
|
164
228
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
229
|
+
batch.channel("user-123").policy.set({
|
|
230
|
+
maxConcurrency: 5,
|
|
231
|
+
maxSize: 1000,
|
|
232
|
+
releaseIntervalMs: 100
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
batch.channel("notifications").message.create({
|
|
236
|
+
content: Buffer.from("Daily digest"),
|
|
237
|
+
name: "daily-digest-2025-08-29"
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
batch.channel("analytics").policy.clear()
|
|
174
241
|
|
|
175
|
-
```typescript
|
|
176
242
|
await batch.execute({ databaseClient })
|
|
243
|
+
```
|
|
177
244
|
|
|
178
|
-
|
|
179
|
-
|
|
245
|
+
## Database Clients
|
|
246
|
+
|
|
247
|
+
LonnyMQ is designed to be database client agnostic, requiring only a minimal interface that most PostgreSQL clients already implement. Your database client must provide a single `query` method with this signature:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface DatabaseClient {
|
|
251
|
+
query(sql: string, params: Array<unknown>): Promise<{
|
|
252
|
+
rows: Array<Record<string, unknown>>
|
|
253
|
+
}>
|
|
254
|
+
}
|
|
180
255
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=Object.prototype.hasOwnProperty;var
|
|
1
|
+
var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=Object.prototype.hasOwnProperty;var y=new WeakMap,J=(e)=>{var n=y.get(e),s;if(n)return n;if(n=d({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")V(e).map((_)=>!K.call(n,_)&&d(n,_,{get:()=>e[_],enumerable:!(s=X(e,_))||s.enumerable}));return y.set(e,n),n};var j=(e,n)=>{for(var s in n)d(e,s,{get:n[s],enumerable:!0,configurable:!0,set:(_)=>n[s]=()=>_})};var re={};j(re,{Queue:()=>x,MessageDequeueCommand:()=>i,MessageDeleteCommand:()=>l,MessageDeferCommand:()=>h,MessageCreateCommand:()=>o,ChannelPolicySetCommand:()=>m,ChannelPolicyClearCommand:()=>r});module.exports=J(re);var E=(e)=>({nodeType:"VALUE",value:e}),t=(e)=>({nodeType:"REF",value:e}),Z=(e)=>({nodeType:"RAW",value:e}),ee=(e)=>{return`'${e.replace(/'/g,"''")}'`},ne=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return ee(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},te=(e)=>{return`"${e.replace(/"/g,'""')}"`},se=(e)=>{if(e.nodeType==="VALUE")return ne(e.value);else if(e.nodeType==="REF")return te(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var a=(e,...n)=>{let s=[];for(let _=0;_<e.length;_+=1)if(s.push(e[_]),_<n.length)s.push(se(n[_]));return Z(s.join(""))};class r{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
|
|
2
2
|
SELECT 1 FROM ${t(this.schema)}."channel_policy_clear"(
|
|
3
3
|
$1
|
|
4
4
|
)
|
|
5
|
-
`.value,[this.channelName])}}class m{schema;channelName;maxSize;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let n=e.maxConcurrency??null;this.maxConcurrency=n!==null?Math.max(0,n):null;let
|
|
5
|
+
`.value,[this.channelName])}}class m{schema;channelName;maxSize;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let n=e.maxConcurrency??null;this.maxConcurrency=n!==null?Math.max(0,n):null;let s=e.maxSize??null;this.maxSize=s!==null?Math.max(0,s):null;let _=e.releaseIntervalMs??null;this.releaseIntervalMs=_!==null?Math.max(0,_):null,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(a`
|
|
6
6
|
SELECT 1 FROM ${t(this.schema)}."channel_policy_set"(
|
|
7
7
|
$1,
|
|
8
8
|
$2::INTEGER,
|
|
9
9
|
$3::INTEGER,
|
|
10
10
|
$4::INTEGER
|
|
11
11
|
)
|
|
12
|
-
`.value,[this.channelName,this.maxSize,this.maxConcurrency,this.releaseIntervalMs])}}var R=(e)=>{return e*1000},
|
|
12
|
+
`.value,[this.channelName,this.maxSize,this.maxConcurrency,this.releaseIntervalMs])}}var R=(e)=>{return e*1000},ae=(e)=>{return R(e*60)},f=(e)=>{return ae(e*60)};var C=require("node:crypto");class p{value;constructor(e){this.value=e}toString(e){return C.createHash("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var u=new p("WAKE"),q=!1,N=R(0),$=f(1);var g=require("path"),__filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",_e=g.dirname(g.dirname(__filename)),ce=new RegExp(`^${_e}/`),c=(e)=>{return e.replace(ce,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var F={name:c(__filename),sql:(e)=>{return[a`
|
|
13
13
|
CREATE FUNCTION ${t(e.schema)}."message_create" (
|
|
14
14
|
p_id UUID,
|
|
15
15
|
p_channel_name TEXT,
|
|
@@ -65,7 +65,8 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
65
65
|
"max_size",
|
|
66
66
|
"max_concurrency",
|
|
67
67
|
"message_next_id",
|
|
68
|
-
"message_next_dequeue_after"
|
|
68
|
+
"message_next_dequeue_after",
|
|
69
|
+
"message_next_seq_no"
|
|
69
70
|
INTO v_channel_state;
|
|
70
71
|
|
|
71
72
|
IF v_channel_state."current_size" >= v_channel_policy."max_size" THEN
|
|
@@ -95,6 +96,7 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
95
96
|
"name" = EXCLUDED."name"
|
|
96
97
|
RETURNING
|
|
97
98
|
"id",
|
|
99
|
+
"seq_no",
|
|
98
100
|
"dequeue_after"
|
|
99
101
|
INTO v_message;
|
|
100
102
|
|
|
@@ -106,12 +108,14 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
106
108
|
|
|
107
109
|
IF
|
|
108
110
|
v_channel_state."message_next_id" IS NULL OR
|
|
109
|
-
v_channel_state."message_next_dequeue_after" > v_message."dequeue_after"
|
|
111
|
+
v_channel_state."message_next_dequeue_after" > v_message."dequeue_after" OR
|
|
112
|
+
(v_channel_state."message_next_dequeue_after" = v_message."dequeue_after" AND v_channel_state."message_next_seq_no" > v_message."seq_no")
|
|
110
113
|
THEN
|
|
111
114
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
112
115
|
"current_size" = v_channel_state."current_size" + 1,
|
|
113
116
|
"message_next_id" = v_message."id",
|
|
114
|
-
"message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after")
|
|
117
|
+
"message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after"),
|
|
118
|
+
"message_next_seq_no" = v_message."seq_no"
|
|
115
119
|
WHERE "id" = v_channel_state."id";
|
|
116
120
|
ELSE
|
|
117
121
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
@@ -126,7 +130,7 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
126
130
|
RETURN;
|
|
127
131
|
END;
|
|
128
132
|
$$ LANGUAGE plpgsql;
|
|
129
|
-
`]}};var M=require("node:crypto");class
|
|
133
|
+
`]}};var M=require("node:crypto");class o{schema;channelName;name;content;lockMs;id;delayMs;createdAt;constructor(e){let n=e.name??null,s=e.lockMs===void 0?$:Math.max(0,e.lockMs),_=e.delayMs===void 0?N:e.delayMs;this.id=M.randomUUID(),this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=n,this.lockMs=s,this.delayMs=_,this.createdAt=new Date}async execute(e){let n=await e.query(a`
|
|
130
134
|
SELECT * FROM ${t(this.schema)}."message_create"(
|
|
131
135
|
$1,
|
|
132
136
|
$2,
|
|
@@ -135,7 +139,7 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
135
139
|
$5::INTEGER,
|
|
136
140
|
$6::INTEGER
|
|
137
141
|
)
|
|
138
|
-
`.value,[this.id,this.channelName,this.name,this.content,this.lockMs,this.delayMs]).then((
|
|
142
|
+
`.value,[this.id,this.channelName,this.name,this.content,this.lockMs,this.delayMs]).then((s)=>s.rows[0]);if(n.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(n.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED"};else if(n.result_code===0)return{resultType:"MESSAGE_CREATED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var G={name:c(__filename),sql:(e)=>{return[a`
|
|
139
143
|
CREATE FUNCTION ${t(e.schema)}."message_dequeue" ()
|
|
140
144
|
RETURNS TABLE (
|
|
141
145
|
result_code INTEGER,
|
|
@@ -246,11 +250,12 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
246
250
|
|
|
247
251
|
SELECT
|
|
248
252
|
"message"."id",
|
|
249
|
-
"message"."dequeue_after"
|
|
253
|
+
"message"."dequeue_after",
|
|
254
|
+
"message"."seq_no"
|
|
250
255
|
FROM ${t(e.schema)}."message"
|
|
251
256
|
WHERE NOT "is_locked"
|
|
252
257
|
AND "channel_name" = v_message_dequeue."channel_name"
|
|
253
|
-
ORDER BY "dequeue_after" ASC, "
|
|
258
|
+
ORDER BY "dequeue_after" ASC, "seq_no" ASC
|
|
254
259
|
LIMIT 1
|
|
255
260
|
INTO v_message_next_dequeue;
|
|
256
261
|
|
|
@@ -263,7 +268,8 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
263
268
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
264
269
|
"current_concurrency" = v_channel_state."current_concurrency" + 1,
|
|
265
270
|
"message_next_id" = v_message_next_dequeue."id",
|
|
266
|
-
"message_next_dequeue_after" = v_message_next_dequeue_after
|
|
271
|
+
"message_next_dequeue_after" = v_message_next_dequeue_after,
|
|
272
|
+
"message_next_seq_no" = v_message_next_dequeue."seq_no"
|
|
267
273
|
WHERE "id" = v_channel_state."id";
|
|
268
274
|
ELSE
|
|
269
275
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
@@ -286,14 +292,14 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
286
292
|
RETURN;
|
|
287
293
|
END;
|
|
288
294
|
$$ LANGUAGE plpgsql;
|
|
289
|
-
`]}};class
|
|
295
|
+
`]}};class i{schema;constructor(e){this.schema=e.schema}async execute(e){let n=await e.query(a`
|
|
290
296
|
SELECT
|
|
291
297
|
result_code,
|
|
292
298
|
metadata,
|
|
293
299
|
content,
|
|
294
300
|
state
|
|
295
301
|
FROM ${t(this.schema)}."message_dequeue"()
|
|
296
|
-
`.value,[]).then((
|
|
302
|
+
`.value,[]).then((s)=>s.rows[0]);if(n.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:n.metadata.retry_ms};else if(n.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:n.metadata.id,channelName:n.metadata.channel_name,name:n.metadata.name,content:n.content,state:n.state,numAttempts:n.metadata.num_attempts}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var B={name:c(__filename),sql:(e)=>{return[a`
|
|
297
303
|
CREATE FUNCTION ${t(e.schema)}."message_delete" (
|
|
298
304
|
p_id UUID
|
|
299
305
|
)
|
|
@@ -358,11 +364,11 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
358
364
|
RETURN;
|
|
359
365
|
END;
|
|
360
366
|
$$ LANGUAGE plpgsql;
|
|
361
|
-
`]}};class l{schema;id;constructor(e){this.schema=e.schema,this.id=e.id}async execute(e){let n=await e.query(
|
|
367
|
+
`]}};class l{schema;id;constructor(e){this.schema=e.schema,this.id=e.id}async execute(e){let n=await e.query(a`
|
|
362
368
|
SELECT * FROM ${t(this.schema)}."message_delete"(
|
|
363
369
|
$1
|
|
364
370
|
)
|
|
365
|
-
`.value,[this.id]).then((
|
|
371
|
+
`.value,[this.id]).then((s)=>s.rows[0]);if(n.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(n.result_code===1)return{resultType:"STATE_INVALID"};else if(n.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var P={name:c(__filename),sql:(e)=>{return[a`
|
|
366
372
|
CREATE FUNCTION ${t(e.schema)}."message_defer" (
|
|
367
373
|
p_id UUID,
|
|
368
374
|
p_delay_ms INTEGER,
|
|
@@ -379,7 +385,8 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
379
385
|
SELECT
|
|
380
386
|
"message"."id",
|
|
381
387
|
"message"."channel_name",
|
|
382
|
-
"message"."is_locked"
|
|
388
|
+
"message"."is_locked",
|
|
389
|
+
"message"."seq_no"
|
|
383
390
|
FROM ${t(e.schema)}."message"
|
|
384
391
|
WHERE "id" = p_id
|
|
385
392
|
FOR UPDATE
|
|
@@ -398,7 +405,8 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
398
405
|
SELECT
|
|
399
406
|
"channel_state"."current_concurrency",
|
|
400
407
|
"channel_state"."message_next_id",
|
|
401
|
-
"channel_state"."message_next_dequeue_after"
|
|
408
|
+
"channel_state"."message_next_dequeue_after",
|
|
409
|
+
"channel_state"."message_next_seq_no"
|
|
402
410
|
FROM ${t(e.schema)}."channel_state"
|
|
403
411
|
WHERE "name" = v_message."channel_name"
|
|
404
412
|
FOR UPDATE
|
|
@@ -408,12 +416,14 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
408
416
|
|
|
409
417
|
IF
|
|
410
418
|
v_channel_state."message_next_id" IS NULL OR
|
|
411
|
-
v_channel_state."message_next_dequeue_after" > v_dequeue_after
|
|
419
|
+
v_channel_state."message_next_dequeue_after" > v_dequeue_after OR
|
|
420
|
+
(v_channel_state."message_next_dequeue_after" = v_dequeue_after AND v_channel_state."message_next_seq_no" > v_message."seq_no")
|
|
412
421
|
THEN
|
|
413
422
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
414
423
|
"current_concurrency" = v_channel_state."current_concurrency" - 1,
|
|
415
424
|
"message_next_id" = v_message."id",
|
|
416
|
-
"message_next_dequeue_after" = v_dequeue_after
|
|
425
|
+
"message_next_dequeue_after" = v_dequeue_after,
|
|
426
|
+
"message_next_seq_no" = v_message."seq_no"
|
|
417
427
|
WHERE "name" = v_message."channel_name";
|
|
418
428
|
ELSE
|
|
419
429
|
UPDATE ${t(e.schema)}."channel_state" SET
|
|
@@ -434,15 +444,15 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
434
444
|
RETURN;
|
|
435
445
|
END;
|
|
436
446
|
$$ LANGUAGE plpgsql;
|
|
437
|
-
`]}};class h{schema;id;delayMs;state;constructor(e){let n=e.delayMs===void 0?N:e.delayMs;this.schema=e.schema,this.id=e.id,this.delayMs=n,this.state=e.state??null}async execute(e){let n=await e.query(
|
|
447
|
+
`]}};class h{schema;id;delayMs;state;constructor(e){let n=e.delayMs===void 0?N:e.delayMs;this.schema=e.schema,this.id=e.id,this.delayMs=n,this.state=e.state??null}async execute(e){let n=await e.query(a`
|
|
438
448
|
SELECT * FROM ${t(this.schema)}."message_defer"(
|
|
439
449
|
$1,
|
|
440
450
|
$2,
|
|
441
451
|
$3
|
|
442
452
|
)
|
|
443
|
-
`.value,[this.id,this.delayMs,this.state]).then((
|
|
444
|
-
`),
|
|
445
|
-
`).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",k={name:c(__filename),sql:(e)=>{return[
|
|
453
|
+
`.value,[this.id,this.delayMs,this.state]).then((s)=>s.rows[0]);if(n.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(n.result_code===1)return{resultType:"STATE_INVALID"};else if(n.result_code===2)return{resultType:"MESSAGE_DEFERRED"};else throw new Error("Unexpected result")}}var H=(e)=>{let n=e.split(`
|
|
454
|
+
`),s=Number.MAX_SAFE_INTEGER;for(let _ of n){if(_.trim().length===0)continue;let T=_.search(/\S/);s=Math.min(s,T)}return n.map((_)=>_.slice(s)).join(`
|
|
455
|
+
`).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",k={name:c(__filename),sql:(e)=>{return[a`
|
|
446
456
|
CREATE TABLE ${t(e.schema)}."channel_policy" (
|
|
447
457
|
"id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
|
|
448
458
|
"name" TEXT NOT NULL,
|
|
@@ -451,10 +461,10 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
451
461
|
"release_interval_ms" INTEGER,
|
|
452
462
|
PRIMARY KEY ("id")
|
|
453
463
|
);
|
|
454
|
-
`,
|
|
464
|
+
`,a`
|
|
455
465
|
CREATE UNIQUE INDEX "channel_policy_name_ux"
|
|
456
466
|
ON ${t(e.schema)}."channel_policy" ("name");
|
|
457
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",b={name:c(__filename),sql:(e)=>{return[
|
|
467
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",b={name:c(__filename),sql:(e)=>{return[a`
|
|
458
468
|
CREATE TABLE ${t(e.schema)}."channel_state" (
|
|
459
469
|
"id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
|
|
460
470
|
"name" TEXT NOT NULL,
|
|
@@ -465,21 +475,23 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
465
475
|
"current_concurrency" INTEGER NOT NULL,
|
|
466
476
|
"message_next_id" UUID,
|
|
467
477
|
"message_next_dequeue_after" TIMESTAMP,
|
|
478
|
+
"message_next_seq_no" BIGINT,
|
|
468
479
|
PRIMARY KEY ("id")
|
|
469
480
|
);
|
|
470
|
-
`,
|
|
481
|
+
`,a`
|
|
471
482
|
CREATE UNIQUE INDEX "channel_state_name_ux"
|
|
472
483
|
ON ${t(e.schema)}."channel_state" ("name");
|
|
473
|
-
`,
|
|
484
|
+
`,a`
|
|
474
485
|
CREATE INDEX "channel_state_dequeue_ix"
|
|
475
486
|
ON ${t(e.schema)}."channel_state" (
|
|
476
487
|
"message_next_dequeue_after" ASC
|
|
477
488
|
) WHERE "message_next_id" IS NOT NULL
|
|
478
489
|
AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
|
|
479
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",z={name:c(__filename),sql:(e)=>{return[
|
|
490
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",z={name:c(__filename),sql:(e)=>{return[a`
|
|
480
491
|
CREATE TABLE ${t(e.schema)}."message" (
|
|
481
492
|
"id" UUID NOT NULL,
|
|
482
493
|
"channel_name" TEXT NOT NULL,
|
|
494
|
+
"seq_no" BIGSERIAL NOT NULL,
|
|
483
495
|
"name" TEXT,
|
|
484
496
|
"content" BYTEA NOT NULL,
|
|
485
497
|
"state" BYTEA,
|
|
@@ -489,25 +501,25 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
489
501
|
"dequeue_after" TIMESTAMP NOT NULL,
|
|
490
502
|
PRIMARY KEY ("id")
|
|
491
503
|
);
|
|
492
|
-
`,
|
|
504
|
+
`,a`
|
|
493
505
|
CREATE UNIQUE INDEX "message_name_ux"
|
|
494
506
|
ON ${t(e.schema)}."message" (
|
|
495
507
|
"channel_name",
|
|
496
508
|
"name"
|
|
497
509
|
) WHERE "num_attempts" = 0
|
|
498
|
-
`,
|
|
510
|
+
`,a`
|
|
499
511
|
CREATE INDEX "message_dequeue_ix"
|
|
500
512
|
ON ${t(e.schema)}."message" (
|
|
501
513
|
"channel_name",
|
|
502
514
|
"dequeue_after" ASC,
|
|
503
|
-
"
|
|
515
|
+
"seq_no" ASC
|
|
504
516
|
) WHERE NOT "is_locked";
|
|
505
|
-
`,
|
|
517
|
+
`,a`
|
|
506
518
|
CREATE INDEX "message_locked_dequeue_ix"
|
|
507
519
|
ON ${t(e.schema)}."message" (
|
|
508
520
|
"dequeue_after" ASC
|
|
509
521
|
) WHERE "is_locked";
|
|
510
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",w={name:c(__filename),sql:(e)=>{let n=u.toString(e.schema);return[
|
|
522
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",w={name:c(__filename),sql:(e)=>{let n=u.toString(e.schema);return[a`
|
|
511
523
|
CREATE FUNCTION ${t(e.schema)}."wake" (
|
|
512
524
|
p_delay_ms INTEGER
|
|
513
525
|
) RETURNS VOID AS $$
|
|
@@ -517,7 +529,7 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
517
529
|
END IF;
|
|
518
530
|
END;
|
|
519
531
|
$$ LANGUAGE plpgsql;
|
|
520
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",W={name:c(__filename),sql:(e)=>{return[
|
|
532
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",W={name:c(__filename),sql:(e)=>{return[a`
|
|
521
533
|
CREATE FUNCTION ${t(e.schema)}."channel_policy_set" (
|
|
522
534
|
p_name TEXT,
|
|
523
535
|
p_max_size INTEGER,
|
|
@@ -549,7 +561,7 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
549
561
|
PERFORM ${t(e.schema)}."wake"(0);
|
|
550
562
|
END;
|
|
551
563
|
$$ LANGUAGE plpgsql;
|
|
552
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",Y={name:c(__filename),sql:(e)=>{return[
|
|
564
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",Y={name:c(__filename),sql:(e)=>{return[a`
|
|
553
565
|
CREATE FUNCTION ${t(e.schema)}."channel_policy_clear" (
|
|
554
566
|
p_name TEXT
|
|
555
567
|
) RETURNS VOID AS $$
|
|
@@ -581,4 +593,4 @@ var{defineProperty:d,getOwnPropertyNames:V,getOwnPropertyDescriptor:X}=Object,K=
|
|
|
581
593
|
PERFORM ${t(e.schema)}."wake"(0);
|
|
582
594
|
END;
|
|
583
595
|
$$ LANGUAGE plpgsql;
|
|
584
|
-
`]}};class S{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let n=new
|
|
596
|
+
`]}};class S{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let n=new o({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}),s=new Promise((_)=>{this.registerFn({sortKey:JSON.stringify([n.channelName,n.name,n.createdAt.toISOString()]),execute:(T)=>n.execute(T).then((Q)=>_(Q))})});return{messageId:n.id,promise:s}}}class A{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let n=new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs});this.registerFn({sortKey:JSON.stringify([n.channelName,null,n.createdAt.toISOString()]),execute:async(s)=>{await n.execute(s)}})}clear(){let e=new r({schema:this.schema,channelName:this.channelName});this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:async(n)=>{await e.execute(n)}})}}class I{policy;message;constructor(e){this.message=new S(e),this.policy=new A(e)}}var Ee=(e,n)=>{return e.sortKey.localeCompare(n.sortKey)};class L{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new I({schema:this.schema,channelName:e,registerFn:(n)=>{this.commands.push(n)}})}async execute(e){for(let n of this.commands.sort(Ee))await n.execute(e.databaseClient)}}class v{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new o({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}).execute(e.databaseClient)}}class U{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}).execute(e.databaseClient)}clear(e){return new r({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class O{policy;message;constructor(e){this.message=new v({schema:e.schema,channelName:e.channelName}),this.policy=new U({schema:e.schema,channelName:e.channelName})}}class D{schema;id;channelName;name;content;state;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){return new h({schema:this.schema,id:this.id,delayMs:e.delayMs,state:e.state}).execute(e.databaseClient)}async delete(e){return new l({schema:this.schema,id:this.id}).execute(e.databaseClient)}}class x{schema;constructor(e){this.schema=e.schema}async dequeue(e){let s=await new i({schema:this.schema}).execute(e.databaseClient);if(s.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new D({schema:this.schema,id:s.message.id,channelName:s.message.channelName,name:s.message.name,content:s.message.content,state:s.message.state,numAttempts:s.message.numAttempts})};else return s}channel(e){return new O({schema:this.schema,channelName:e})}batch(){return new L({schema:this.schema})}migrations(e){return[k,b,z,w,F,G,B,P,W,Y].map((n)=>({name:n.name,sql:n.sql({schema:this.schema,useWake:e.useWake??q}).map((s)=>H(s.value))})).sort((n,s)=>n.name.localeCompare(s.name))}wakeChannel(){return u.toString(this.schema)}}
|
package/dist/index.d.ts
CHANGED
|
@@ -268,7 +268,9 @@ export type MessageDequeueResult = {
|
|
|
268
268
|
};
|
|
269
269
|
export declare class Queue {
|
|
270
270
|
private readonly schema;
|
|
271
|
-
constructor(
|
|
271
|
+
constructor(params: {
|
|
272
|
+
schema: string;
|
|
273
|
+
});
|
|
272
274
|
dequeue(params: {
|
|
273
275
|
databaseClient: DatabaseClient;
|
|
274
276
|
}): Promise<MessageDequeueResult>;
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=>({nodeType:"RAW",value:e}),W=(e)=>{return`'${e.replace(/'/g,"''")}'`},Y=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return W(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},Q=(e)=>{return`"${e.replace(/"/g,'""')}"`},V=(e)=>{if(e.nodeType==="VALUE")return Y(e.value);else if(e.nodeType==="REF")return Q(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var
|
|
1
|
+
var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=>({nodeType:"RAW",value:e}),W=(e)=>{return`'${e.replace(/'/g,"''")}'`},Y=(e)=>{if(e===null)return"NULL";else if(typeof e==="string")return W(e);else if(typeof e==="number")return e.toString();else if(typeof e==="boolean")return e?"TRUE":"FALSE";else if(e instanceof Date)return`'${e.toISOString()}'`;else if(typeof e==="bigint")return e.toString();else throw new Error(`Unsupported value type: ${typeof e}`)},Q=(e)=>{return`"${e.replace(/"/g,'""')}"`},V=(e)=>{if(e.nodeType==="VALUE")return Y(e.value);else if(e.nodeType==="REF")return Q(e.value);else if(e.nodeType==="RAW")return e.value;else throw new Error("Unsupported SQL node type")};var s=(e,...t)=>{let a=[];for(let E=0;E<e.length;E+=1)if(a.push(e[E]),E<t.length)a.push(V(t[E]));return w(a.join(""))};class r{schema;channelName;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(s`
|
|
2
2
|
SELECT 1 FROM ${n(this.schema)}."channel_policy_clear"(
|
|
3
3
|
$1
|
|
4
4
|
)
|
|
5
|
-
`.value,[this.channelName])}}class m{schema;channelName;maxSize;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let t=e.maxConcurrency??null;this.maxConcurrency=t!==null?Math.max(0,t):null;let
|
|
5
|
+
`.value,[this.channelName])}}class m{schema;channelName;maxSize;maxConcurrency;releaseIntervalMs;createdAt;constructor(e){this.schema=e.schema,this.channelName=e.channelName;let t=e.maxConcurrency??null;this.maxConcurrency=t!==null?Math.max(0,t):null;let a=e.maxSize??null;this.maxSize=a!==null?Math.max(0,a):null;let E=e.releaseIntervalMs??null;this.releaseIntervalMs=E!==null?Math.max(0,E):null,this.createdAt=new Date}sortKeyGet(){return JSON.stringify([this.channelName,null,this.createdAt.toISOString()])}async execute(e){await e.query(s`
|
|
6
6
|
SELECT 1 FROM ${n(this.schema)}."channel_policy_set"(
|
|
7
7
|
$1,
|
|
8
8
|
$2::INTEGER,
|
|
9
9
|
$3::INTEGER,
|
|
10
10
|
$4::INTEGER
|
|
11
11
|
)
|
|
12
|
-
`.value,[this.channelName,this.maxSize,this.maxConcurrency,this.releaseIntervalMs])}}var d=(e)=>{return e*1000},X=(e)=>{return d(e*60)},O=(e)=>{return X(e*60)};import{createHash as K}from"node:crypto";class
|
|
12
|
+
`.value,[this.channelName,this.maxSize,this.maxConcurrency,this.releaseIntervalMs])}}var d=(e)=>{return e*1000},X=(e)=>{return d(e*60)},O=(e)=>{return X(e*60)};import{createHash as K}from"node:crypto";class D{value;constructor(e){this.value=e}toString(e){return K("sha256").update(e).update(this.value).digest("base64").replace(/=/g,"")}}var i=new D("WAKE"),x=!1,l=d(0),y=O(1);import{dirname as f}from"path";var __filename="/home/runner/work/lonnymq/lonnymq/src/core/path.ts",J=f(f(__filename)),j=new RegExp(`^${J}/`),_=(e)=>{return e.replace(j,"")};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/04-function-message-create.ts";var C={name:_(__filename),sql:(e)=>{return[s`
|
|
13
13
|
CREATE FUNCTION ${n(e.schema)}."message_create" (
|
|
14
14
|
p_id UUID,
|
|
15
15
|
p_channel_name TEXT,
|
|
@@ -65,7 +65,8 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
65
65
|
"max_size",
|
|
66
66
|
"max_concurrency",
|
|
67
67
|
"message_next_id",
|
|
68
|
-
"message_next_dequeue_after"
|
|
68
|
+
"message_next_dequeue_after",
|
|
69
|
+
"message_next_seq_no"
|
|
69
70
|
INTO v_channel_state;
|
|
70
71
|
|
|
71
72
|
IF v_channel_state."current_size" >= v_channel_policy."max_size" THEN
|
|
@@ -95,6 +96,7 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
95
96
|
"name" = EXCLUDED."name"
|
|
96
97
|
RETURNING
|
|
97
98
|
"id",
|
|
99
|
+
"seq_no",
|
|
98
100
|
"dequeue_after"
|
|
99
101
|
INTO v_message;
|
|
100
102
|
|
|
@@ -106,12 +108,14 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
106
108
|
|
|
107
109
|
IF
|
|
108
110
|
v_channel_state."message_next_id" IS NULL OR
|
|
109
|
-
v_channel_state."message_next_dequeue_after" > v_message."dequeue_after"
|
|
111
|
+
v_channel_state."message_next_dequeue_after" > v_message."dequeue_after" OR
|
|
112
|
+
(v_channel_state."message_next_dequeue_after" = v_message."dequeue_after" AND v_channel_state."message_next_seq_no" > v_message."seq_no")
|
|
110
113
|
THEN
|
|
111
114
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
112
115
|
"current_size" = v_channel_state."current_size" + 1,
|
|
113
116
|
"message_next_id" = v_message."id",
|
|
114
|
-
"message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after")
|
|
117
|
+
"message_next_dequeue_after" = GREATEST(v_now, v_message."dequeue_after"),
|
|
118
|
+
"message_next_seq_no" = v_message."seq_no"
|
|
115
119
|
WHERE "id" = v_channel_state."id";
|
|
116
120
|
ELSE
|
|
117
121
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
@@ -126,7 +130,7 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
126
130
|
RETURN;
|
|
127
131
|
END;
|
|
128
132
|
$$ LANGUAGE plpgsql;
|
|
129
|
-
`]}};import{randomUUID as Z}from"node:crypto";class
|
|
133
|
+
`]}};import{randomUUID as Z}from"node:crypto";class o{schema;channelName;name;content;lockMs;id;delayMs;createdAt;constructor(e){let t=e.name??null,a=e.lockMs===void 0?y:Math.max(0,e.lockMs),E=e.delayMs===void 0?l:e.delayMs;this.id=Z(),this.schema=e.schema,this.channelName=e.channelName,this.content=e.content,this.name=t,this.lockMs=a,this.delayMs=E,this.createdAt=new Date}async execute(e){let t=await e.query(s`
|
|
130
134
|
SELECT * FROM ${n(this.schema)}."message_create"(
|
|
131
135
|
$1,
|
|
132
136
|
$2,
|
|
@@ -135,7 +139,7 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
135
139
|
$5::INTEGER,
|
|
136
140
|
$6::INTEGER
|
|
137
141
|
)
|
|
138
|
-
`.value,[this.id,this.channelName,this.name,this.content,this.lockMs,this.delayMs]).then((
|
|
142
|
+
`.value,[this.id,this.channelName,this.name,this.content,this.lockMs,this.delayMs]).then((a)=>a.rows[0]);if(t.result_code===1)return{resultType:"MESSAGE_DROPPED"};else if(t.result_code===2)return{resultType:"MESSAGE_DEDUPLICATED"};else if(t.result_code===0)return{resultType:"MESSAGE_CREATED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/05-function-message-dequeue.ts";var p={name:_(__filename),sql:(e)=>{return[s`
|
|
139
143
|
CREATE FUNCTION ${n(e.schema)}."message_dequeue" ()
|
|
140
144
|
RETURNS TABLE (
|
|
141
145
|
result_code INTEGER,
|
|
@@ -246,11 +250,12 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
246
250
|
|
|
247
251
|
SELECT
|
|
248
252
|
"message"."id",
|
|
249
|
-
"message"."dequeue_after"
|
|
253
|
+
"message"."dequeue_after",
|
|
254
|
+
"message"."seq_no"
|
|
250
255
|
FROM ${n(e.schema)}."message"
|
|
251
256
|
WHERE NOT "is_locked"
|
|
252
257
|
AND "channel_name" = v_message_dequeue."channel_name"
|
|
253
|
-
ORDER BY "dequeue_after" ASC, "
|
|
258
|
+
ORDER BY "dequeue_after" ASC, "seq_no" ASC
|
|
254
259
|
LIMIT 1
|
|
255
260
|
INTO v_message_next_dequeue;
|
|
256
261
|
|
|
@@ -263,7 +268,8 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
263
268
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
264
269
|
"current_concurrency" = v_channel_state."current_concurrency" + 1,
|
|
265
270
|
"message_next_id" = v_message_next_dequeue."id",
|
|
266
|
-
"message_next_dequeue_after" = v_message_next_dequeue_after
|
|
271
|
+
"message_next_dequeue_after" = v_message_next_dequeue_after,
|
|
272
|
+
"message_next_seq_no" = v_message_next_dequeue."seq_no"
|
|
267
273
|
WHERE "id" = v_channel_state."id";
|
|
268
274
|
ELSE
|
|
269
275
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
@@ -286,14 +292,14 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
286
292
|
RETURN;
|
|
287
293
|
END;
|
|
288
294
|
$$ LANGUAGE plpgsql;
|
|
289
|
-
`]}};class h{schema;constructor(e){this.schema=e.schema}async execute(e){let t=await e.query(
|
|
295
|
+
`]}};class h{schema;constructor(e){this.schema=e.schema}async execute(e){let t=await e.query(s`
|
|
290
296
|
SELECT
|
|
291
297
|
result_code,
|
|
292
298
|
metadata,
|
|
293
299
|
content,
|
|
294
300
|
state
|
|
295
301
|
FROM ${n(this.schema)}."message_dequeue"()
|
|
296
|
-
`.value,[]).then((
|
|
302
|
+
`.value,[]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_AVAILABLE",retryMs:t.metadata.retry_ms};else if(t.result_code===1)return{resultType:"MESSAGE_DEQUEUED",message:{id:t.metadata.id,channelName:t.metadata.channel_name,name:t.metadata.name,content:t.content,state:t.state,numAttempts:t.metadata.num_attempts}};else throw new Error("Unexpected dequeue result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/06-function-message-delete.ts";var q={name:_(__filename),sql:(e)=>{return[s`
|
|
297
303
|
CREATE FUNCTION ${n(e.schema)}."message_delete" (
|
|
298
304
|
p_id UUID
|
|
299
305
|
)
|
|
@@ -358,11 +364,11 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
358
364
|
RETURN;
|
|
359
365
|
END;
|
|
360
366
|
$$ LANGUAGE plpgsql;
|
|
361
|
-
`]}};class u{schema;id;constructor(e){this.schema=e.schema,this.id=e.id}async execute(e){let t=await e.query(
|
|
367
|
+
`]}};class u{schema;id;constructor(e){this.schema=e.schema,this.id=e.id}async execute(e){let t=await e.query(s`
|
|
362
368
|
SELECT * FROM ${n(this.schema)}."message_delete"(
|
|
363
369
|
$1
|
|
364
370
|
)
|
|
365
|
-
`.value,[this.id]).then((
|
|
371
|
+
`.value,[this.id]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DELETED"};else throw new Error("Unexpected result")}}var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/07-function-message-defer.ts";var $={name:_(__filename),sql:(e)=>{return[s`
|
|
366
372
|
CREATE FUNCTION ${n(e.schema)}."message_defer" (
|
|
367
373
|
p_id UUID,
|
|
368
374
|
p_delay_ms INTEGER,
|
|
@@ -379,7 +385,8 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
379
385
|
SELECT
|
|
380
386
|
"message"."id",
|
|
381
387
|
"message"."channel_name",
|
|
382
|
-
"message"."is_locked"
|
|
388
|
+
"message"."is_locked",
|
|
389
|
+
"message"."seq_no"
|
|
383
390
|
FROM ${n(e.schema)}."message"
|
|
384
391
|
WHERE "id" = p_id
|
|
385
392
|
FOR UPDATE
|
|
@@ -398,7 +405,8 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
398
405
|
SELECT
|
|
399
406
|
"channel_state"."current_concurrency",
|
|
400
407
|
"channel_state"."message_next_id",
|
|
401
|
-
"channel_state"."message_next_dequeue_after"
|
|
408
|
+
"channel_state"."message_next_dequeue_after",
|
|
409
|
+
"channel_state"."message_next_seq_no"
|
|
402
410
|
FROM ${n(e.schema)}."channel_state"
|
|
403
411
|
WHERE "name" = v_message."channel_name"
|
|
404
412
|
FOR UPDATE
|
|
@@ -408,12 +416,14 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
408
416
|
|
|
409
417
|
IF
|
|
410
418
|
v_channel_state."message_next_id" IS NULL OR
|
|
411
|
-
v_channel_state."message_next_dequeue_after" > v_dequeue_after
|
|
419
|
+
v_channel_state."message_next_dequeue_after" > v_dequeue_after OR
|
|
420
|
+
(v_channel_state."message_next_dequeue_after" = v_dequeue_after AND v_channel_state."message_next_seq_no" > v_message."seq_no")
|
|
412
421
|
THEN
|
|
413
422
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
414
423
|
"current_concurrency" = v_channel_state."current_concurrency" - 1,
|
|
415
424
|
"message_next_id" = v_message."id",
|
|
416
|
-
"message_next_dequeue_after" = v_dequeue_after
|
|
425
|
+
"message_next_dequeue_after" = v_dequeue_after,
|
|
426
|
+
"message_next_seq_no" = v_message."seq_no"
|
|
417
427
|
WHERE "name" = v_message."channel_name";
|
|
418
428
|
ELSE
|
|
419
429
|
UPDATE ${n(e.schema)}."channel_state" SET
|
|
@@ -434,15 +444,15 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
434
444
|
RETURN;
|
|
435
445
|
END;
|
|
436
446
|
$$ LANGUAGE plpgsql;
|
|
437
|
-
`]}};class N{schema;id;delayMs;state;constructor(e){let t=e.delayMs===void 0?l:e.delayMs;this.schema=e.schema,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(
|
|
447
|
+
`]}};class N{schema;id;delayMs;state;constructor(e){let t=e.delayMs===void 0?l:e.delayMs;this.schema=e.schema,this.id=e.id,this.delayMs=t,this.state=e.state??null}async execute(e){let t=await e.query(s`
|
|
438
448
|
SELECT * FROM ${n(this.schema)}."message_defer"(
|
|
439
449
|
$1,
|
|
440
450
|
$2,
|
|
441
451
|
$3
|
|
442
452
|
)
|
|
443
|
-
`.value,[this.id,this.delayMs,this.state]).then((
|
|
444
|
-
`),
|
|
445
|
-
`).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",M={name:_(__filename),sql:(e)=>{return[
|
|
453
|
+
`.value,[this.id,this.delayMs,this.state]).then((a)=>a.rows[0]);if(t.result_code===0)return{resultType:"MESSAGE_NOT_FOUND"};else if(t.result_code===1)return{resultType:"STATE_INVALID"};else if(t.result_code===2)return{resultType:"MESSAGE_DEFERRED"};else throw new Error("Unexpected result")}}var F=(e)=>{let t=e.split(`
|
|
454
|
+
`),a=Number.MAX_SAFE_INTEGER;for(let E of t){if(E.trim().length===0)continue;let T=E.search(/\S/);a=Math.min(a,T)}return t.map((E)=>E.slice(a)).join(`
|
|
455
|
+
`).trim()};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/00-table-channel-policy.ts",M={name:_(__filename),sql:(e)=>{return[s`
|
|
446
456
|
CREATE TABLE ${n(e.schema)}."channel_policy" (
|
|
447
457
|
"id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
|
|
448
458
|
"name" TEXT NOT NULL,
|
|
@@ -451,10 +461,10 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
451
461
|
"release_interval_ms" INTEGER,
|
|
452
462
|
PRIMARY KEY ("id")
|
|
453
463
|
);
|
|
454
|
-
`,
|
|
464
|
+
`,s`
|
|
455
465
|
CREATE UNIQUE INDEX "channel_policy_name_ux"
|
|
456
466
|
ON ${n(e.schema)}."channel_policy" ("name");
|
|
457
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",G={name:_(__filename),sql:(e)=>{return[
|
|
467
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/01-table-channel-state.ts",G={name:_(__filename),sql:(e)=>{return[s`
|
|
458
468
|
CREATE TABLE ${n(e.schema)}."channel_state" (
|
|
459
469
|
"id" UUID NOT NULL DEFAULT GEN_RANDOM_UUID(),
|
|
460
470
|
"name" TEXT NOT NULL,
|
|
@@ -465,21 +475,23 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
465
475
|
"current_concurrency" INTEGER NOT NULL,
|
|
466
476
|
"message_next_id" UUID,
|
|
467
477
|
"message_next_dequeue_after" TIMESTAMP,
|
|
478
|
+
"message_next_seq_no" BIGINT,
|
|
468
479
|
PRIMARY KEY ("id")
|
|
469
480
|
);
|
|
470
|
-
`,
|
|
481
|
+
`,s`
|
|
471
482
|
CREATE UNIQUE INDEX "channel_state_name_ux"
|
|
472
483
|
ON ${n(e.schema)}."channel_state" ("name");
|
|
473
|
-
`,
|
|
484
|
+
`,s`
|
|
474
485
|
CREATE INDEX "channel_state_dequeue_ix"
|
|
475
486
|
ON ${n(e.schema)}."channel_state" (
|
|
476
487
|
"message_next_dequeue_after" ASC
|
|
477
488
|
) WHERE "message_next_id" IS NOT NULL
|
|
478
489
|
AND ("max_concurrency" IS NULL OR "current_concurrency" < "max_concurrency");
|
|
479
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",
|
|
490
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/02-table-message.ts",B={name:_(__filename),sql:(e)=>{return[s`
|
|
480
491
|
CREATE TABLE ${n(e.schema)}."message" (
|
|
481
492
|
"id" UUID NOT NULL,
|
|
482
493
|
"channel_name" TEXT NOT NULL,
|
|
494
|
+
"seq_no" BIGSERIAL NOT NULL,
|
|
483
495
|
"name" TEXT,
|
|
484
496
|
"content" BYTEA NOT NULL,
|
|
485
497
|
"state" BYTEA,
|
|
@@ -489,25 +501,25 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
489
501
|
"dequeue_after" TIMESTAMP NOT NULL,
|
|
490
502
|
PRIMARY KEY ("id")
|
|
491
503
|
);
|
|
492
|
-
`,
|
|
504
|
+
`,s`
|
|
493
505
|
CREATE UNIQUE INDEX "message_name_ux"
|
|
494
506
|
ON ${n(e.schema)}."message" (
|
|
495
507
|
"channel_name",
|
|
496
508
|
"name"
|
|
497
509
|
) WHERE "num_attempts" = 0
|
|
498
|
-
`,
|
|
510
|
+
`,s`
|
|
499
511
|
CREATE INDEX "message_dequeue_ix"
|
|
500
512
|
ON ${n(e.schema)}."message" (
|
|
501
513
|
"channel_name",
|
|
502
514
|
"dequeue_after" ASC,
|
|
503
|
-
"
|
|
515
|
+
"seq_no" ASC
|
|
504
516
|
) WHERE NOT "is_locked";
|
|
505
|
-
`,
|
|
517
|
+
`,s`
|
|
506
518
|
CREATE INDEX "message_locked_dequeue_ix"
|
|
507
519
|
ON ${n(e.schema)}."message" (
|
|
508
520
|
"dequeue_after" ASC
|
|
509
521
|
) WHERE "is_locked";
|
|
510
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",
|
|
522
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/03-function-wake.ts",P={name:_(__filename),sql:(e)=>{let t=i.toString(e.schema);return[s`
|
|
511
523
|
CREATE FUNCTION ${n(e.schema)}."wake" (
|
|
512
524
|
p_delay_ms INTEGER
|
|
513
525
|
) RETURNS VOID AS $$
|
|
@@ -517,7 +529,7 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
517
529
|
END IF;
|
|
518
530
|
END;
|
|
519
531
|
$$ LANGUAGE plpgsql;
|
|
520
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",H={name:_(__filename),sql:(e)=>{return[
|
|
532
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/08-function-channel-policy-set.ts",H={name:_(__filename),sql:(e)=>{return[s`
|
|
521
533
|
CREATE FUNCTION ${n(e.schema)}."channel_policy_set" (
|
|
522
534
|
p_name TEXT,
|
|
523
535
|
p_max_size INTEGER,
|
|
@@ -549,7 +561,7 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
549
561
|
PERFORM ${n(e.schema)}."wake"(0);
|
|
550
562
|
END;
|
|
551
563
|
$$ LANGUAGE plpgsql;
|
|
552
|
-
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",k={name:_(__filename),sql:(e)=>{return[
|
|
564
|
+
`]}};var __filename="/home/runner/work/lonnymq/lonnymq/src/migration/09-function-channel-policy-clear.ts",k={name:_(__filename),sql:(e)=>{return[s`
|
|
553
565
|
CREATE FUNCTION ${n(e.schema)}."channel_policy_clear" (
|
|
554
566
|
p_name TEXT
|
|
555
567
|
) RETURNS VOID AS $$
|
|
@@ -581,4 +593,4 @@ var c=(e)=>({nodeType:"VALUE",value:e}),n=(e)=>({nodeType:"REF",value:e}),w=(e)=
|
|
|
581
593
|
PERFORM ${n(e.schema)}."wake"(0);
|
|
582
594
|
END;
|
|
583
595
|
$$ LANGUAGE plpgsql;
|
|
584
|
-
`]}};class R{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let t=new
|
|
596
|
+
`]}};class R{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}create(e){let t=new o({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}),a=new Promise((E)=>{this.registerFn({sortKey:JSON.stringify([t.channelName,t.name,t.createdAt.toISOString()]),execute:(T)=>t.execute(T).then((z)=>E(z))})});return{messageId:t.id,promise:a}}}class g{schema;channelName;registerFn;constructor(e){this.schema=e.schema,this.channelName=e.channelName,this.registerFn=e.registerFn}set(e){let t=new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs});this.registerFn({sortKey:JSON.stringify([t.channelName,null,t.createdAt.toISOString()]),execute:async(a)=>{await t.execute(a)}})}clear(){let e=new r({schema:this.schema,channelName:this.channelName});this.registerFn({sortKey:JSON.stringify([e.channelName,null,e.createdAt.toISOString()]),execute:async(t)=>{await e.execute(t)}})}}class S{policy;message;constructor(e){this.message=new R(e),this.policy=new g(e)}}var ee=(e,t)=>{return e.sortKey.localeCompare(t.sortKey)};class A{commands;schema;constructor(e){this.commands=[],this.schema=e.schema}channel(e){return new S({schema:this.schema,channelName:e,registerFn:(t)=>{this.commands.push(t)}})}async execute(e){for(let t of this.commands.sort(ee))await t.execute(e.databaseClient)}}class I{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}async create(e){return new o({schema:this.schema,channelName:this.channelName,name:e.name,content:e.content,lockMs:e.lockMs,delayMs:e.delayMs}).execute(e.databaseClient)}}class L{schema;channelName;constructor(e){this.schema=e.schema,this.channelName=e.channelName}set(e){return new m({schema:this.schema,channelName:this.channelName,maxConcurrency:e.maxConcurrency,maxSize:e.maxSize,releaseIntervalMs:e.releaseIntervalMs}).execute(e.databaseClient)}clear(e){return new r({schema:this.schema,channelName:this.channelName}).execute(e.databaseClient)}}class v{policy;message;constructor(e){this.message=new I({schema:e.schema,channelName:e.channelName}),this.policy=new L({schema:e.schema,channelName:e.channelName})}}class U{schema;id;channelName;name;content;state;numAttempts;constructor(e){this.schema=e.schema,this.id=e.id,this.channelName=e.channelName,this.name=e.name,this.content=e.content,this.state=e.state,this.numAttempts=e.numAttempts}async defer(e){return new N({schema:this.schema,id:this.id,delayMs:e.delayMs,state:e.state}).execute(e.databaseClient)}async delete(e){return new u({schema:this.schema,id:this.id}).execute(e.databaseClient)}}class b{schema;constructor(e){this.schema=e.schema}async dequeue(e){let a=await new h({schema:this.schema}).execute(e.databaseClient);if(a.resultType==="MESSAGE_DEQUEUED")return{resultType:"MESSAGE_DEQUEUED",message:new U({schema:this.schema,id:a.message.id,channelName:a.message.channelName,name:a.message.name,content:a.message.content,state:a.message.state,numAttempts:a.message.numAttempts})};else return a}channel(e){return new v({schema:this.schema,channelName:e})}batch(){return new A({schema:this.schema})}migrations(e){return[M,G,B,P,C,p,q,$,H,k].map((t)=>({name:t.name,sql:t.sql({schema:this.schema,useWake:e.useWake??x}).map((a)=>F(a.value))})).sort((t,a)=>t.name.localeCompare(a.name))}wakeChannel(){return i.toString(this.schema)}}export{b as Queue,h as MessageDequeueCommand,u as MessageDeleteCommand,N as MessageDeferCommand,o as MessageCreateCommand,m as ChannelPolicySetCommand,r as ChannelPolicyClearCommand};
|