@medusajs/orchestration 3.0.0-preview-20250410180148 → 3.0.0-preview-20251201152819
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/dist/joiner/remote-joiner.d.ts +3 -2
- package/dist/joiner/remote-joiner.d.ts.map +1 -1
- package/dist/joiner/remote-joiner.js +312 -141
- package/dist/joiner/remote-joiner.js.map +1 -1
- package/dist/transaction/datastore/abstract-storage.d.ts +7 -5
- package/dist/transaction/datastore/abstract-storage.d.ts.map +1 -1
- package/dist/transaction/datastore/abstract-storage.js +3 -3
- package/dist/transaction/datastore/abstract-storage.js.map +1 -1
- package/dist/transaction/datastore/base-in-memory-storage.d.ts +2 -1
- package/dist/transaction/datastore/base-in-memory-storage.d.ts.map +1 -1
- package/dist/transaction/datastore/base-in-memory-storage.js +2 -0
- package/dist/transaction/datastore/base-in-memory-storage.js.map +1 -1
- package/dist/transaction/distributed-transaction.d.ts +23 -6
- package/dist/transaction/distributed-transaction.d.ts.map +1 -1
- package/dist/transaction/distributed-transaction.js +284 -54
- package/dist/transaction/distributed-transaction.js.map +1 -1
- package/dist/transaction/errors.d.ts +11 -0
- package/dist/transaction/errors.d.ts.map +1 -1
- package/dist/transaction/errors.js +34 -2
- package/dist/transaction/errors.js.map +1 -1
- package/dist/transaction/transaction-orchestrator.d.ts +99 -10
- package/dist/transaction/transaction-orchestrator.d.ts.map +1 -1
- package/dist/transaction/transaction-orchestrator.js +599 -274
- package/dist/transaction/transaction-orchestrator.js.map +1 -1
- package/dist/transaction/transaction-step.d.ts +2 -0
- package/dist/transaction/transaction-step.d.ts.map +1 -1
- package/dist/transaction/transaction-step.js +10 -3
- package/dist/transaction/transaction-step.js.map +1 -1
- package/dist/transaction/types.d.ts +31 -1
- package/dist/transaction/types.d.ts.map +1 -1
- package/dist/transaction/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/workflow/global-workflow.d.ts.map +1 -1
- package/dist/workflow/global-workflow.js +16 -4
- package/dist/workflow/global-workflow.js.map +1 -1
- package/dist/workflow/local-workflow.d.ts +3 -1
- package/dist/workflow/local-workflow.d.ts.map +1 -1
- package/dist/workflow/local-workflow.js +71 -9
- package/dist/workflow/local-workflow.js.map +1 -1
- package/package.json +8 -26
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TransactionOrchestrator = void 0;
|
|
4
|
+
const ulid_1 = require("ulid");
|
|
4
5
|
const distributed_transaction_1 = require("./distributed-transaction");
|
|
5
6
|
const transaction_step_1 = require("./transaction-step");
|
|
6
7
|
const types_1 = require("./types");
|
|
7
8
|
const utils_1 = require("@medusajs/utils");
|
|
8
9
|
const events_1 = require("events");
|
|
9
10
|
const errors_1 = require("./errors");
|
|
11
|
+
const canMoveForwardStates = new Set([
|
|
12
|
+
utils_1.TransactionStepState.DONE,
|
|
13
|
+
utils_1.TransactionStepState.FAILED,
|
|
14
|
+
utils_1.TransactionStepState.TIMEOUT,
|
|
15
|
+
utils_1.TransactionStepState.SKIPPED,
|
|
16
|
+
utils_1.TransactionStepState.SKIPPED_FAILURE,
|
|
17
|
+
]);
|
|
18
|
+
const canMoveBackwardStates = new Set([
|
|
19
|
+
utils_1.TransactionStepState.DONE,
|
|
20
|
+
utils_1.TransactionStepState.REVERTED,
|
|
21
|
+
utils_1.TransactionStepState.FAILED,
|
|
22
|
+
utils_1.TransactionStepState.DORMANT,
|
|
23
|
+
utils_1.TransactionStepState.SKIPPED,
|
|
24
|
+
]);
|
|
25
|
+
const flagStepsToRevertStates = new Set([
|
|
26
|
+
utils_1.TransactionStepState.DONE,
|
|
27
|
+
utils_1.TransactionStepState.TIMEOUT,
|
|
28
|
+
]);
|
|
29
|
+
const setStepTimeoutSkipStates = new Set([
|
|
30
|
+
utils_1.TransactionStepState.TIMEOUT,
|
|
31
|
+
utils_1.TransactionStepState.DONE,
|
|
32
|
+
utils_1.TransactionStepState.REVERTED,
|
|
33
|
+
]);
|
|
10
34
|
/**
|
|
11
35
|
* @class TransactionOrchestrator is responsible for managing and executing distributed transactions.
|
|
12
36
|
* It is based on a single transaction definition, which is used to execute all the transaction steps
|
|
@@ -26,6 +50,11 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
26
50
|
this.parseFlowOptions();
|
|
27
51
|
}
|
|
28
52
|
}
|
|
53
|
+
static isExpectedError(error) {
|
|
54
|
+
return (errors_1.SkipCancelledExecutionError.isSkipCancelledExecutionError(error) ||
|
|
55
|
+
errors_1.SkipExecutionError.isSkipExecutionError(error) ||
|
|
56
|
+
errors_1.SkipStepAlreadyFinishedError.isSkipStepAlreadyFinishedError(error));
|
|
57
|
+
}
|
|
29
58
|
static clone(orchestrator) {
|
|
30
59
|
return new TransactionOrchestrator({
|
|
31
60
|
id: orchestrator.id,
|
|
@@ -37,7 +66,7 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
37
66
|
static getKeyName(...params) {
|
|
38
67
|
return params.join(this.SEPARATOR);
|
|
39
68
|
}
|
|
40
|
-
getPreviousStep(flow, step) {
|
|
69
|
+
static getPreviousStep(flow, step) {
|
|
41
70
|
const id = step.id.split(".");
|
|
42
71
|
id.pop();
|
|
43
72
|
const parentId = id.join(".");
|
|
@@ -64,36 +93,26 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
64
93
|
this.compensateSteps = steps;
|
|
65
94
|
return steps;
|
|
66
95
|
}
|
|
96
|
+
static countSiblings(flow, step) {
|
|
97
|
+
const previous = TransactionOrchestrator.getPreviousStep(flow, step);
|
|
98
|
+
return previous.next.length;
|
|
99
|
+
}
|
|
67
100
|
canMoveForward(flow, previousStep) {
|
|
68
|
-
const
|
|
69
|
-
utils_1.TransactionStepState.DONE,
|
|
70
|
-
utils_1.TransactionStepState.FAILED,
|
|
71
|
-
utils_1.TransactionStepState.TIMEOUT,
|
|
72
|
-
utils_1.TransactionStepState.SKIPPED,
|
|
73
|
-
utils_1.TransactionStepState.SKIPPED_FAILURE,
|
|
74
|
-
];
|
|
75
|
-
const siblings = this.getPreviousStep(flow, previousStep).next.map((sib) => flow.steps[sib]);
|
|
101
|
+
const siblings = TransactionOrchestrator.getPreviousStep(flow, previousStep).next.map((sib) => flow.steps[sib]);
|
|
76
102
|
return (!!previousStep.definition.noWait ||
|
|
77
|
-
siblings.every((sib) =>
|
|
103
|
+
siblings.every((sib) => canMoveForwardStates.has(sib.invoke.state)));
|
|
78
104
|
}
|
|
79
105
|
canMoveBackward(flow, step) {
|
|
80
|
-
const states = [
|
|
81
|
-
utils_1.TransactionStepState.DONE,
|
|
82
|
-
utils_1.TransactionStepState.REVERTED,
|
|
83
|
-
utils_1.TransactionStepState.FAILED,
|
|
84
|
-
utils_1.TransactionStepState.DORMANT,
|
|
85
|
-
utils_1.TransactionStepState.SKIPPED,
|
|
86
|
-
];
|
|
87
106
|
const siblings = step.next.map((sib) => flow.steps[sib]);
|
|
88
107
|
return (siblings.length === 0 ||
|
|
89
|
-
siblings.every((sib) =>
|
|
108
|
+
siblings.every((sib) => canMoveBackwardStates.has(sib.compensate.state)));
|
|
90
109
|
}
|
|
91
110
|
canContinue(flow, step) {
|
|
92
111
|
if (flow.state == types_1.TransactionState.COMPENSATING) {
|
|
93
112
|
return this.canMoveBackward(flow, step);
|
|
94
113
|
}
|
|
95
114
|
else {
|
|
96
|
-
const previous =
|
|
115
|
+
const previous = TransactionOrchestrator.getPreviousStep(flow, step);
|
|
97
116
|
if (previous.id === TransactionOrchestrator.ROOT_STEP) {
|
|
98
117
|
return true;
|
|
99
118
|
}
|
|
@@ -139,6 +158,46 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
139
158
|
return hasTimedOut;
|
|
140
159
|
}
|
|
141
160
|
async checkAllSteps(transaction) {
|
|
161
|
+
const flow = transaction.getFlow();
|
|
162
|
+
const result = await this.computeCurrentTransactionState(transaction);
|
|
163
|
+
// Handle state transitions and emit events
|
|
164
|
+
if (flow.state === types_1.TransactionState.WAITING_TO_COMPENSATE &&
|
|
165
|
+
result.next.length === 0 &&
|
|
166
|
+
!flow.hasWaitingSteps) {
|
|
167
|
+
flow.state = types_1.TransactionState.COMPENSATING;
|
|
168
|
+
this.flagStepsToRevert(flow);
|
|
169
|
+
this.emit(types_1.DistributedTransactionEvent.COMPENSATE_BEGIN, { transaction });
|
|
170
|
+
const result = await this.checkAllSteps(transaction);
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
else if (result.completed === result.total) {
|
|
174
|
+
if (result.hasSkippedOnFailure) {
|
|
175
|
+
flow.hasSkippedOnFailureSteps = true;
|
|
176
|
+
}
|
|
177
|
+
if (result.hasSkipped) {
|
|
178
|
+
flow.hasSkippedSteps = true;
|
|
179
|
+
}
|
|
180
|
+
if (result.hasIgnoredFailure) {
|
|
181
|
+
flow.hasFailedSteps = true;
|
|
182
|
+
}
|
|
183
|
+
if (result.hasFailed) {
|
|
184
|
+
flow.state = types_1.TransactionState.FAILED;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
flow.state = result.hasReverted
|
|
188
|
+
? types_1.TransactionState.REVERTED
|
|
189
|
+
: types_1.TransactionState.DONE;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
current: result.current,
|
|
194
|
+
next: result.next,
|
|
195
|
+
total: result.total,
|
|
196
|
+
remaining: result.total - result.completed,
|
|
197
|
+
completed: result.completed,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async computeCurrentTransactionState(transaction) {
|
|
142
201
|
let hasSkipped = false;
|
|
143
202
|
let hasSkippedOnFailure = false;
|
|
144
203
|
let hasIgnoredFailure = false;
|
|
@@ -177,9 +236,20 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
177
236
|
await transaction.scheduleRetry(stepDef, stepDef.definition.retryIntervalAwaiting);
|
|
178
237
|
}
|
|
179
238
|
}
|
|
239
|
+
else if (stepDef.retryRescheduledAt) {
|
|
240
|
+
// The step is not configured for awaiting retry but is manually force to retry
|
|
241
|
+
stepDef.retryRescheduledAt = null;
|
|
242
|
+
nextSteps.push(stepDef);
|
|
243
|
+
}
|
|
180
244
|
continue;
|
|
181
245
|
}
|
|
182
246
|
else if (curState.status === types_1.TransactionStepStatus.TEMPORARY_FAILURE) {
|
|
247
|
+
if (!stepDef.temporaryFailedAt &&
|
|
248
|
+
stepDef.definition.autoRetry === false) {
|
|
249
|
+
stepDef.temporaryFailedAt = Date.now();
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
stepDef.temporaryFailedAt = null;
|
|
183
253
|
currentSteps.push(stepDef);
|
|
184
254
|
if (!stepDef.canRetry()) {
|
|
185
255
|
if (stepDef.hasRetryInterval() && !stepDef.retryRescheduledAt) {
|
|
@@ -206,7 +276,8 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
206
276
|
hasReverted = true;
|
|
207
277
|
}
|
|
208
278
|
else if (curState.state === utils_1.TransactionStepState.FAILED) {
|
|
209
|
-
if (stepDef.definition.continueOnPermanentFailure
|
|
279
|
+
if (stepDef.definition.continueOnPermanentFailure ||
|
|
280
|
+
stepDef.definition.skipOnPermanentFailure) {
|
|
210
281
|
hasIgnoredFailure = true;
|
|
211
282
|
}
|
|
212
283
|
else {
|
|
@@ -217,40 +288,17 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
217
288
|
}
|
|
218
289
|
flow.hasWaitingSteps = hasWaiting;
|
|
219
290
|
flow.hasRevertedSteps = hasReverted;
|
|
220
|
-
const totalSteps = allSteps.length - 1;
|
|
221
|
-
if (flow.state === types_1.TransactionState.WAITING_TO_COMPENSATE &&
|
|
222
|
-
nextSteps.length === 0 &&
|
|
223
|
-
!hasWaiting) {
|
|
224
|
-
flow.state = types_1.TransactionState.COMPENSATING;
|
|
225
|
-
this.flagStepsToRevert(flow);
|
|
226
|
-
this.emit(types_1.DistributedTransactionEvent.COMPENSATE_BEGIN, { transaction });
|
|
227
|
-
return await this.checkAllSteps(transaction);
|
|
228
|
-
}
|
|
229
|
-
else if (completedSteps === totalSteps) {
|
|
230
|
-
if (hasSkippedOnFailure) {
|
|
231
|
-
flow.hasSkippedOnFailureSteps = true;
|
|
232
|
-
}
|
|
233
|
-
if (hasSkipped) {
|
|
234
|
-
flow.hasSkippedSteps = true;
|
|
235
|
-
}
|
|
236
|
-
if (hasIgnoredFailure) {
|
|
237
|
-
flow.hasFailedSteps = true;
|
|
238
|
-
}
|
|
239
|
-
if (hasFailed) {
|
|
240
|
-
flow.state = types_1.TransactionState.FAILED;
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
flow.state = hasReverted
|
|
244
|
-
? types_1.TransactionState.REVERTED
|
|
245
|
-
: types_1.TransactionState.DONE;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
291
|
return {
|
|
249
292
|
current: currentSteps,
|
|
250
293
|
next: nextSteps,
|
|
251
|
-
total:
|
|
252
|
-
remaining: totalSteps - completedSteps,
|
|
294
|
+
total: allSteps.length - 1,
|
|
253
295
|
completed: completedSteps,
|
|
296
|
+
hasSkipped,
|
|
297
|
+
hasSkippedOnFailure,
|
|
298
|
+
hasIgnoredFailure,
|
|
299
|
+
hasFailed,
|
|
300
|
+
hasWaiting,
|
|
301
|
+
hasReverted,
|
|
254
302
|
};
|
|
255
303
|
}
|
|
256
304
|
flagStepsToRevert(flow) {
|
|
@@ -260,7 +308,11 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
260
308
|
}
|
|
261
309
|
const stepDef = flow.steps[step];
|
|
262
310
|
const curState = stepDef.getStates();
|
|
263
|
-
if (
|
|
311
|
+
if (stepDef._v) {
|
|
312
|
+
flow._v = 0;
|
|
313
|
+
stepDef._v = 0;
|
|
314
|
+
}
|
|
315
|
+
if (flagStepsToRevertStates.has(curState.state) ||
|
|
264
316
|
curState.status === types_1.TransactionStepStatus.PERMANENT_FAILURE) {
|
|
265
317
|
stepDef.beginCompensation();
|
|
266
318
|
stepDef.changeState(utils_1.TransactionStepState.NOT_STARTED);
|
|
@@ -284,11 +336,21 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
284
336
|
step.changeState(utils_1.TransactionStepState.DONE);
|
|
285
337
|
}
|
|
286
338
|
let shouldEmit = true;
|
|
339
|
+
let transactionIsCancelling = false;
|
|
287
340
|
try {
|
|
288
|
-
await transaction.saveCheckpoint(
|
|
341
|
+
await transaction.saveCheckpoint({
|
|
342
|
+
_v: step._v,
|
|
343
|
+
parallelSteps: TransactionOrchestrator.countSiblings(transaction.getFlow(), step),
|
|
344
|
+
stepId: step.id,
|
|
345
|
+
});
|
|
289
346
|
}
|
|
290
347
|
catch (error) {
|
|
291
|
-
|
|
348
|
+
if (!TransactionOrchestrator.isExpectedError(error)) {
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
transactionIsCancelling =
|
|
352
|
+
errors_1.SkipCancelledExecutionError.isSkipCancelledExecutionError(error);
|
|
353
|
+
shouldEmit = !errors_1.SkipExecutionError.isSkipExecutionError(error);
|
|
292
354
|
}
|
|
293
355
|
const cleaningUp = [];
|
|
294
356
|
if (step.hasRetryScheduled()) {
|
|
@@ -297,7 +359,9 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
297
359
|
if (step.hasTimeout()) {
|
|
298
360
|
cleaningUp.push(transaction.clearStepTimeout(step));
|
|
299
361
|
}
|
|
300
|
-
|
|
362
|
+
if (cleaningUp.length) {
|
|
363
|
+
await (0, utils_1.promiseAll)(cleaningUp);
|
|
364
|
+
}
|
|
301
365
|
if (shouldEmit) {
|
|
302
366
|
const eventName = step.isCompensating()
|
|
303
367
|
? types_1.DistributedTransactionEvent.COMPENSATE_STEP_SUCCESS
|
|
@@ -306,25 +370,53 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
306
370
|
}
|
|
307
371
|
return {
|
|
308
372
|
stopExecution: !shouldEmit,
|
|
373
|
+
transactionIsCancelling,
|
|
309
374
|
};
|
|
310
375
|
}
|
|
311
|
-
static async
|
|
376
|
+
static async retryStep(transaction, step) {
|
|
377
|
+
if (!step.retryRescheduledAt) {
|
|
378
|
+
step.hasScheduledRetry = true;
|
|
379
|
+
step.retryRescheduledAt = Date.now();
|
|
380
|
+
}
|
|
381
|
+
transaction.getFlow().hasWaitingSteps = true;
|
|
382
|
+
try {
|
|
383
|
+
await transaction.saveCheckpoint({
|
|
384
|
+
_v: step._v,
|
|
385
|
+
parallelSteps: TransactionOrchestrator.countSiblings(transaction.getFlow(), step),
|
|
386
|
+
stepId: step.id,
|
|
387
|
+
});
|
|
388
|
+
await transaction.scheduleRetry(step, 0);
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
if (!TransactionOrchestrator.isExpectedError(error)) {
|
|
392
|
+
throw error;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
static async skipStep({ transaction, step, }) {
|
|
312
397
|
const hasStepTimedOut = step.getStates().state === utils_1.TransactionStepState.TIMEOUT;
|
|
313
398
|
if (!hasStepTimedOut) {
|
|
314
399
|
step.changeStatus(types_1.TransactionStepStatus.OK);
|
|
315
400
|
step.changeState(utils_1.TransactionStepState.SKIPPED);
|
|
316
401
|
}
|
|
317
402
|
let shouldEmit = true;
|
|
403
|
+
let transactionIsCancelling = false;
|
|
318
404
|
try {
|
|
319
|
-
await transaction.saveCheckpoint(
|
|
405
|
+
await transaction.saveCheckpoint({
|
|
406
|
+
_v: step._v,
|
|
407
|
+
parallelSteps: TransactionOrchestrator.countSiblings(transaction.getFlow(), step),
|
|
408
|
+
stepId: step.id,
|
|
409
|
+
});
|
|
320
410
|
}
|
|
321
411
|
catch (error) {
|
|
412
|
+
if (!TransactionOrchestrator.isExpectedError(error)) {
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
transactionIsCancelling =
|
|
416
|
+
errors_1.SkipCancelledExecutionError.isSkipCancelledExecutionError(error);
|
|
322
417
|
if (errors_1.SkipExecutionError.isSkipExecutionError(error)) {
|
|
323
418
|
shouldEmit = false;
|
|
324
419
|
}
|
|
325
|
-
else {
|
|
326
|
-
throw error;
|
|
327
|
-
}
|
|
328
420
|
}
|
|
329
421
|
const cleaningUp = [];
|
|
330
422
|
if (step.hasRetryScheduled()) {
|
|
@@ -333,21 +425,20 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
333
425
|
if (step.hasTimeout()) {
|
|
334
426
|
cleaningUp.push(transaction.clearStepTimeout(step));
|
|
335
427
|
}
|
|
336
|
-
|
|
428
|
+
if (cleaningUp.length) {
|
|
429
|
+
await (0, utils_1.promiseAll)(cleaningUp);
|
|
430
|
+
}
|
|
337
431
|
if (shouldEmit) {
|
|
338
432
|
const eventName = types_1.DistributedTransactionEvent.STEP_SKIPPED;
|
|
339
433
|
transaction.emit(eventName, { step, transaction });
|
|
340
434
|
}
|
|
341
435
|
return {
|
|
342
436
|
stopExecution: !shouldEmit,
|
|
437
|
+
transactionIsCancelling,
|
|
343
438
|
};
|
|
344
439
|
}
|
|
345
440
|
static async setStepTimeout(transaction, step, error) {
|
|
346
|
-
if (
|
|
347
|
-
utils_1.TransactionStepState.TIMEOUT,
|
|
348
|
-
utils_1.TransactionStepState.DONE,
|
|
349
|
-
utils_1.TransactionStepState.REVERTED,
|
|
350
|
-
].includes(step.getStates().state)) {
|
|
441
|
+
if (setStepTimeoutSkipStates.has(step.getStates().state)) {
|
|
351
442
|
return;
|
|
352
443
|
}
|
|
353
444
|
step.changeState(utils_1.TransactionStepState.TIMEOUT);
|
|
@@ -365,15 +456,30 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
365
456
|
await transaction.clearStepTimeout(step);
|
|
366
457
|
}
|
|
367
458
|
static async setStepFailure(transaction, step, error, maxRetries = TransactionOrchestrator.DEFAULT_RETRIES, isTimeout = false, timeoutError) {
|
|
459
|
+
const result = {
|
|
460
|
+
stopExecution: false,
|
|
461
|
+
transactionIsCancelling: false,
|
|
462
|
+
};
|
|
368
463
|
if (errors_1.SkipExecutionError.isSkipExecutionError(error)) {
|
|
369
|
-
return
|
|
370
|
-
stopExecution: false,
|
|
371
|
-
};
|
|
464
|
+
return result;
|
|
372
465
|
}
|
|
373
466
|
step.failures++;
|
|
374
467
|
if ((0, utils_1.isErrorLike)(error)) {
|
|
375
468
|
error = (0, utils_1.serializeError)(error);
|
|
376
469
|
}
|
|
470
|
+
else {
|
|
471
|
+
try {
|
|
472
|
+
const serialized = JSON.stringify(error);
|
|
473
|
+
error = error?.message
|
|
474
|
+
? JSON.parse(serialized)
|
|
475
|
+
: { message: serialized };
|
|
476
|
+
}
|
|
477
|
+
catch (e) {
|
|
478
|
+
error = {
|
|
479
|
+
message: "Unknown non-serializable error",
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
377
483
|
if (!isTimeout &&
|
|
378
484
|
step.getStates().status !== types_1.TransactionStepStatus.PERMANENT_FAILURE) {
|
|
379
485
|
step.changeStatus(types_1.TransactionStepStatus.TEMPORARY_FAILURE);
|
|
@@ -390,23 +496,35 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
390
496
|
const handlerType = step.isCompensating()
|
|
391
497
|
? types_1.TransactionHandlerType.COMPENSATE
|
|
392
498
|
: types_1.TransactionHandlerType.INVOKE;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
499
|
+
error.stack ??= "";
|
|
500
|
+
const workflowId = transaction.modelId;
|
|
501
|
+
const stepAction = step.definition.action;
|
|
502
|
+
const sourcePath = transaction.getFlow().metadata?.sourcePath;
|
|
503
|
+
const sourceStack = sourcePath
|
|
504
|
+
? `\n⮑ \sat ${sourcePath}: [${workflowId} -> ${stepAction} (${types_1.TransactionHandlerType.INVOKE})]`
|
|
505
|
+
: `\n⮑ \sat [${workflowId} -> ${stepAction} (${types_1.TransactionHandlerType.INVOKE})]`;
|
|
506
|
+
error.stack += sourceStack;
|
|
402
507
|
transaction.addError(step.definition.action, handlerType, error);
|
|
403
508
|
}
|
|
404
509
|
if (!step.isCompensating()) {
|
|
405
|
-
if (step.definition.continueOnPermanentFailure
|
|
510
|
+
if ((step.definition.continueOnPermanentFailure ||
|
|
511
|
+
step.definition.skipOnPermanentFailure) &&
|
|
406
512
|
!errors_1.TransactionTimeoutError.isTransactionTimeoutError(timeoutError)) {
|
|
407
|
-
|
|
408
|
-
const
|
|
409
|
-
|
|
513
|
+
if (step.definition.skipOnPermanentFailure) {
|
|
514
|
+
const until = (0, utils_1.isString)(step.definition.skipOnPermanentFailure)
|
|
515
|
+
? step.definition.skipOnPermanentFailure
|
|
516
|
+
: undefined;
|
|
517
|
+
let stepsToSkip = [...step.next];
|
|
518
|
+
while (stepsToSkip.length > 0) {
|
|
519
|
+
const currentStep = flow.steps[stepsToSkip.shift()];
|
|
520
|
+
if (until && currentStep.definition.action === until) {
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
currentStep.changeState(utils_1.TransactionStepState.SKIPPED_FAILURE);
|
|
524
|
+
if (currentStep.next?.length > 0) {
|
|
525
|
+
stepsToSkip = stepsToSkip.concat(currentStep.next);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
410
528
|
}
|
|
411
529
|
}
|
|
412
530
|
else {
|
|
@@ -417,30 +535,49 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
417
535
|
cleaningUp.push(transaction.clearStepTimeout(step));
|
|
418
536
|
}
|
|
419
537
|
}
|
|
420
|
-
|
|
538
|
+
else {
|
|
539
|
+
const isAsync = step.isCompensating()
|
|
540
|
+
? step.definition.compensateAsync
|
|
541
|
+
: step.definition.async;
|
|
542
|
+
if (step.getStates().status === types_1.TransactionStepStatus.TEMPORARY_FAILURE &&
|
|
543
|
+
step.definition.autoRetry === false &&
|
|
544
|
+
isAsync) {
|
|
545
|
+
step.temporaryFailedAt = Date.now();
|
|
546
|
+
result.stopExecution = true;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
421
549
|
try {
|
|
422
|
-
await transaction.saveCheckpoint(
|
|
550
|
+
await transaction.saveCheckpoint({
|
|
551
|
+
_v: step._v,
|
|
552
|
+
parallelSteps: TransactionOrchestrator.countSiblings(transaction.getFlow(), step),
|
|
553
|
+
stepId: step.id,
|
|
554
|
+
});
|
|
423
555
|
}
|
|
424
556
|
catch (error) {
|
|
425
|
-
if (
|
|
426
|
-
shouldEmit = false;
|
|
427
|
-
}
|
|
428
|
-
else {
|
|
557
|
+
if (!TransactionOrchestrator.isExpectedError(error)) {
|
|
429
558
|
throw error;
|
|
430
559
|
}
|
|
560
|
+
result.transactionIsCancelling =
|
|
561
|
+
errors_1.SkipCancelledExecutionError.isSkipCancelledExecutionError(error);
|
|
562
|
+
if (errors_1.SkipExecutionError.isSkipExecutionError(error)) {
|
|
563
|
+
result.stopExecution = true;
|
|
564
|
+
}
|
|
431
565
|
}
|
|
432
566
|
if (step.hasRetryScheduled()) {
|
|
433
567
|
cleaningUp.push(transaction.clearRetry(step));
|
|
434
568
|
}
|
|
435
|
-
|
|
436
|
-
|
|
569
|
+
if (cleaningUp.length) {
|
|
570
|
+
await (0, utils_1.promiseAll)(cleaningUp);
|
|
571
|
+
}
|
|
572
|
+
if (!result.stopExecution) {
|
|
437
573
|
const eventName = step.isCompensating()
|
|
438
574
|
? types_1.DistributedTransactionEvent.COMPENSATE_STEP_FAILURE
|
|
439
575
|
: types_1.DistributedTransactionEvent.STEP_FAILURE;
|
|
440
576
|
transaction.emit(eventName, { step, transaction });
|
|
441
577
|
}
|
|
442
578
|
return {
|
|
443
|
-
stopExecution:
|
|
579
|
+
stopExecution: result.stopExecution,
|
|
580
|
+
transactionIsCancelling: result.transactionIsCancelling,
|
|
444
581
|
};
|
|
445
582
|
}
|
|
446
583
|
async executeNext(transaction) {
|
|
@@ -450,50 +587,49 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
450
587
|
return;
|
|
451
588
|
}
|
|
452
589
|
const flow = transaction.getFlow();
|
|
453
|
-
|
|
454
|
-
const execution = [];
|
|
590
|
+
let nextSteps = await this.checkAllSteps(transaction);
|
|
455
591
|
const hasTimedOut = await this.checkTransactionTimeout(transaction, nextSteps.current);
|
|
456
592
|
if (hasTimedOut) {
|
|
457
593
|
continue;
|
|
458
594
|
}
|
|
459
595
|
if (nextSteps.remaining === 0) {
|
|
460
|
-
|
|
461
|
-
|
|
596
|
+
await this.finalizeTransaction(transaction);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const stepsShouldContinueExecution = nextSteps.next.map((step) => {
|
|
600
|
+
const { shouldContinueExecution } = this.prepareStepForExecution(step, flow);
|
|
601
|
+
return shouldContinueExecution;
|
|
602
|
+
});
|
|
603
|
+
let asyncStepCount = 0;
|
|
604
|
+
for (const s of nextSteps.next) {
|
|
605
|
+
const stepIsAsync = s.isCompensating()
|
|
606
|
+
? s.definition.compensateAsync
|
|
607
|
+
: s.definition.async;
|
|
608
|
+
if (stepIsAsync)
|
|
609
|
+
asyncStepCount++;
|
|
610
|
+
}
|
|
611
|
+
const hasMultipleAsyncSteps = asyncStepCount > 1;
|
|
612
|
+
const hasAsyncSteps = !!asyncStepCount;
|
|
613
|
+
// If there is any async step, we don't need to save the checkpoint here as it will be saved
|
|
614
|
+
// later down there
|
|
615
|
+
await transaction.saveCheckpoint().catch((error) => {
|
|
616
|
+
if (TransactionOrchestrator.isExpectedError(error)) {
|
|
617
|
+
continueExecution = false;
|
|
618
|
+
return;
|
|
462
619
|
}
|
|
463
|
-
|
|
464
|
-
|
|
620
|
+
throw error;
|
|
621
|
+
});
|
|
622
|
+
if (!continueExecution) {
|
|
623
|
+
break;
|
|
465
624
|
}
|
|
625
|
+
const execution = [];
|
|
626
|
+
const executionAsync = [];
|
|
627
|
+
let i = 0;
|
|
466
628
|
for (const step of nextSteps.next) {
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
: types_1.TransactionHandlerType.INVOKE;
|
|
471
|
-
step.lastAttempt = Date.now();
|
|
472
|
-
step.attempts++;
|
|
473
|
-
if (curState.state === utils_1.TransactionStepState.NOT_STARTED) {
|
|
474
|
-
if (!step.startedAt) {
|
|
475
|
-
step.startedAt = Date.now();
|
|
476
|
-
}
|
|
477
|
-
if (step.isCompensating()) {
|
|
478
|
-
step.changeState(utils_1.TransactionStepState.COMPENSATING);
|
|
479
|
-
if (step.definition.noCompensation) {
|
|
480
|
-
step.changeState(utils_1.TransactionStepState.REVERTED);
|
|
481
|
-
continue;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
else if (flow.state === types_1.TransactionState.INVOKING) {
|
|
485
|
-
step.changeState(utils_1.TransactionStepState.INVOKING);
|
|
486
|
-
}
|
|
629
|
+
const stepIndex = i++;
|
|
630
|
+
if (!stepsShouldContinueExecution[stepIndex]) {
|
|
631
|
+
continue;
|
|
487
632
|
}
|
|
488
|
-
step.changeStatus(types_1.TransactionStepStatus.WAITING);
|
|
489
|
-
const payload = new distributed_transaction_1.TransactionPayload({
|
|
490
|
-
model_id: flow.modelId,
|
|
491
|
-
idempotency_key: TransactionOrchestrator.getKeyName(flow.modelId, flow.transactionId, step.definition.action, type),
|
|
492
|
-
action: step.definition.action + "",
|
|
493
|
-
action_type: type,
|
|
494
|
-
attempt: step.attempts,
|
|
495
|
-
timestamp: Date.now(),
|
|
496
|
-
}, transaction.payload, transaction.getContext());
|
|
497
633
|
if (step.hasTimeout() && !step.timedOutAt && step.attempts === 1) {
|
|
498
634
|
await transaction.scheduleStepTimeout(step, step.definition.timeout);
|
|
499
635
|
}
|
|
@@ -504,145 +640,262 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
504
640
|
const isAsync = step.isCompensating()
|
|
505
641
|
? step.definition.compensateAsync
|
|
506
642
|
: step.definition.async;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
await transaction.scheduleRetry(step, 0);
|
|
516
|
-
}
|
|
517
|
-
return ret;
|
|
518
|
-
};
|
|
519
|
-
const traceData = {
|
|
520
|
-
action: step.definition.action + "",
|
|
521
|
-
type,
|
|
522
|
-
step_id: step.id,
|
|
523
|
-
step_uuid: step.uuid + "",
|
|
524
|
-
attempts: step.attempts,
|
|
525
|
-
failures: step.failures,
|
|
526
|
-
async: !!(type === "invoke"
|
|
527
|
-
? step.definition.async
|
|
528
|
-
: step.definition.compensateAsync),
|
|
529
|
-
idempotency_key: payload.metadata.idempotency_key,
|
|
530
|
-
};
|
|
531
|
-
const handlerArgs = [
|
|
532
|
-
step.definition.action + "",
|
|
533
|
-
type,
|
|
534
|
-
payload,
|
|
535
|
-
transaction,
|
|
536
|
-
step,
|
|
537
|
-
this,
|
|
538
|
-
];
|
|
643
|
+
// Compute current transaction state
|
|
644
|
+
await this.computeCurrentTransactionState(transaction);
|
|
645
|
+
const promise = this.createStepExecutionPromise(transaction, step);
|
|
646
|
+
const hasVersionControl = hasMultipleAsyncSteps || step.hasAwaitingRetry();
|
|
647
|
+
if (hasVersionControl && !step._v) {
|
|
648
|
+
transaction.getFlow()._v += 1;
|
|
649
|
+
step._v = transaction.getFlow()._v;
|
|
650
|
+
}
|
|
539
651
|
if (!isAsync) {
|
|
540
|
-
|
|
541
|
-
return await transaction.handler(...handlerArgs);
|
|
542
|
-
};
|
|
543
|
-
let promise;
|
|
544
|
-
if (TransactionOrchestrator.traceStep) {
|
|
545
|
-
promise = TransactionOrchestrator.traceStep(stepHandler, traceData);
|
|
546
|
-
}
|
|
547
|
-
else {
|
|
548
|
-
promise = stepHandler();
|
|
549
|
-
}
|
|
550
|
-
execution.push(promise
|
|
551
|
-
.then(async (response) => {
|
|
552
|
-
if (this.hasExpired({ transaction, step }, Date.now())) {
|
|
553
|
-
await this.checkStepTimeout(transaction, step);
|
|
554
|
-
await this.checkTransactionTimeout(transaction, nextSteps.next.includes(step) ? nextSteps.next : [step]);
|
|
555
|
-
}
|
|
556
|
-
const output = response?.__type ? response.output : response;
|
|
557
|
-
if (errors_1.SkipStepResponse.isSkipStepResponse(output)) {
|
|
558
|
-
await TransactionOrchestrator.skipStep(transaction, step);
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
await TransactionOrchestrator.setStepSuccess(transaction, step, response);
|
|
562
|
-
})
|
|
563
|
-
.catch(async (error) => {
|
|
564
|
-
const response = error?.getStepResponse?.();
|
|
565
|
-
if (this.hasExpired({ transaction, step }, Date.now())) {
|
|
566
|
-
await this.checkStepTimeout(transaction, step);
|
|
567
|
-
await this.checkTransactionTimeout(transaction, nextSteps.next.includes(step) ? nextSteps.next : [step]);
|
|
568
|
-
}
|
|
569
|
-
if (errors_1.PermanentStepFailureError.isPermanentStepFailureError(error)) {
|
|
570
|
-
await setStepFailure(error, {
|
|
571
|
-
endRetry: true,
|
|
572
|
-
response,
|
|
573
|
-
});
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
await setStepFailure(error, {
|
|
577
|
-
response,
|
|
578
|
-
});
|
|
579
|
-
}));
|
|
652
|
+
execution.push(this.executeSyncStep(promise, transaction, step, nextSteps));
|
|
580
653
|
}
|
|
581
654
|
else {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
};
|
|
585
|
-
execution.push(transaction.saveCheckpoint().then(() => {
|
|
586
|
-
let promise;
|
|
587
|
-
if (TransactionOrchestrator.traceStep) {
|
|
588
|
-
promise = TransactionOrchestrator.traceStep(stepHandler, traceData);
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
promise = stepHandler();
|
|
592
|
-
}
|
|
593
|
-
promise
|
|
594
|
-
.then(async (response) => {
|
|
595
|
-
const output = response?.__type ? response.output : response;
|
|
596
|
-
if (errors_1.SkipStepResponse.isSkipStepResponse(output)) {
|
|
597
|
-
await TransactionOrchestrator.skipStep(transaction, step);
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
if (!step.definition.backgroundExecution ||
|
|
601
|
-
step.definition.nested) {
|
|
602
|
-
const eventName = types_1.DistributedTransactionEvent.STEP_AWAITING;
|
|
603
|
-
transaction.emit(eventName, { step, transaction });
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
if (this.hasExpired({ transaction, step }, Date.now())) {
|
|
607
|
-
await this.checkStepTimeout(transaction, step);
|
|
608
|
-
await this.checkTransactionTimeout(transaction, nextSteps.next.includes(step) ? nextSteps.next : [step]);
|
|
609
|
-
}
|
|
610
|
-
await TransactionOrchestrator.setStepSuccess(transaction, step, response);
|
|
611
|
-
}
|
|
612
|
-
// check nested flow
|
|
613
|
-
await transaction.scheduleRetry(step, 0);
|
|
614
|
-
})
|
|
615
|
-
.catch(async (error) => {
|
|
616
|
-
const response = error?.getStepResponse?.();
|
|
617
|
-
if (errors_1.PermanentStepFailureError.isPermanentStepFailureError(error)) {
|
|
618
|
-
await setStepFailure(error, {
|
|
619
|
-
endRetry: true,
|
|
620
|
-
response,
|
|
621
|
-
});
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
await setStepFailure(error, {
|
|
625
|
-
response,
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
}));
|
|
655
|
+
// Execute async step in background as part of the next event loop cycle and continue the execution of the transaction
|
|
656
|
+
executionAsync.push(() => this.executeAsyncStep(promise, transaction, step, nextSteps));
|
|
629
657
|
}
|
|
630
658
|
}
|
|
631
|
-
|
|
632
|
-
|
|
659
|
+
await (0, utils_1.promiseAll)(execution);
|
|
660
|
+
if (!nextSteps.next.length || (hasAsyncSteps && !execution.length)) {
|
|
661
|
+
continueExecution = false;
|
|
633
662
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
663
|
+
if (hasAsyncSteps) {
|
|
664
|
+
await transaction.saveCheckpoint().catch((error) => {
|
|
665
|
+
if (TransactionOrchestrator.isExpectedError(error)) {
|
|
666
|
+
continueExecution = false;
|
|
667
|
+
}
|
|
639
668
|
throw error;
|
|
669
|
+
});
|
|
670
|
+
for (const exec of executionAsync) {
|
|
671
|
+
void exec();
|
|
640
672
|
}
|
|
641
673
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Finalize the transaction when all steps are complete
|
|
678
|
+
*/
|
|
679
|
+
async finalizeTransaction(transaction) {
|
|
680
|
+
if (transaction.hasTimeout()) {
|
|
681
|
+
void transaction.clearTransactionTimeout();
|
|
682
|
+
}
|
|
683
|
+
await transaction.saveCheckpoint().catch((error) => {
|
|
684
|
+
if (!TransactionOrchestrator.isExpectedError(error)) {
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
this.emit(types_1.DistributedTransactionEvent.FINISH, { transaction });
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Prepare a step for execution by setting state and incrementing attempts
|
|
692
|
+
*/
|
|
693
|
+
prepareStepForExecution(step, flow) {
|
|
694
|
+
const curState = step.getStates();
|
|
695
|
+
step.lastAttempt = Date.now();
|
|
696
|
+
step.attempts++;
|
|
697
|
+
if (curState.state === utils_1.TransactionStepState.NOT_STARTED) {
|
|
698
|
+
if (!step.startedAt) {
|
|
699
|
+
step.startedAt = Date.now();
|
|
700
|
+
}
|
|
701
|
+
if (step.isCompensating()) {
|
|
702
|
+
step.changeState(utils_1.TransactionStepState.COMPENSATING);
|
|
703
|
+
if (step.definition.noCompensation) {
|
|
704
|
+
step.changeState(utils_1.TransactionStepState.REVERTED);
|
|
705
|
+
return { shouldContinueExecution: false };
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else if (flow.state === types_1.TransactionState.INVOKING) {
|
|
709
|
+
step.changeState(utils_1.TransactionStepState.INVOKING);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
step.changeStatus(types_1.TransactionStepStatus.WAITING);
|
|
713
|
+
return { shouldContinueExecution: true };
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Create the payload for a step execution
|
|
717
|
+
*/
|
|
718
|
+
createStepPayload(transaction, step, flow, type) {
|
|
719
|
+
return new distributed_transaction_1.TransactionPayload({
|
|
720
|
+
model_id: flow.modelId,
|
|
721
|
+
idempotency_key: TransactionOrchestrator.getKeyName(flow.modelId, flow.transactionId, step.definition.action, type),
|
|
722
|
+
action: step.definition.action + "",
|
|
723
|
+
action_type: type,
|
|
724
|
+
attempt: step.attempts,
|
|
725
|
+
timestamp: Date.now(),
|
|
726
|
+
}, transaction.payload, transaction.getContext());
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Prepare handler arguments for step execution
|
|
730
|
+
*/
|
|
731
|
+
prepareHandlerArgs(transaction, step, payload, type) {
|
|
732
|
+
return [
|
|
733
|
+
step.definition.action + "",
|
|
734
|
+
type,
|
|
735
|
+
payload,
|
|
736
|
+
transaction,
|
|
737
|
+
step,
|
|
738
|
+
this,
|
|
739
|
+
];
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Create the step execution promise with optional tracing
|
|
743
|
+
*/
|
|
744
|
+
createStepExecutionPromise(transaction, step) {
|
|
745
|
+
const type = step.isCompensating()
|
|
746
|
+
? types_1.TransactionHandlerType.COMPENSATE
|
|
747
|
+
: types_1.TransactionHandlerType.INVOKE;
|
|
748
|
+
const flow = transaction.getFlow();
|
|
749
|
+
const payload = this.createStepPayload(transaction, step, flow, type);
|
|
750
|
+
const handlerArgs = this.prepareHandlerArgs(transaction, step, payload, type);
|
|
751
|
+
const traceData = {
|
|
752
|
+
action: step.definition.action + "",
|
|
753
|
+
type,
|
|
754
|
+
step_id: step.id,
|
|
755
|
+
step_uuid: step.uuid + "",
|
|
756
|
+
attempts: step.attempts,
|
|
757
|
+
failures: step.failures,
|
|
758
|
+
async: !!(type === "invoke"
|
|
759
|
+
? step.definition.async
|
|
760
|
+
: step.definition.compensateAsync),
|
|
761
|
+
idempotency_key: handlerArgs[2].metadata.idempotency_key,
|
|
762
|
+
};
|
|
763
|
+
const stepHandler = async () => {
|
|
764
|
+
return await transaction.handler(...handlerArgs);
|
|
765
|
+
};
|
|
766
|
+
// Return the appropriate promise based on tracing configuration
|
|
767
|
+
if (TransactionOrchestrator.traceStep) {
|
|
768
|
+
return () => TransactionOrchestrator.traceStep(stepHandler, traceData);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
return stepHandler;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Execute a synchronous step and handle its result
|
|
776
|
+
*/
|
|
777
|
+
executeSyncStep(promiseFn, transaction, step, nextSteps) {
|
|
778
|
+
return promiseFn()
|
|
779
|
+
.then(async (response) => {
|
|
780
|
+
await this.handleStepExpiration(transaction, step, nextSteps);
|
|
781
|
+
const output = response?.__type || response?.output?.__type
|
|
782
|
+
? response.output
|
|
783
|
+
: response;
|
|
784
|
+
if (errors_1.SkipStepResponse.isSkipStepResponse(output)) {
|
|
785
|
+
await TransactionOrchestrator.skipStep({
|
|
786
|
+
transaction,
|
|
787
|
+
step,
|
|
788
|
+
});
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
await this.handleStepSuccess(transaction, step, response);
|
|
792
|
+
})
|
|
793
|
+
.catch(async (error) => {
|
|
794
|
+
if (TransactionOrchestrator.isExpectedError(error)) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const response = error?.getStepResponse?.();
|
|
798
|
+
await this.handleStepExpiration(transaction, step, nextSteps);
|
|
799
|
+
if (errors_1.PermanentStepFailureError.isPermanentStepFailureError(error)) {
|
|
800
|
+
await this.handleStepFailure(transaction, step, error, true, response);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
await this.handleStepFailure(transaction, step, error, false, response);
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Execute an asynchronous step and handle its result
|
|
808
|
+
*/
|
|
809
|
+
executeAsyncStep(promiseFn, transaction, step, nextSteps) {
|
|
810
|
+
return promiseFn()
|
|
811
|
+
.then(async (response) => {
|
|
812
|
+
const output = response?.__type || response?.output?.__type
|
|
813
|
+
? response.output
|
|
814
|
+
: response;
|
|
815
|
+
if (errors_1.SkipStepResponse.isSkipStepResponse(output)) {
|
|
816
|
+
await TransactionOrchestrator.skipStep({
|
|
817
|
+
transaction,
|
|
818
|
+
step,
|
|
819
|
+
});
|
|
820
|
+
// Schedule to continue the execution of async steps because they are not awaited on purpose and can be handled by another machine
|
|
821
|
+
await transaction.scheduleRetry(step, 0);
|
|
822
|
+
return;
|
|
645
823
|
}
|
|
824
|
+
else {
|
|
825
|
+
if (!step.definition.backgroundExecution || step.definition.nested) {
|
|
826
|
+
const eventName = types_1.DistributedTransactionEvent.STEP_AWAITING;
|
|
827
|
+
transaction.emit(eventName, { step, transaction });
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
await this.handleStepExpiration(transaction, step, nextSteps);
|
|
831
|
+
await this.handleStepSuccess(transaction, step, response);
|
|
832
|
+
}
|
|
833
|
+
})
|
|
834
|
+
.catch(async (error) => {
|
|
835
|
+
if (TransactionOrchestrator.isExpectedError(error)) {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const response = error?.getStepResponse?.();
|
|
839
|
+
if (errors_1.PermanentStepFailureError.isPermanentStepFailureError(error)) {
|
|
840
|
+
await this.handleStepFailure(transaction, step, error, true, response);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
await this.handleStepFailure(transaction, step, error, false, response);
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Check if step or transaction has expired and handle timeouts
|
|
848
|
+
*/
|
|
849
|
+
async handleStepExpiration(transaction, step, nextSteps) {
|
|
850
|
+
if (this.hasExpired({ transaction, step }, Date.now())) {
|
|
851
|
+
await this.checkStepTimeout(transaction, step);
|
|
852
|
+
await this.checkTransactionTimeout(transaction, nextSteps.next.includes(step) ? nextSteps.next : [step]);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Handle successful step completion
|
|
857
|
+
*/
|
|
858
|
+
async handleStepSuccess(transaction, step, response) {
|
|
859
|
+
const isAsync = step.isCompensating()
|
|
860
|
+
? step.definition.compensateAsync
|
|
861
|
+
: step.definition.async;
|
|
862
|
+
if ((0, utils_1.isDefined)(response) && step.saveResponse && !isAsync) {
|
|
863
|
+
transaction.addResponse(step.definition.action, step.isCompensating()
|
|
864
|
+
? types_1.TransactionHandlerType.COMPENSATE
|
|
865
|
+
: types_1.TransactionHandlerType.INVOKE, response);
|
|
866
|
+
}
|
|
867
|
+
const ret = await TransactionOrchestrator.setStepSuccess(transaction, step, response);
|
|
868
|
+
if (ret.transactionIsCancelling) {
|
|
869
|
+
await this.cancelTransaction(transaction, {
|
|
870
|
+
preventExecuteNext: true,
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
if (isAsync && !ret.stopExecution) {
|
|
874
|
+
// Schedule to continue the execution of async steps because they are not awaited on purpose and can be handled by another machine
|
|
875
|
+
await transaction.scheduleRetry(step, 0);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Handle step failure
|
|
880
|
+
*/
|
|
881
|
+
async handleStepFailure(transaction, step, error, isPermanent, response) {
|
|
882
|
+
const isAsync = step.isCompensating()
|
|
883
|
+
? step.definition.compensateAsync
|
|
884
|
+
: step.definition.async;
|
|
885
|
+
if ((0, utils_1.isDefined)(response) && step.saveResponse) {
|
|
886
|
+
transaction.addResponse(step.definition.action, step.isCompensating()
|
|
887
|
+
? types_1.TransactionHandlerType.COMPENSATE
|
|
888
|
+
: types_1.TransactionHandlerType.INVOKE, response);
|
|
889
|
+
}
|
|
890
|
+
const ret = await TransactionOrchestrator.setStepFailure(transaction, step, error, isPermanent ? 0 : step.definition.maxRetries);
|
|
891
|
+
if (ret.transactionIsCancelling) {
|
|
892
|
+
await this.cancelTransaction(transaction, {
|
|
893
|
+
preventExecuteNext: true,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
if (isAsync && !ret.stopExecution) {
|
|
897
|
+
// Schedule to continue the execution of async steps because they are not awaited on purpose and can be handled by another machine
|
|
898
|
+
await transaction.scheduleRetry(step, 0);
|
|
646
899
|
}
|
|
647
900
|
}
|
|
648
901
|
/**
|
|
@@ -661,7 +914,9 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
661
914
|
if (flow.state === types_1.TransactionState.NOT_STARTED) {
|
|
662
915
|
flow.state = types_1.TransactionState.INVOKING;
|
|
663
916
|
flow.startedAt = Date.now();
|
|
664
|
-
await transaction.saveCheckpoint(
|
|
917
|
+
await transaction.saveCheckpoint({
|
|
918
|
+
ttl: flow.hasAsyncSteps ? 0 : TransactionOrchestrator.DEFAULT_TTL,
|
|
919
|
+
});
|
|
665
920
|
if (transaction.hasTimeout()) {
|
|
666
921
|
await transaction.scheduleTransactionTimeout(transaction.getTimeout());
|
|
667
922
|
}
|
|
@@ -687,7 +942,7 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
687
942
|
* Cancel and revert a transaction compensating all its executed steps. It can be an ongoing transaction or a completed one
|
|
688
943
|
* @param transaction - The transaction to be reverted
|
|
689
944
|
*/
|
|
690
|
-
async cancelTransaction(transaction) {
|
|
945
|
+
async cancelTransaction(transaction, options) {
|
|
691
946
|
if (transaction.modelId !== this.id) {
|
|
692
947
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `TransactionModel "${transaction.modelId}" cannot be orchestrated by "${this.id}" model.`);
|
|
693
948
|
}
|
|
@@ -695,7 +950,16 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
695
950
|
if (flow.state === types_1.TransactionState.FAILED) {
|
|
696
951
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `Cannot revert a permanent failed transaction.`);
|
|
697
952
|
}
|
|
953
|
+
if (flow.state === types_1.TransactionState.COMPENSATING ||
|
|
954
|
+
flow.state === types_1.TransactionState.WAITING_TO_COMPENSATE) {
|
|
955
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `Cannot revert a transaction that is already compensating.`);
|
|
956
|
+
}
|
|
698
957
|
flow.state = types_1.TransactionState.WAITING_TO_COMPENSATE;
|
|
958
|
+
flow.cancelledAt = Date.now();
|
|
959
|
+
await transaction.saveCheckpoint();
|
|
960
|
+
if (options?.preventExecuteNext) {
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
699
963
|
await this.executeNext(transaction);
|
|
700
964
|
}
|
|
701
965
|
parseFlowOptions() {
|
|
@@ -706,13 +970,12 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
706
970
|
const hasRetriesTimeout = features.hasRetriesTimeout;
|
|
707
971
|
const hasTransactionTimeout = !!this.options.timeout;
|
|
708
972
|
const isIdempotent = !!this.options.idempotent;
|
|
709
|
-
if (hasAsyncSteps) {
|
|
710
|
-
this.options.store = true;
|
|
711
|
-
}
|
|
712
973
|
if (hasStepTimeouts ||
|
|
713
974
|
hasRetriesTimeout ||
|
|
714
975
|
hasTransactionTimeout ||
|
|
715
|
-
isIdempotent
|
|
976
|
+
isIdempotent ||
|
|
977
|
+
this.options.retentionTime ||
|
|
978
|
+
hasAsyncSteps) {
|
|
716
979
|
this.options.store = true;
|
|
717
980
|
}
|
|
718
981
|
const parsedOptions = {
|
|
@@ -724,12 +987,13 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
724
987
|
TransactionOrchestrator.workflowOptions[this.id] = parsedOptions;
|
|
725
988
|
return [steps, features];
|
|
726
989
|
}
|
|
727
|
-
createTransactionFlow(transactionId, flowMetadata) {
|
|
990
|
+
createTransactionFlow(transactionId, flowMetadata, context) {
|
|
728
991
|
const [steps, features] = TransactionOrchestrator.buildSteps(this.definition);
|
|
729
992
|
const flow = {
|
|
730
993
|
modelId: this.id,
|
|
731
994
|
options: this.options,
|
|
732
995
|
transactionId: transactionId,
|
|
996
|
+
runId: context?.runId ?? (0, ulid_1.ulid)(),
|
|
733
997
|
metadata: flowMetadata,
|
|
734
998
|
hasAsyncSteps: features.hasAsyncSteps,
|
|
735
999
|
hasFailedSteps: false,
|
|
@@ -741,11 +1005,12 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
741
1005
|
state: types_1.TransactionState.NOT_STARTED,
|
|
742
1006
|
definition: this.definition,
|
|
743
1007
|
steps,
|
|
1008
|
+
_v: 0, // Initialize version to 0
|
|
744
1009
|
};
|
|
745
1010
|
return flow;
|
|
746
1011
|
}
|
|
747
|
-
static async loadTransactionById(modelId, transactionId) {
|
|
748
|
-
const transaction = await distributed_transaction_1.DistributedTransaction.loadTransaction(modelId, transactionId);
|
|
1012
|
+
static async loadTransactionById(modelId, transactionId, options) {
|
|
1013
|
+
const transaction = await distributed_transaction_1.DistributedTransaction.loadTransaction(modelId, transactionId, options);
|
|
749
1014
|
if (transaction !== null) {
|
|
750
1015
|
const flow = transaction.flow;
|
|
751
1016
|
const [steps] = TransactionOrchestrator.buildSteps(flow.definition, flow.steps);
|
|
@@ -786,6 +1051,9 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
786
1051
|
}
|
|
787
1052
|
const definitionCopy = { ...obj };
|
|
788
1053
|
delete definitionCopy.next;
|
|
1054
|
+
const isAsync = !!definitionCopy.async;
|
|
1055
|
+
const hasRetryInterval = !!(definitionCopy.retryInterval || definitionCopy.retryIntervalAwaiting);
|
|
1056
|
+
const hasTimeout = !!definitionCopy.timeout;
|
|
789
1057
|
if (definitionCopy.async) {
|
|
790
1058
|
features.hasAsyncSteps = true;
|
|
791
1059
|
}
|
|
@@ -799,6 +1067,16 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
799
1067
|
if (definitionCopy.nested) {
|
|
800
1068
|
features.hasNestedTransactions = true;
|
|
801
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Force the checkpoint to save even for sync step when they have specific configurations.
|
|
1072
|
+
*/
|
|
1073
|
+
definitionCopy.store = !!(definitionCopy.store ||
|
|
1074
|
+
isAsync ||
|
|
1075
|
+
hasRetryInterval ||
|
|
1076
|
+
hasTimeout);
|
|
1077
|
+
if (existingSteps?.[id]) {
|
|
1078
|
+
existingSteps[id].definition.store = definitionCopy.store;
|
|
1079
|
+
}
|
|
802
1080
|
states[id] = Object.assign(new transaction_step_1.TransactionStep(), existingSteps?.[id] || {
|
|
803
1081
|
id,
|
|
804
1082
|
uuid: definitionCopy.uuid,
|
|
@@ -817,6 +1095,7 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
817
1095
|
failures: 0,
|
|
818
1096
|
lastAttempt: null,
|
|
819
1097
|
next: [],
|
|
1098
|
+
_v: 0, // Initialize step version to 0
|
|
820
1099
|
});
|
|
821
1100
|
}
|
|
822
1101
|
if (Array.isArray(obj.next)) {
|
|
@@ -836,12 +1115,12 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
836
1115
|
* @param payload - payload to be passed to all the transaction steps
|
|
837
1116
|
* @param flowMetadata - flow metadata which can include event group id for example
|
|
838
1117
|
*/
|
|
839
|
-
async beginTransaction(transactionId, handler, payload, flowMetadata) {
|
|
1118
|
+
async beginTransaction({ transactionId, handler, payload, flowMetadata, context, onLoad, }) {
|
|
840
1119
|
const existingTransaction = await TransactionOrchestrator.loadTransactionById(this.id, transactionId);
|
|
841
1120
|
let newTransaction = false;
|
|
842
1121
|
let modelFlow;
|
|
843
1122
|
if (!existingTransaction) {
|
|
844
|
-
modelFlow = this.createTransactionFlow(transactionId, flowMetadata);
|
|
1123
|
+
modelFlow = this.createTransactionFlow(transactionId, flowMetadata, context);
|
|
845
1124
|
newTransaction = true;
|
|
846
1125
|
}
|
|
847
1126
|
else {
|
|
@@ -849,7 +1128,12 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
849
1128
|
}
|
|
850
1129
|
const transaction = new distributed_transaction_1.DistributedTransaction(modelFlow, handler, payload, existingTransaction?.errors, existingTransaction?.context);
|
|
851
1130
|
if (newTransaction && this.getOptions().store) {
|
|
852
|
-
await transaction.saveCheckpoint(
|
|
1131
|
+
await transaction.saveCheckpoint({
|
|
1132
|
+
ttl: modelFlow.hasAsyncSteps ? 0 : TransactionOrchestrator.DEFAULT_TTL,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
if (onLoad) {
|
|
1136
|
+
await onLoad(transaction);
|
|
853
1137
|
}
|
|
854
1138
|
return transaction;
|
|
855
1139
|
}
|
|
@@ -857,8 +1141,8 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
857
1141
|
* @param transactionId - unique identifier of the transaction
|
|
858
1142
|
* @param handler - function to handle action of the transaction
|
|
859
1143
|
*/
|
|
860
|
-
async retrieveExistingTransaction(transactionId, handler) {
|
|
861
|
-
const existingTransaction = await TransactionOrchestrator.loadTransactionById(this.id, transactionId);
|
|
1144
|
+
async retrieveExistingTransaction(transactionId, handler, options) {
|
|
1145
|
+
const existingTransaction = await TransactionOrchestrator.loadTransactionById(this.id, transactionId, { isCancelling: options?.isCancelling });
|
|
862
1146
|
if (!existingTransaction) {
|
|
863
1147
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, `Transaction ${transactionId} could not be found.`);
|
|
864
1148
|
}
|
|
@@ -901,13 +1185,16 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
901
1185
|
* @param handler - The handler function to execute the step
|
|
902
1186
|
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
|
|
903
1187
|
*/
|
|
904
|
-
async skipStep(responseIdempotencyKey, handler, transaction) {
|
|
1188
|
+
async skipStep({ responseIdempotencyKey, handler, transaction, }) {
|
|
905
1189
|
const [curTransaction, step] = await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(responseIdempotencyKey, handler, transaction);
|
|
906
1190
|
if (step.getStates().status === types_1.TransactionStepStatus.WAITING) {
|
|
907
1191
|
this.emit(types_1.DistributedTransactionEvent.RESUME, {
|
|
908
1192
|
transaction: curTransaction,
|
|
909
1193
|
});
|
|
910
|
-
await TransactionOrchestrator.skipStep(
|
|
1194
|
+
await TransactionOrchestrator.skipStep({
|
|
1195
|
+
transaction: curTransaction,
|
|
1196
|
+
step,
|
|
1197
|
+
});
|
|
911
1198
|
await this.executeNext(curTransaction);
|
|
912
1199
|
}
|
|
913
1200
|
else {
|
|
@@ -915,19 +1202,48 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
915
1202
|
}
|
|
916
1203
|
return curTransaction;
|
|
917
1204
|
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Manually force a step to retry even if it is still in awaiting status
|
|
1207
|
+
* @param responseIdempotencyKey - The idempotency key for the step
|
|
1208
|
+
* @param handler - The handler function to execute the step
|
|
1209
|
+
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
|
|
1210
|
+
*/
|
|
1211
|
+
async retryStep({ responseIdempotencyKey, handler, transaction, onLoad, }) {
|
|
1212
|
+
const [curTransaction, step] = await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(responseIdempotencyKey, handler, transaction);
|
|
1213
|
+
if (onLoad) {
|
|
1214
|
+
await onLoad(curTransaction);
|
|
1215
|
+
}
|
|
1216
|
+
if (step.getStates().status === types_1.TransactionStepStatus.WAITING) {
|
|
1217
|
+
this.emit(types_1.DistributedTransactionEvent.RESUME, {
|
|
1218
|
+
transaction: curTransaction,
|
|
1219
|
+
});
|
|
1220
|
+
await TransactionOrchestrator.retryStep(curTransaction, step);
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `Cannot retry step when status is ${step.getStates().status}`);
|
|
1224
|
+
}
|
|
1225
|
+
return curTransaction;
|
|
1226
|
+
}
|
|
918
1227
|
/** Register a step success for a specific transaction and step
|
|
919
1228
|
* @param responseIdempotencyKey - The idempotency key for the step
|
|
920
1229
|
* @param handler - The handler function to execute the step
|
|
921
1230
|
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
|
|
922
1231
|
* @param response - The response of the step
|
|
923
1232
|
*/
|
|
924
|
-
async registerStepSuccess(responseIdempotencyKey, handler, transaction, response) {
|
|
1233
|
+
async registerStepSuccess({ responseIdempotencyKey, handler, transaction, response, onLoad, }) {
|
|
925
1234
|
const [curTransaction, step] = await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(responseIdempotencyKey, handler, transaction);
|
|
1235
|
+
if (onLoad) {
|
|
1236
|
+
await onLoad(curTransaction);
|
|
1237
|
+
}
|
|
926
1238
|
if (step.getStates().status === types_1.TransactionStepStatus.WAITING) {
|
|
927
1239
|
this.emit(types_1.DistributedTransactionEvent.RESUME, {
|
|
928
1240
|
transaction: curTransaction,
|
|
929
1241
|
});
|
|
930
|
-
await TransactionOrchestrator.setStepSuccess(curTransaction, step, response);
|
|
1242
|
+
const ret = await TransactionOrchestrator.setStepSuccess(curTransaction, step, response);
|
|
1243
|
+
if (ret.transactionIsCancelling) {
|
|
1244
|
+
await this.cancelTransaction(curTransaction);
|
|
1245
|
+
return curTransaction;
|
|
1246
|
+
}
|
|
931
1247
|
await this.executeNext(curTransaction);
|
|
932
1248
|
}
|
|
933
1249
|
else {
|
|
@@ -943,13 +1259,22 @@ class TransactionOrchestrator extends events_1.EventEmitter {
|
|
|
943
1259
|
* @param transaction - The current transaction
|
|
944
1260
|
* @param response - The response of the step
|
|
945
1261
|
*/
|
|
946
|
-
async registerStepFailure(responseIdempotencyKey, error, handler, transaction) {
|
|
1262
|
+
async registerStepFailure({ responseIdempotencyKey, error, handler, transaction, onLoad, forcePermanentFailure, }) {
|
|
947
1263
|
const [curTransaction, step] = await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(responseIdempotencyKey, handler, transaction);
|
|
1264
|
+
if (onLoad) {
|
|
1265
|
+
await onLoad(curTransaction);
|
|
1266
|
+
}
|
|
948
1267
|
if (step.getStates().status === types_1.TransactionStepStatus.WAITING) {
|
|
949
1268
|
this.emit(types_1.DistributedTransactionEvent.RESUME, {
|
|
950
1269
|
transaction: curTransaction,
|
|
951
1270
|
});
|
|
952
|
-
await TransactionOrchestrator.setStepFailure(curTransaction, step, error,
|
|
1271
|
+
const ret = await TransactionOrchestrator.setStepFailure(curTransaction, step, error,
|
|
1272
|
+
// On permanent failure, the step should not consider any retries
|
|
1273
|
+
forcePermanentFailure ? 0 : step.definition.maxRetries);
|
|
1274
|
+
if (ret.transactionIsCancelling) {
|
|
1275
|
+
await this.cancelTransaction(curTransaction);
|
|
1276
|
+
return curTransaction;
|
|
1277
|
+
}
|
|
953
1278
|
await this.executeNext(curTransaction);
|
|
954
1279
|
}
|
|
955
1280
|
else {
|