bunqueue 2.8.48 → 2.8.50
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 +19 -9
- package/dist/application/contextFactory.d.ts +4 -2
- package/dist/application/contextFactory.js +3 -0
- package/dist/application/flowFailureRecovery.d.ts +17 -0
- package/dist/application/flowFailureRecovery.js +92 -0
- package/dist/application/operations/ack.d.ts +3 -2
- package/dist/application/operations/ack.js +34 -8
- package/dist/application/operations/flowPush.d.ts +9 -0
- package/dist/application/operations/flowPush.js +112 -0
- package/dist/application/operations/flowTopologyValidation.d.ts +3 -0
- package/dist/application/operations/flowTopologyValidation.js +101 -0
- package/dist/application/operations/flowValidation.d.ts +3 -0
- package/dist/application/operations/flowValidation.js +166 -0
- package/dist/application/operations/push.d.ts +3 -0
- package/dist/application/operations/push.js +2 -2
- package/dist/application/operations/pushInsert.d.ts +7 -1
- package/dist/application/operations/pushInsert.js +18 -8
- package/dist/application/operations/pushLocks.d.ts +1 -0
- package/dist/application/operations/pushLocks.js +5 -1
- package/dist/application/operations/queryOperations.js +7 -6
- package/dist/application/queueManager.d.ts +5 -0
- package/dist/application/queueManager.js +195 -60
- package/dist/cli/commandRouter.d.ts +1 -1
- package/dist/client/flow.d.ts +3 -6
- package/dist/client/flow.js +71 -210
- package/dist/client/flowAtomic.d.ts +9 -0
- package/dist/client/flowAtomic.js +24 -0
- package/dist/client/flowJobCoreMethods.d.ts +28 -0
- package/dist/client/flowJobCoreMethods.js +151 -0
- package/dist/client/flowJobDependencies.d.ts +7 -0
- package/dist/client/flowJobDependencies.js +64 -0
- package/dist/client/flowJobFactory.d.ts +11 -24
- package/dist/client/flowJobFactory.js +91 -360
- package/dist/client/flowJobMoveMethods.d.ts +16 -0
- package/dist/client/flowJobMoveMethods.js +112 -0
- package/dist/client/flowJobTypes.d.ts +29 -0
- package/dist/client/flowJobTypes.js +6 -0
- package/dist/client/flowLegacyPlan.d.ts +16 -0
- package/dist/client/flowLegacyPlan.js +118 -0
- package/dist/client/flowOptions.d.ts +10 -0
- package/dist/client/flowOptions.js +44 -0
- package/dist/client/flowPlan.d.ts +16 -0
- package/dist/client/flowPlan.js +93 -0
- package/dist/client/flowPush.js +4 -1
- package/dist/client/flowReader.d.ts +10 -0
- package/dist/client/flowReader.js +147 -0
- package/dist/client/jobHelpers.js +2 -0
- package/dist/client/workflow/clock.d.ts +3 -1
- package/dist/client/workflow/clock.js +18 -1
- package/dist/client/workflow/compensationChild.d.ts +30 -0
- package/dist/client/workflow/compensationChild.js +65 -0
- package/dist/client/workflow/compensationPass.d.ts +16 -0
- package/dist/client/workflow/compensationPass.js +96 -0
- package/dist/client/workflow/compensationSupport.d.ts +24 -0
- package/dist/client/workflow/compensationSupport.js +57 -0
- package/dist/client/workflow/compensator.d.ts +11 -50
- package/dist/client/workflow/compensator.js +22 -374
- package/dist/client/workflow/definitionGuard.d.ts +11 -0
- package/dist/client/workflow/definitionGuard.js +22 -0
- package/dist/client/workflow/engine.d.ts +2 -2
- package/dist/client/workflow/engine.js +8 -8
- package/dist/client/workflow/eventTypes.d.ts +44 -0
- package/dist/client/workflow/eventTypes.js +2 -0
- package/dist/client/workflow/executionTypes.d.ts +133 -0
- package/dist/client/workflow/executionTypes.js +2 -0
- package/dist/client/workflow/executor.d.ts +4 -17
- package/dist/client/workflow/executor.js +50 -208
- package/dist/client/workflow/executorLifecycle.d.ts +18 -0
- package/dist/client/workflow/executorLifecycle.js +69 -0
- package/dist/client/workflow/executorNodes.d.ts +17 -0
- package/dist/client/workflow/executorNodes.js +138 -0
- package/dist/client/workflow/identity.d.ts +2 -0
- package/dist/client/workflow/identity.js +8 -0
- package/dist/client/workflow/index.d.ts +1 -1
- package/dist/client/workflow/loops.d.ts +3 -5
- package/dist/client/workflow/loops.js +25 -23
- package/dist/client/workflow/mapRunner.d.ts +4 -0
- package/dist/client/workflow/mapRunner.js +45 -0
- package/dist/client/workflow/recovery.js +10 -3
- package/dist/client/workflow/runner.d.ts +2 -21
- package/dist/client/workflow/runner.js +39 -104
- package/dist/client/workflow/runnerTiming.d.ts +14 -0
- package/dist/client/workflow/runnerTiming.js +66 -0
- package/dist/client/workflow/stepTypes.d.ts +171 -0
- package/dist/client/workflow/stepTypes.js +3 -0
- package/dist/client/workflow/store.d.ts +17 -17
- package/dist/client/workflow/store.js +65 -100
- package/dist/client/workflow/storeExecutionCodec.d.ts +11 -0
- package/dist/client/workflow/storeExecutionCodec.js +34 -0
- package/dist/client/workflow/storeListing.d.ts +11 -0
- package/dist/client/workflow/storeListing.js +45 -0
- package/dist/client/workflow/storeMaintenance.d.ts +4 -0
- package/dist/client/workflow/storeMaintenance.js +40 -0
- package/dist/client/workflow/storeSignals.d.ts +9 -0
- package/dist/client/workflow/storeSignals.js +38 -2
- package/dist/client/workflow/subWorkflowRunner.d.ts +13 -0
- package/dist/client/workflow/subWorkflowRunner.js +40 -0
- package/dist/client/workflow/types.d.ts +4 -356
- package/dist/client/workflow/types.js +1 -3
- package/dist/client/workflow/waitFor.js +39 -26
- package/dist/client/workflow/workflow.d.ts +16 -59
- package/dist/client/workflow/workflow.js +53 -179
- package/dist/client/workflow/workflowDecisions.d.ts +11 -0
- package/dist/client/workflow/workflowDecisions.js +27 -0
- package/dist/client/workflow/workflowDefinition.d.ts +16 -0
- package/dist/client/workflow/workflowDefinition.js +123 -0
- package/dist/client/workflow/workflowIntrospection.d.ts +5 -0
- package/dist/client/workflow/workflowIntrospection.js +46 -0
- package/dist/client/workflow/workflowValidation.d.ts +44 -0
- package/dist/client/workflow/workflowValidation.js +143 -0
- package/dist/domain/types/command.d.ts +7 -1
- package/dist/domain/types/flow.d.ts +25 -0
- package/dist/domain/types/flow.js +1 -0
- package/dist/infrastructure/persistence/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +52 -3
- package/dist/infrastructure/persistence/sqlite.d.ts +16 -0
- package/dist/infrastructure/persistence/sqlite.js +129 -7
- package/dist/infrastructure/persistence/sqliteBatch.js +10 -4
- package/dist/infrastructure/persistence/sqliteSerializer.d.ts +2 -0
- package/dist/infrastructure/persistence/sqliteSerializer.js +18 -5
- package/dist/infrastructure/persistence/statements.d.ts +4 -0
- package/dist/infrastructure/persistence/statements.js +8 -2
- package/dist/infrastructure/server/handlerRoutes.js +3 -0
- package/dist/infrastructure/server/handlers/advanced.js +2 -2
- package/dist/infrastructure/server/handlers/flow.d.ts +7 -0
- package/dist/infrastructure/server/handlers/flow.js +11 -0
- package/dist/infrastructure/server/handlers/index.d.ts +1 -0
- package/dist/infrastructure/server/handlers/index.js +1 -0
- package/package.json +4 -2
|
@@ -13,8 +13,9 @@ import { WorkerManager } from './workerManager';
|
|
|
13
13
|
import { EventsManager } from './eventsManager';
|
|
14
14
|
import { createMonitoringState } from './monitoringChecks';
|
|
15
15
|
import { RWLock, withWriteLock } from '../shared/lock';
|
|
16
|
-
import { shardIndex, SHARD_COUNT } from '../shared/hash';
|
|
16
|
+
import { processingShardIndex, shardIndex, SHARD_COUNT } from '../shared/hash';
|
|
17
17
|
import { pushJob, pushJobBatch } from './operations/push';
|
|
18
|
+
import { pushFlowBatch } from './operations/flowPush';
|
|
18
19
|
import { pullJob, pullJobBatch } from './operations/pull';
|
|
19
20
|
import { ackJob, ackJobBatch, ackJobBatchWithResults, failJob } from './operations/ack';
|
|
20
21
|
import * as queueControl from './operations/queueControl';
|
|
@@ -35,6 +36,7 @@ import { ContextFactory } from './contextFactory';
|
|
|
35
36
|
import { processPendingDependencies } from './dependencyProcessor';
|
|
36
37
|
import { handleTaskError, handleTaskSuccess } from './taskErrorTracking';
|
|
37
38
|
import { DependencyResultTracker } from './dependencyResultTracker';
|
|
39
|
+
import { recoverFlowFailures } from './flowFailureRecovery';
|
|
38
40
|
/**
|
|
39
41
|
* QueueManager - Central coordinator
|
|
40
42
|
*/
|
|
@@ -44,6 +46,7 @@ export class QueueManager {
|
|
|
44
46
|
// Sharded data structures
|
|
45
47
|
shards = [];
|
|
46
48
|
shardLocks = [];
|
|
49
|
+
customIdLock = new RWLock();
|
|
47
50
|
processingShards = [];
|
|
48
51
|
processingLocks = [];
|
|
49
52
|
// Global indexes (bounded with LRU eviction)
|
|
@@ -162,6 +165,17 @@ export class QueueManager {
|
|
|
162
165
|
this.contextFactory = new ContextFactory(this.getContextDependencies(), this.getContextCallbacks());
|
|
163
166
|
// Load and start
|
|
164
167
|
bgTasks.recover(this.contextFactory.getBackgroundContext());
|
|
168
|
+
if (this.storage) {
|
|
169
|
+
recoverFlowFailures({
|
|
170
|
+
storage: this.storage,
|
|
171
|
+
shards: this.shards,
|
|
172
|
+
jobIndex: this.jobIndex,
|
|
173
|
+
completedJobs: this.completedJobs,
|
|
174
|
+
dependencyResults: this.dependencyResults,
|
|
175
|
+
failedChildrenValues: this.failedChildrenValues,
|
|
176
|
+
ignoredChildrenFailures: this.ignoredChildrenFailures,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
165
179
|
this.recoveryStats = { queues: this.queueNamesCache.size, jobs: this.jobIndex.size };
|
|
166
180
|
if (this.storage) {
|
|
167
181
|
this.cronScheduler.load(this.storage.loadCronJobs());
|
|
@@ -175,6 +189,7 @@ export class QueueManager {
|
|
|
175
189
|
storage: this.storage,
|
|
176
190
|
shards: this.shards,
|
|
177
191
|
shardLocks: this.shardLocks,
|
|
192
|
+
customIdLock: this.customIdLock,
|
|
178
193
|
processingShards: this.processingShards,
|
|
179
194
|
processingLocks: this.processingLocks,
|
|
180
195
|
jobIndex: this.jobIndex,
|
|
@@ -208,6 +223,7 @@ export class QueueManager {
|
|
|
208
223
|
registerQueueName: this.registerQueueName.bind(this),
|
|
209
224
|
unregisterQueueName: this.unregisterQueueName.bind(this),
|
|
210
225
|
onJobCompleted: this.onJobCompleted.bind(this),
|
|
226
|
+
onJobFailed: this.onJobFailed.bind(this),
|
|
211
227
|
onJobsCompleted: this.onJobsCompleted.bind(this),
|
|
212
228
|
hasPendingDeps: this.hasPendingDeps.bind(this),
|
|
213
229
|
onRepeat: this.handleRepeat.bind(this),
|
|
@@ -260,6 +276,9 @@ export class QueueManager {
|
|
|
260
276
|
this.registerQueueName(queue);
|
|
261
277
|
return pushJobBatch(queue, inputs, this.contextFactory.getPushContext());
|
|
262
278
|
}
|
|
279
|
+
async pushFlow(batch) {
|
|
280
|
+
return pushFlowBatch(batch, this.contextFactory.getPushContext());
|
|
281
|
+
}
|
|
263
282
|
async pull(queue, timeoutMs = 0, signal) {
|
|
264
283
|
return pullJob(queue, timeoutMs, this.contextFactory.getPullContext(), signal);
|
|
265
284
|
}
|
|
@@ -665,49 +684,96 @@ export class QueueManager {
|
|
|
665
684
|
* Used by FlowProducer when creating flows where children need to reference parent.
|
|
666
685
|
*/
|
|
667
686
|
async updateJobParent(childJobId, parentJobId) {
|
|
687
|
+
if (childJobId === parentJobId)
|
|
688
|
+
throw new Error('A flow job cannot be its own parent');
|
|
668
689
|
const childJob = await this.getJob(childJobId);
|
|
669
690
|
if (!childJob)
|
|
670
|
-
|
|
691
|
+
throw new Error(`Child job not found: ${String(childJobId)}`);
|
|
671
692
|
const parentJob = await this.getJob(parentJobId);
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
693
|
+
if (!parentJob)
|
|
694
|
+
throw new Error(`Parent job not found: ${String(parentJobId)}`);
|
|
695
|
+
const priorParent = childJob.parentId;
|
|
696
|
+
if (priorParent && String(priorParent) !== 'pending' && priorParent !== parentJobId) {
|
|
697
|
+
throw new Error(`Child job ${String(childJobId)} already belongs to parent ${String(priorParent)}`);
|
|
698
|
+
}
|
|
699
|
+
const parentLocation = this.jobIndex.get(parentJobId);
|
|
700
|
+
if (parentLocation?.type !== 'queue') {
|
|
701
|
+
throw new Error(`Parent job ${String(parentJobId)} is not linkable`);
|
|
702
|
+
}
|
|
703
|
+
const childLocation = this.jobIndex.get(childJobId);
|
|
704
|
+
const shardIndexes = [
|
|
705
|
+
...new Set([shardIndex(childJob.queue), shardIndex(parentJob.queue)]),
|
|
706
|
+
].sort((a, b) => a - b);
|
|
707
|
+
const processingIndexes = childLocation?.type === 'processing' ? [processingShardIndex(childJobId)] : [];
|
|
708
|
+
const guards = [await this.customIdLock.acquireWrite()];
|
|
709
|
+
try {
|
|
710
|
+
for (const index of shardIndexes)
|
|
711
|
+
guards.push(await this.shardLocks[index].acquireWrite());
|
|
712
|
+
for (const index of processingIndexes) {
|
|
713
|
+
guards.push(await this.processingLocks[index].acquireWrite());
|
|
685
714
|
}
|
|
686
|
-
if (
|
|
687
|
-
|
|
715
|
+
if (this.jobIndex.get(parentJobId)?.type !== 'queue') {
|
|
716
|
+
throw new Error(`Parent job ${String(parentJobId)} changed state while linking`);
|
|
688
717
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
718
|
+
const childData = {
|
|
719
|
+
...childJob.data,
|
|
720
|
+
__parentId: String(parentJobId),
|
|
721
|
+
__parentQueue: parentJob.queue,
|
|
722
|
+
};
|
|
723
|
+
const childrenIds = parentJob.childrenIds.includes(childJobId)
|
|
724
|
+
? [...parentJob.childrenIds]
|
|
725
|
+
: [...parentJob.childrenIds, childJobId];
|
|
726
|
+
const dependsOn = parentJob.dependsOn.includes(childJobId)
|
|
727
|
+
? [...parentJob.dependsOn]
|
|
728
|
+
: [...parentJob.dependsOn, childJobId];
|
|
729
|
+
const parentData = {
|
|
730
|
+
...parentJob.data,
|
|
731
|
+
__childrenIds: childrenIds.map(String),
|
|
732
|
+
};
|
|
733
|
+
const linkedChild = { ...childJob, data: childData, parentId: parentJobId };
|
|
734
|
+
const linkedParent = { ...parentJob, data: parentData, childrenIds, dependsOn };
|
|
735
|
+
const childFinished = this.completedJobs.has(childJobId) || this.depCompletions.has(childJobId);
|
|
736
|
+
const parentState = childFinished
|
|
737
|
+
? parentJob.runAt > Date.now()
|
|
738
|
+
? 'delayed'
|
|
739
|
+
: parentJob.priority > 0
|
|
740
|
+
? 'prioritized'
|
|
741
|
+
: 'waiting'
|
|
742
|
+
: 'waiting-children';
|
|
743
|
+
this.storage?.updateFlowLink(linkedChild, linkedParent, parentState);
|
|
744
|
+
childJob.parentId = parentJobId;
|
|
745
|
+
childJob.data = childData;
|
|
746
|
+
parentJob.childrenIds = childrenIds;
|
|
747
|
+
parentJob.dependsOn = dependsOn;
|
|
748
|
+
parentJob.data = parentData;
|
|
749
|
+
if (!childFinished) {
|
|
750
|
+
const shard = this.shards[shardIndex(parentJob.queue)];
|
|
751
|
+
if (!shard.waitingDeps.has(parentJobId)) {
|
|
752
|
+
const removed = shard.getQueue(parentJob.queue).remove(parentJobId);
|
|
753
|
+
if (removed)
|
|
754
|
+
shard.decrementQueued(parentJobId);
|
|
755
|
+
shard.waitingDeps.set(parentJobId, parentJob);
|
|
756
|
+
}
|
|
757
|
+
shard.registerDependencies(parentJobId, [childJobId]);
|
|
758
|
+
this.dependencyResults.registerConsumer(parentJobId, dependsOn);
|
|
695
759
|
}
|
|
696
760
|
}
|
|
761
|
+
finally {
|
|
762
|
+
for (let index = guards.length - 1; index >= 0; index--)
|
|
763
|
+
guards[index].release();
|
|
764
|
+
}
|
|
697
765
|
// Handle race condition: child may have already terminally failed
|
|
698
766
|
// before parent linkage was established (parentId was 'pending').
|
|
699
767
|
// If so, propagate failParentOnFailure now with the real parent ID.
|
|
700
768
|
const childLoc = this.jobIndex.get(childJobId);
|
|
701
769
|
if (childLoc?.type === 'dlq' && childJob.failParentOnFailure) {
|
|
702
|
-
this.moveParentToFailed(parentJobId, childJob, 'Child job failed')
|
|
703
|
-
// Best-effort: parent may already be in DLQ or removed
|
|
704
|
-
});
|
|
770
|
+
await this.moveParentToFailed(parentJobId, childJob, 'Child job failed');
|
|
705
771
|
}
|
|
706
772
|
if (childLoc?.type === 'dlq' &&
|
|
707
773
|
(childJob.removeDependencyOnFailure ||
|
|
708
774
|
childJob.ignoreDependencyOnFailure ||
|
|
709
775
|
childJob.continueParentOnFailure)) {
|
|
710
|
-
this.onChildDependencyOption(childJob, 'Child job failed');
|
|
776
|
+
await this.onChildDependencyOption(childJob, 'Child job failed');
|
|
711
777
|
}
|
|
712
778
|
}
|
|
713
779
|
getJobByCustomId(customId) {
|
|
@@ -1221,10 +1287,17 @@ export class QueueManager {
|
|
|
1221
1287
|
// leaked an entry permanently.
|
|
1222
1288
|
this.failedChildrenValues.delete(completedId);
|
|
1223
1289
|
this.ignoredChildrenFailures.delete(completedId);
|
|
1290
|
+
this.storage?.deleteFlowFailure(completedId);
|
|
1224
1291
|
this.pendingDepChecks.add(completedId);
|
|
1225
1292
|
this.scheduleDependencyFlush();
|
|
1226
1293
|
void this.checkFlowCompleted(completedId);
|
|
1227
1294
|
}
|
|
1295
|
+
/** Release failure metadata owned by a parent that reached terminal failure. */
|
|
1296
|
+
onJobFailed(failedId) {
|
|
1297
|
+
this.failedChildrenValues.delete(failedId);
|
|
1298
|
+
this.ignoredChildrenFailures.delete(failedId);
|
|
1299
|
+
this.storage?.deleteFlowFailure(failedId);
|
|
1300
|
+
}
|
|
1228
1301
|
/** Check if completing this job completes an entire flow */
|
|
1229
1302
|
async checkFlowCompleted(completedId) {
|
|
1230
1303
|
const job = await this.getJob(completedId);
|
|
@@ -1246,13 +1319,11 @@ export class QueueManager {
|
|
|
1246
1319
|
* BullMQ v5: failParentOnFailure — when a child terminally fails
|
|
1247
1320
|
* and has failParentOnFailure: true, also fail the parent job.
|
|
1248
1321
|
*/
|
|
1249
|
-
failParentOnChildFailure(childJob, error) {
|
|
1322
|
+
async failParentOnChildFailure(childJob, error) {
|
|
1250
1323
|
const parentId = childJob.parentId;
|
|
1251
1324
|
if (!parentId)
|
|
1252
1325
|
return;
|
|
1253
|
-
this.moveParentToFailed(parentId, childJob, error)
|
|
1254
|
-
// Best-effort: parent may already be in DLQ or removed
|
|
1255
|
-
});
|
|
1326
|
+
await this.moveParentToFailed(parentId, childJob, error);
|
|
1256
1327
|
}
|
|
1257
1328
|
/** Move a parent job to DLQ/failed state because a child failed */
|
|
1258
1329
|
async moveParentToFailed(parentId, childJob, error) {
|
|
@@ -1292,6 +1363,7 @@ export class QueueManager {
|
|
|
1292
1363
|
this.jobIndex.set(parentId, { type: 'dlq', queueName: parentJob.queue });
|
|
1293
1364
|
this.storage?.saveDlqEntry(entry);
|
|
1294
1365
|
this.storage?.deleteJob(parentId);
|
|
1366
|
+
this.storage?.deleteFlowFailure(parentId, childJob.id);
|
|
1295
1367
|
});
|
|
1296
1368
|
// Parent reached a terminal (DLQ) state — release its flow-failure tracking.
|
|
1297
1369
|
this.failedChildrenValues.delete(parentId);
|
|
@@ -1316,19 +1388,15 @@ export class QueueManager {
|
|
|
1316
1388
|
/**
|
|
1317
1389
|
* Handle child dependency options: removeDependencyOnFailure, ignoreDependencyOnFailure, continueParentOnFailure
|
|
1318
1390
|
*/
|
|
1319
|
-
onChildDependencyOption(childJob, error) {
|
|
1391
|
+
async onChildDependencyOption(childJob, error) {
|
|
1320
1392
|
if (!childJob.parentId)
|
|
1321
1393
|
return;
|
|
1322
1394
|
if (childJob.continueParentOnFailure) {
|
|
1323
|
-
this.continueParentOnChildFailure(childJob, error)
|
|
1324
|
-
// Best-effort parent propagation; child failure is already durable.
|
|
1325
|
-
});
|
|
1395
|
+
await this.continueParentOnChildFailure(childJob, error);
|
|
1326
1396
|
}
|
|
1327
1397
|
else {
|
|
1328
1398
|
// removeDependencyOnFailure or ignoreDependencyOnFailure
|
|
1329
|
-
this.removeChildFromParentDeps(childJob, error, childJob.ignoreDependencyOnFailure)
|
|
1330
|
-
// Best-effort parent propagation; child failure is already durable.
|
|
1331
|
-
});
|
|
1399
|
+
await this.removeChildFromParentDeps(childJob, error, childJob.ignoreDependencyOnFailure);
|
|
1332
1400
|
}
|
|
1333
1401
|
}
|
|
1334
1402
|
/**
|
|
@@ -1351,6 +1419,18 @@ export class QueueManager {
|
|
|
1351
1419
|
existing[childKey] = error ?? 'unknown error';
|
|
1352
1420
|
this.failedChildrenValues.set(parentId, existing);
|
|
1353
1421
|
const idx = shardIndex(parentJob.queue);
|
|
1422
|
+
await withWriteLock(this.shardLocks[idx], () => {
|
|
1423
|
+
if (this.jobIndex.get(parentId)?.type !== 'queue')
|
|
1424
|
+
return;
|
|
1425
|
+
const shard = this.shards[idx];
|
|
1426
|
+
const dependencies = [...parentJob.dependsOn];
|
|
1427
|
+
shard.unregisterDependencies(parentId, dependencies);
|
|
1428
|
+
for (const dependency of dependencies) {
|
|
1429
|
+
this.dependencyResults.releaseDependency(parentId, dependency);
|
|
1430
|
+
}
|
|
1431
|
+
parentJob.dependsOn = [];
|
|
1432
|
+
this.storage?.updateFlowParentResolution(parentJob);
|
|
1433
|
+
});
|
|
1354
1434
|
await this.promoteParentAfterChildFailure(parentId, parentJob, idx);
|
|
1355
1435
|
}
|
|
1356
1436
|
/**
|
|
@@ -1387,6 +1467,7 @@ export class QueueManager {
|
|
|
1387
1467
|
queue.push(parentJob);
|
|
1388
1468
|
shard.incrementQueued(parentId, false, parentJob.createdAt, parentJob.queue, now);
|
|
1389
1469
|
this.jobIndex.set(parentId, { type: 'queue', shardIdx: idx, queueName: parentJob.queue });
|
|
1470
|
+
this.storage?.updateFlowParentResolution(parentJob);
|
|
1390
1471
|
shard.notify(parentJob.queue);
|
|
1391
1472
|
promoted = true;
|
|
1392
1473
|
}
|
|
@@ -1439,6 +1520,7 @@ export class QueueManager {
|
|
|
1439
1520
|
parentJob.dependsOn.splice(depIndex, 1);
|
|
1440
1521
|
shard.unregisterDependencies(parentId, [childJob.id]);
|
|
1441
1522
|
this.dependencyResults.releaseDependency(parentId, childJob.id);
|
|
1523
|
+
this.storage?.updateFlowParentResolution(parentJob);
|
|
1442
1524
|
}
|
|
1443
1525
|
// If no more pending deps, the parent is ready to be promoted
|
|
1444
1526
|
readyToPromote =
|
|
@@ -1448,6 +1530,8 @@ export class QueueManager {
|
|
|
1448
1530
|
if (readyToPromote) {
|
|
1449
1531
|
await this.promoteParentAfterChildFailure(parentId, parentJob, idx);
|
|
1450
1532
|
}
|
|
1533
|
+
if (!storeIgnored)
|
|
1534
|
+
this.storage?.deleteFlowFailure(parentId, childJob.id);
|
|
1451
1535
|
}
|
|
1452
1536
|
/**
|
|
1453
1537
|
* Get failed children values for a parent job (populated by continueParentOnFailure).
|
|
@@ -1479,34 +1563,85 @@ export class QueueManager {
|
|
|
1479
1563
|
const parentLoc = this.jobIndex.get(parentId);
|
|
1480
1564
|
if (parentLoc?.type !== 'queue')
|
|
1481
1565
|
return false;
|
|
1482
|
-
const
|
|
1566
|
+
const childLoc = this.jobIndex.get(childJobId);
|
|
1567
|
+
if (childLoc?.type !== 'queue' && childLoc?.type !== 'processing')
|
|
1568
|
+
return false;
|
|
1569
|
+
const parentIdx = shardIndex(parentJob.queue);
|
|
1570
|
+
const shardIndexes = [...new Set([parentIdx, shardIndex(childJob.queue)])].sort((a, b) => a - b);
|
|
1571
|
+
const processingIndexes = childLoc.type === 'processing' ? [processingShardIndex(childJobId)] : [];
|
|
1572
|
+
const guards = [];
|
|
1573
|
+
let removed = false;
|
|
1483
1574
|
let promoted = false;
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
parentJob.dependsOn.splice(depIndex, 1);
|
|
1494
|
-
shard.unregisterDependencies(parentId, [childJobId]);
|
|
1495
|
-
this.dependencyResults.releaseDependency(parentId, childJobId);
|
|
1575
|
+
try {
|
|
1576
|
+
for (const index of shardIndexes)
|
|
1577
|
+
guards.push(await this.shardLocks[index].acquireWrite());
|
|
1578
|
+
for (const index of processingIndexes) {
|
|
1579
|
+
guards.push(await this.processingLocks[index].acquireWrite());
|
|
1580
|
+
}
|
|
1581
|
+
if (this.jobIndex.get(parentId)?.type !== 'queue' ||
|
|
1582
|
+
this.jobIndex.get(childJobId)?.type !== childLoc.type) {
|
|
1583
|
+
return false;
|
|
1496
1584
|
}
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1499
|
-
|
|
1585
|
+
const shard = this.shards[parentIdx];
|
|
1586
|
+
if (!shard.waitingDeps.has(parentId) || !parentJob.dependsOn.includes(childJobId)) {
|
|
1587
|
+
return false;
|
|
1588
|
+
}
|
|
1589
|
+
const unresolved = parentJob.dependsOn.filter((dependency) => dependency !== childJobId &&
|
|
1590
|
+
!this.completedJobs.has(dependency) &&
|
|
1591
|
+
!this.depCompletions.has(dependency));
|
|
1592
|
+
const released = parentJob.dependsOn.filter((dependency) => !unresolved.includes(dependency));
|
|
1593
|
+
const childrenIds = parentJob.childrenIds.filter((childId) => childId !== childJobId);
|
|
1594
|
+
const childData = { ...childJob.data };
|
|
1595
|
+
delete childData.__parentId;
|
|
1596
|
+
delete childData.__parentQueue;
|
|
1597
|
+
const parentData = { ...parentJob.data };
|
|
1598
|
+
if (childrenIds.length > 0)
|
|
1599
|
+
parentData.__childrenIds = childrenIds.map(String);
|
|
1600
|
+
else
|
|
1601
|
+
delete parentData.__childrenIds;
|
|
1602
|
+
const runAt = unresolved.length === 0 ? Date.now() : parentJob.runAt;
|
|
1603
|
+
const parentState = unresolved.length > 0
|
|
1604
|
+
? 'waiting-children'
|
|
1605
|
+
: parentJob.priority > 0
|
|
1606
|
+
? 'prioritized'
|
|
1607
|
+
: 'waiting';
|
|
1608
|
+
const detachedChild = { ...childJob, parentId: null, data: childData };
|
|
1609
|
+
const detachedParent = {
|
|
1610
|
+
...parentJob,
|
|
1611
|
+
childrenIds,
|
|
1612
|
+
dependsOn: unresolved,
|
|
1613
|
+
data: parentData,
|
|
1614
|
+
runAt,
|
|
1615
|
+
};
|
|
1616
|
+
this.storage?.removeFlowLink(detachedChild, detachedParent, parentState);
|
|
1617
|
+
childJob.parentId = null;
|
|
1618
|
+
childJob.data = childData;
|
|
1619
|
+
parentJob.childrenIds = childrenIds;
|
|
1620
|
+
parentJob.dependsOn = unresolved;
|
|
1621
|
+
parentJob.data = parentData;
|
|
1622
|
+
parentJob.runAt = runAt;
|
|
1623
|
+
shard.unregisterDependencies(parentId, released);
|
|
1624
|
+
for (const dependency of released) {
|
|
1625
|
+
this.dependencyResults.releaseDependency(parentId, dependency);
|
|
1626
|
+
}
|
|
1627
|
+
if (unresolved.length === 0) {
|
|
1500
1628
|
shard.waitingDeps.delete(parentId);
|
|
1501
|
-
const now = Date.now();
|
|
1502
|
-
parentJob.runAt = now;
|
|
1503
1629
|
shard.getQueue(parentJob.queue).push(parentJob);
|
|
1504
|
-
shard.incrementQueued(parentId, false, parentJob.createdAt, parentJob.queue,
|
|
1505
|
-
this.jobIndex.set(parentId, {
|
|
1630
|
+
shard.incrementQueued(parentId, false, parentJob.createdAt, parentJob.queue, runAt);
|
|
1631
|
+
this.jobIndex.set(parentId, {
|
|
1632
|
+
type: 'queue',
|
|
1633
|
+
shardIdx: parentIdx,
|
|
1634
|
+
queueName: parentJob.queue,
|
|
1635
|
+
});
|
|
1506
1636
|
shard.notify(parentJob.queue);
|
|
1507
1637
|
promoted = true;
|
|
1508
1638
|
}
|
|
1509
|
-
|
|
1639
|
+
removed = true;
|
|
1640
|
+
}
|
|
1641
|
+
finally {
|
|
1642
|
+
for (let index = guards.length - 1; index >= 0; index--)
|
|
1643
|
+
guards[index].release();
|
|
1644
|
+
}
|
|
1510
1645
|
if (promoted) {
|
|
1511
1646
|
this.eventsManager.broadcast({
|
|
1512
1647
|
eventType: 'waiting',
|
|
@@ -1516,7 +1651,7 @@ export class QueueManager {
|
|
|
1516
1651
|
prev: 'waiting-children',
|
|
1517
1652
|
});
|
|
1518
1653
|
}
|
|
1519
|
-
return
|
|
1654
|
+
return removed;
|
|
1520
1655
|
}
|
|
1521
1656
|
/**
|
|
1522
1657
|
* Remove all unprocessed (waiting/delayed) children of a parent job.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const CLI_NETWORK_COMMANDS: readonly ("push" | "queue" | "dlq" | "job" | "cron" | "worker" | "concurrency" | "ping" | "stats" | "metrics" | "pull" | "ack" | "
|
|
1
|
+
export declare const CLI_NETWORK_COMMANDS: readonly ("push" | "fail" | "queue" | "dlq" | "job" | "cron" | "worker" | "concurrency" | "ping" | "stats" | "metrics" | "pull" | "ack" | "rate-limit" | "health" | "webhook")[];
|
|
2
2
|
/** Build the protocol command for a routed network CLI command. */
|
|
3
3
|
export declare function buildCommand(command: string, args: string[]): Promise<Record<string, unknown> | null>;
|
package/dist/client/flow.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export declare class FlowProducer extends EventEmitter {
|
|
|
34
34
|
private readonly embedded;
|
|
35
35
|
private readonly tcp;
|
|
36
36
|
private readonly useSharedPool;
|
|
37
|
+
private closed;
|
|
37
38
|
constructor(opts?: FlowProducerOptions);
|
|
38
39
|
/** Get push context for helper functions */
|
|
39
40
|
private get pushCtx();
|
|
@@ -70,10 +71,6 @@ export declare class FlowProducer extends EventEmitter {
|
|
|
70
71
|
getParentResults<R = unknown>(parentIds: string[]): Map<string, R>;
|
|
71
72
|
/** Build callbacks that wire flow job methods to actual management operations */
|
|
72
73
|
private buildCallbacks;
|
|
73
|
-
private
|
|
74
|
-
private
|
|
75
|
-
private buildJobNode;
|
|
76
|
-
private buildJobNodeFromTcp;
|
|
77
|
-
private addFlowNode;
|
|
78
|
-
private addTreeNode;
|
|
74
|
+
private buildPlannedNode;
|
|
75
|
+
private indexSnapshots;
|
|
79
76
|
}
|