lonnymq 0.0.20 → 0.0.22
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 +1 -67
- package/dist/index.cjs +97 -145
- package/dist/index.d.ts +2 -103
- package/dist/index.js +96 -144
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,36 +180,6 @@ When deferring a message, you can optionally specify `delayMs` and `state` argum
|
|
|
180
180
|
|
|
181
181
|
**Note:** The above shows just one processing pattern (defer on failure with retry limits). You have complete flexibility in how you handle message processing - you might delete messages immediately, defer them unconditionally, implement different retry strategies based on error types, or use the message metadata (attempts, state, channel) to make sophisticated routing decisions.
|
|
182
182
|
|
|
183
|
-
### Message Heartbeats
|
|
184
|
-
|
|
185
|
-
For long-running message processing, you can use the heartbeat functionality to extend the lock time beyond the initial `lockMs` value. This is useful when you need to process a message for longer than originally anticipated but want to keep a shorter default lock time for faster recovery.
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
const dequeueResult = await queue.dequeue({ databaseClient })
|
|
189
|
-
|
|
190
|
-
if (dequeueResult.resultType === "MESSAGE_DEQUEUED") {
|
|
191
|
-
const { message } = dequeueResult
|
|
192
|
-
|
|
193
|
-
// Start a long-running process
|
|
194
|
-
const intervalId = setInterval(async () => {
|
|
195
|
-
// Send heartbeat every 10 seconds to keep the message locked
|
|
196
|
-
await message.heartbeat({ databaseClient })
|
|
197
|
-
}, 10000)
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
// Process the message (this might take a long time)
|
|
201
|
-
await longRunningProcessMessage(message.content)
|
|
202
|
-
await message.delete({ databaseClient })
|
|
203
|
-
} catch (error) {
|
|
204
|
-
await message.defer({ databaseClient, delayMs: 30000 })
|
|
205
|
-
} finally {
|
|
206
|
-
clearInterval(intervalId)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Heartbeats reset the unlock time to the current time plus the original `lockMs` value.
|
|
212
|
-
|
|
213
183
|
### Graceful Shutdowns and Message Recovery
|
|
214
184
|
|
|
215
185
|
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 essential that you shut down gracefully by catching unhandled exceptions and signals (i.e., `SIGINT`/`SIGTERM`) and finalize all outstanding messages before exiting.
|
|
@@ -282,7 +252,7 @@ await wait(messageId)
|
|
|
282
252
|
|
|
283
253
|
## Deadlocks
|
|
284
254
|
|
|
285
|
-
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
|
|
255
|
+
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 ordering with respect to the target channel name:
|
|
286
256
|
|
|
287
257
|
- Message create
|
|
288
258
|
- Channel policy set
|
|
@@ -293,42 +263,6 @@ Beyond the actions specified above, it is manifestly **unsafe** to bulk-perform
|
|
|
293
263
|
- Message dequeue
|
|
294
264
|
- Message defer
|
|
295
265
|
- Message delete
|
|
296
|
-
- Message heartbeat
|
|
297
|
-
|
|
298
|
-
## Batching Operations
|
|
299
|
-
|
|
300
|
-
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.
|
|
301
|
-
|
|
302
|
-
The batch interface mirrors a subset of the queue interface, supporting only the operations that are safe to perform together: message creation and channel policy management.
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
const batch = queue.batch()
|
|
306
|
-
|
|
307
|
-
// Create messages (assigned to random channels)
|
|
308
|
-
batch.message.create({
|
|
309
|
-
content: Buffer.from("Welcome email"),
|
|
310
|
-
lockMs: 30000
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
// Create messages on specific channels
|
|
314
|
-
batch.channel("user-notifications").message.create({
|
|
315
|
-
content: Buffer.from("User signup notification"),
|
|
316
|
-
lockMs: 60000
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
// Set channel policies
|
|
320
|
-
batch.channel("high-priority").policy.set({
|
|
321
|
-
maxConcurrency: 5,
|
|
322
|
-
releaseIntervalMs: 100
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
// Clear channel policies
|
|
326
|
-
batch.channel("analytics").policy.clear()
|
|
327
|
-
|
|
328
|
-
await batch.execute({ databaseClient })
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
The batch system ensures all operations are executed in a consistent lexicographical order, eliminating the possibility of deadlocks when multiple workers are performing bulk operations simultaneously.
|
|
332
266
|
|
|
333
267
|
## Database Clients
|
|
334
268
|
|