@prsm/queue 2.1.1 → 3.0.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/README.md +8 -8
- package/package.json +1 -1
- package/src/queue.js +23 -45
- package/types/queue.d.ts +11 -11
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ Throw an error to trigger retry. After `maxRetries`, the task fails permanently.
|
|
|
110
110
|
|
|
111
111
|
## Grouped Queues
|
|
112
112
|
|
|
113
|
-
Isolated concurrency per key - perfect for per-tenant throttling.
|
|
113
|
+
Isolated concurrency per key - perfect for per-tenant throttling. Pass `{ group }` as the second argument to `push` or `pushAndWait`.
|
|
114
114
|
|
|
115
115
|
```js
|
|
116
116
|
const queue = new Queue({
|
|
@@ -124,11 +124,11 @@ queue.process(async (payload) => {
|
|
|
124
124
|
|
|
125
125
|
await queue.ready()
|
|
126
126
|
|
|
127
|
-
await queue.
|
|
128
|
-
await queue.
|
|
127
|
+
await queue.push({ action: 'sync' }, { group: 'tenant-123' })
|
|
128
|
+
await queue.push({ action: 'sync' }, { group: 'tenant-456' })
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
Each tenant processes independently. One slow tenant won't block others. Total concurrent tasks across all tenants is capped by `concurrency`.
|
|
131
|
+
Each tenant processes independently. One slow tenant won't block others. Total concurrent tasks across all tenants is capped by `concurrency`. When the group is conditional, just omit the option - no branching needed.
|
|
132
132
|
|
|
133
133
|
## Events
|
|
134
134
|
|
|
@@ -147,7 +147,7 @@ queue.on('drain', () => {})
|
|
|
147
147
|
uuid: string,
|
|
148
148
|
payload: any,
|
|
149
149
|
createdAt: number,
|
|
150
|
-
groupKey?: string, // present when pushed
|
|
150
|
+
groupKey?: string, // present when pushed with { group }
|
|
151
151
|
attempts: number
|
|
152
152
|
}
|
|
153
153
|
```
|
|
@@ -169,7 +169,7 @@ queue.process(async ({ prompt }) => {
|
|
|
169
169
|
|
|
170
170
|
app.post('/api/generate', async (req, res) => {
|
|
171
171
|
const { tenantId, prompt } = req.body
|
|
172
|
-
const taskId = await queue.
|
|
172
|
+
const taskId = await queue.push({ prompt }, { group: tenantId })
|
|
173
173
|
res.json({ queued: true, taskId })
|
|
174
174
|
})
|
|
175
175
|
```
|
|
@@ -202,10 +202,10 @@ queue.on('failed', ({ task, error }) => {
|
|
|
202
202
|
})
|
|
203
203
|
|
|
204
204
|
mesh.exposeCommand('generate-report', async (ctx) => {
|
|
205
|
-
const taskId = await queue.
|
|
205
|
+
const taskId = await queue.push({
|
|
206
206
|
connectionId: ctx.connection.id,
|
|
207
207
|
...ctx.payload,
|
|
208
|
-
})
|
|
208
|
+
}, { group: ctx.connection.id })
|
|
209
209
|
return { queued: true, taskId }
|
|
210
210
|
})
|
|
211
211
|
|
package/package.json
CHANGED
package/src/queue.js
CHANGED
|
@@ -151,14 +151,17 @@ export default class Queue extends EventEmitter {
|
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
153
|
* @param {any} payload
|
|
154
|
+
* @param {{ group?: string }} [options]
|
|
154
155
|
* @returns {Promise<string>}
|
|
155
156
|
*/
|
|
156
|
-
async push(payload) {
|
|
157
|
+
async push(payload, { group } = {}) {
|
|
157
158
|
if (this._closed) throw new Error("Queue is closed")
|
|
158
|
-
const task =
|
|
159
|
+
const task = group
|
|
160
|
+
? { uuid: randomUUID(), payload, createdAt: Date.now(), groupKey: group, attempts: 0 }
|
|
161
|
+
: { uuid: randomUUID(), payload, createdAt: Date.now(), attempts: 0 }
|
|
159
162
|
this._pushed++
|
|
160
163
|
try {
|
|
161
|
-
await this.
|
|
164
|
+
await this._enqueue(task, group)
|
|
162
165
|
} catch (err) {
|
|
163
166
|
this._pushed--
|
|
164
167
|
throw err
|
|
@@ -169,16 +172,18 @@ export default class Queue extends EventEmitter {
|
|
|
169
172
|
|
|
170
173
|
/**
|
|
171
174
|
* @param {any} payload
|
|
172
|
-
* @param {
|
|
175
|
+
* @param {{ group?: string, timeout?: number|string }} [options]
|
|
173
176
|
* @returns {Promise<any>}
|
|
174
177
|
*/
|
|
175
|
-
pushAndWait(payload, timeout = 0) {
|
|
178
|
+
pushAndWait(payload, { group, timeout = 0 } = {}) {
|
|
176
179
|
if (this._closed) return Promise.reject(new Error("Queue is closed"))
|
|
177
|
-
const task =
|
|
180
|
+
const task = group
|
|
181
|
+
? { uuid: randomUUID(), payload, createdAt: Date.now(), groupKey: group, attempts: 0 }
|
|
182
|
+
: { uuid: randomUUID(), payload, createdAt: Date.now(), attempts: 0 }
|
|
178
183
|
this._pushed++
|
|
179
184
|
const result = this._awaitTask(task.uuid, timeout)
|
|
180
185
|
result.catch(() => {})
|
|
181
|
-
return this.
|
|
186
|
+
return this._enqueue(task, group).then(() => {
|
|
182
187
|
this.emit("new", { task })
|
|
183
188
|
return result
|
|
184
189
|
}, (err) => {
|
|
@@ -187,44 +192,17 @@ export default class Queue extends EventEmitter {
|
|
|
187
192
|
})
|
|
188
193
|
}
|
|
189
194
|
|
|
190
|
-
/**
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return this._redis.lPush(`queue:groups:${key}`, JSON.stringify(task)).then(async () => {
|
|
202
|
-
this.emit("new", { task })
|
|
203
|
-
if (!this._groupWorkers.has(key)) {
|
|
204
|
-
this._groupWorkers.set(key, new Map())
|
|
205
|
-
this._groupInFlight.set(key, 0)
|
|
206
|
-
await this._startGroupWorkers(key)
|
|
207
|
-
}
|
|
208
|
-
}, (err) => {
|
|
209
|
-
this._pushed--
|
|
210
|
-
throw err
|
|
211
|
-
})
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
push: async (payload) => {
|
|
216
|
-
const task = makeTask(payload)
|
|
217
|
-
this._pushed++
|
|
218
|
-
await commit(task)
|
|
219
|
-
return task.uuid
|
|
220
|
-
},
|
|
221
|
-
pushAndWait: (payload, timeout = 0) => {
|
|
222
|
-
const task = makeTask(payload)
|
|
223
|
-
this._pushed++
|
|
224
|
-
const result = this._awaitTask(task.uuid, timeout)
|
|
225
|
-
result.catch(() => {})
|
|
226
|
-
return commit(task).then(() => result)
|
|
227
|
-
},
|
|
195
|
+
/** @private */
|
|
196
|
+
async _enqueue(task, group) {
|
|
197
|
+
if (group) {
|
|
198
|
+
await this._redis.lPush(`queue:groups:${group}`, JSON.stringify(task))
|
|
199
|
+
if (!this._groupWorkers.has(group)) {
|
|
200
|
+
this._groupWorkers.set(group, new Map())
|
|
201
|
+
this._groupInFlight.set(group, 0)
|
|
202
|
+
await this._startGroupWorkers(group)
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
await this._redis.lPush("queue:tasks", JSON.stringify(task))
|
|
228
206
|
}
|
|
229
207
|
}
|
|
230
208
|
|
package/types/queue.d.ts
CHANGED
|
@@ -2357,23 +2357,23 @@ export default class Queue extends EventEmitter<[never]> {
|
|
|
2357
2357
|
process(handler: TaskHandler): void;
|
|
2358
2358
|
/**
|
|
2359
2359
|
* @param {any} payload
|
|
2360
|
+
* @param {{ group?: string }} [options]
|
|
2360
2361
|
* @returns {Promise<string>}
|
|
2361
2362
|
*/
|
|
2362
|
-
push(payload: any
|
|
2363
|
+
push(payload: any, { group }?: {
|
|
2364
|
+
group?: string;
|
|
2365
|
+
}): Promise<string>;
|
|
2363
2366
|
/**
|
|
2364
2367
|
* @param {any} payload
|
|
2365
|
-
* @param {
|
|
2368
|
+
* @param {{ group?: string, timeout?: number|string }} [options]
|
|
2366
2369
|
* @returns {Promise<any>}
|
|
2367
2370
|
*/
|
|
2368
|
-
pushAndWait(payload: any,
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
push: (payload: any) => Promise<string>;
|
|
2375
|
-
pushAndWait: (payload: any, timeout?: number | string) => Promise<any>;
|
|
2376
|
-
};
|
|
2371
|
+
pushAndWait(payload: any, { group, timeout }?: {
|
|
2372
|
+
group?: string;
|
|
2373
|
+
timeout?: number | string;
|
|
2374
|
+
}): Promise<any>;
|
|
2375
|
+
/** @private */
|
|
2376
|
+
private _enqueue;
|
|
2377
2377
|
/** @private */
|
|
2378
2378
|
private _awaitTask;
|
|
2379
2379
|
/** @returns {Promise<void>} */
|