bpmn-elements 18.0.0 → 18.0.2
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/activity/Activity.js +63 -57
- package/dist/constants.js +8 -2
- package/dist/definition/Definition.js +6 -1
- package/dist/definition/DefinitionExecution.js +3 -2
- package/dist/events/BoundaryEvent.js +2 -2
- package/dist/gateways/ParallelGateway.js +29 -9
- package/dist/process/Process.js +3 -2
- package/dist/process/ProcessExecution.js +47 -13
- package/package.json +1 -1
- package/src/activity/Activity.js +48 -47
- package/src/constants.js +6 -0
- package/src/definition/Definition.js +8 -1
- package/src/definition/DefinitionExecution.js +3 -2
- package/src/events/BoundaryEvent.js +2 -2
- package/src/gateways/ParallelGateway.js +25 -10
- package/src/process/Process.js +3 -2
- package/src/process/ProcessExecution.js +51 -15
- package/types/index.d.ts +49 -19
- package/types/interfaces.d.ts +2 -0
|
@@ -97,8 +97,9 @@ function Activity(Behaviour, activityDef, context) {
|
|
|
97
97
|
outboundSequenceFlows,
|
|
98
98
|
outboundEvaluator: new _outboundEvaluator.OutboundEvaluator(this, outboundSequenceFlows)
|
|
99
99
|
};
|
|
100
|
+
const isThrowingLink = activityDef.isThrowing && activityDef.linkNames?.length;
|
|
100
101
|
this[K_FLAGS] = {
|
|
101
|
-
isEnd: !outboundSequenceFlows.length,
|
|
102
|
+
isEnd: !outboundSequenceFlows.length && !isThrowingLink,
|
|
102
103
|
isStart: !hasInboundTrigger && !behaviour.triggeredByEvent && !activityDef.isCatching,
|
|
103
104
|
isSubProcess: activityDef.isSubProcess,
|
|
104
105
|
isMultiInstance: !!behaviour.loopCharacteristics,
|
|
@@ -268,7 +269,7 @@ Object.defineProperties(Activity.prototype, {
|
|
|
268
269
|
},
|
|
269
270
|
initialized: {
|
|
270
271
|
get() {
|
|
271
|
-
return
|
|
272
|
+
return this[K_EXEC].get('initialized') > 0;
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
});
|
|
@@ -280,7 +281,23 @@ Object.defineProperties(Activity.prototype, {
|
|
|
280
281
|
Activity.prototype.activate = function activate() {
|
|
281
282
|
if (this[_constants.K_ACTIVATED]) return;
|
|
282
283
|
this[_constants.K_ACTIVATED] = true;
|
|
283
|
-
|
|
284
|
+
this.addInboundListeners();
|
|
285
|
+
return this.consumeInbound();
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Assert the inbound queue consumer when the activity has a trigger or is initialized.
|
|
290
|
+
* Idempotent: asserting the consumer again while one is active is a no-op.
|
|
291
|
+
* @returns {void}
|
|
292
|
+
*/
|
|
293
|
+
Activity.prototype.consumeInbound = function consumeInbound() {
|
|
294
|
+
if (!this[_constants.K_ACTIVATED]) return;
|
|
295
|
+
if (this.status) return;
|
|
296
|
+
if (!this._getInboundTriggers().length && !this.initialized) return;
|
|
297
|
+
const onInbound = this[_constants.K_MESSAGE_HANDLERS].onInbound;
|
|
298
|
+
return this.broker.getQueue('inbound-q').assertConsumer(onInbound, {
|
|
299
|
+
consumerTag: '_run-on-inbound'
|
|
300
|
+
});
|
|
284
301
|
};
|
|
285
302
|
|
|
286
303
|
/** @internal */
|
|
@@ -326,17 +343,28 @@ Activity.prototype.deactivate = function deactivate() {
|
|
|
326
343
|
/**
|
|
327
344
|
* Initialise activity executionId and emit init event without starting the run.
|
|
328
345
|
* @param {Record<string, any>} [initContent] Optional content merged into the init message
|
|
346
|
+
* @param {import('smqp').MessageProperties} [properties] Optional message properties merged into the init message properties
|
|
329
347
|
*/
|
|
330
|
-
Activity.prototype.init = function init(initContent) {
|
|
348
|
+
Activity.prototype.init = function init(initContent, properties) {
|
|
331
349
|
const id = this.id;
|
|
332
350
|
const exec = this[K_EXEC];
|
|
333
|
-
|
|
334
|
-
|
|
351
|
+
exec.set('initialized', (exec.get('initialized') || 0) + 1);
|
|
352
|
+
const executionId = (0, _shared.getUniqueId)(id);
|
|
335
353
|
this.logger.debug(`<${id}> initialized with executionId <${executionId}>`);
|
|
336
354
|
this._publishEvent('init', this._createMessage({
|
|
337
355
|
...initContent,
|
|
338
356
|
executionId
|
|
339
357
|
}));
|
|
358
|
+
this.broker.getQueue('inbound-q').queueMessage({
|
|
359
|
+
routingKey: 'activity.init'
|
|
360
|
+
}, {
|
|
361
|
+
...initContent,
|
|
362
|
+
id,
|
|
363
|
+
executionId
|
|
364
|
+
}, {
|
|
365
|
+
persistent: false,
|
|
366
|
+
...properties
|
|
367
|
+
});
|
|
340
368
|
};
|
|
341
369
|
|
|
342
370
|
/**
|
|
@@ -347,13 +375,15 @@ Activity.prototype.init = function init(initContent) {
|
|
|
347
375
|
Activity.prototype.run = function run(runContent) {
|
|
348
376
|
const id = this.id;
|
|
349
377
|
if (this.isRunning) throw new Error(`activity <${id}> is already running`);
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
378
|
+
const {
|
|
379
|
+
initExecutionId,
|
|
380
|
+
...runMessage
|
|
381
|
+
} = runContent || {};
|
|
382
|
+
const executionId = runMessage?.id === id && initExecutionId ? initExecutionId : (0, _shared.getUniqueId)(id);
|
|
383
|
+
this[K_EXEC].set('executionId', executionId);
|
|
354
384
|
this._consumeApi();
|
|
355
385
|
const content = this._createMessage({
|
|
356
|
-
...
|
|
386
|
+
...runMessage,
|
|
357
387
|
executionId
|
|
358
388
|
});
|
|
359
389
|
const broker = this.broker;
|
|
@@ -558,10 +588,8 @@ Activity.prototype.getActivityById = function getActivityById(elementId) {
|
|
|
558
588
|
|
|
559
589
|
/** @internal */
|
|
560
590
|
Activity.prototype._runDiscard = function runDiscard(discardContent) {
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
exec.set('executionId', executionId);
|
|
564
|
-
exec.delete('initExecutionId');
|
|
591
|
+
const executionId = (0, _shared.getUniqueId)(this.id);
|
|
592
|
+
this[K_EXEC].set('executionId', executionId);
|
|
565
593
|
this._consumeApi();
|
|
566
594
|
const content = this._createMessage({
|
|
567
595
|
...discardContent,
|
|
@@ -613,7 +641,7 @@ Activity.prototype._onShakeMessage = function _onShakeMessage(sourceMessage) {
|
|
|
613
641
|
id: this.id,
|
|
614
642
|
type: this.type
|
|
615
643
|
});
|
|
616
|
-
return this.broker.publish('event', 'activity.shake.
|
|
644
|
+
return this.broker.publish('event', 'activity.shake.converge', message.content, {
|
|
617
645
|
persistent: false,
|
|
618
646
|
type: 'shake'
|
|
619
647
|
});
|
|
@@ -674,43 +702,38 @@ Activity.prototype._shakeOutbound = function shakeOutbound(sourceMessage) {
|
|
|
674
702
|
for (const t of targets.values()) t.shake(message);
|
|
675
703
|
};
|
|
676
704
|
|
|
677
|
-
/** @internal */
|
|
678
|
-
Activity.prototype._consumeInbound = function consumeInbound() {
|
|
679
|
-
if (!this[_constants.K_ACTIVATED]) return;
|
|
680
|
-
if (this.status || !this._getInboundTriggers().length) return;
|
|
681
|
-
const inboundQ = this.broker.getQueue('inbound-q');
|
|
682
|
-
const onInbound = this[_constants.K_MESSAGE_HANDLERS].onInbound;
|
|
683
|
-
return inboundQ.assertConsumer(onInbound, {
|
|
684
|
-
consumerTag: '_run-on-inbound'
|
|
685
|
-
});
|
|
686
|
-
};
|
|
687
|
-
|
|
688
705
|
/** @internal */
|
|
689
706
|
Activity.prototype._onInbound = function onInbound(routingKey, message) {
|
|
690
707
|
message.ack();
|
|
691
708
|
const broker = this.broker;
|
|
692
709
|
broker.cancel('_run-on-inbound');
|
|
693
710
|
const content = message.content;
|
|
694
|
-
const inbound = [(0, _messageHelper.cloneContent)(content)];
|
|
695
711
|
switch (routingKey) {
|
|
696
|
-
case 'activity.
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
712
|
+
case 'activity.init':
|
|
713
|
+
{
|
|
714
|
+
const exec = this[K_EXEC];
|
|
715
|
+
exec.set('initialized', (exec.get('initialized') || 0) - 1);
|
|
716
|
+
return this.run({
|
|
717
|
+
initExecutionId: content.executionId,
|
|
718
|
+
id: content.id,
|
|
719
|
+
message: content.message,
|
|
720
|
+
...(content.inbound?.length && {
|
|
721
|
+
inbound: content.inbound
|
|
722
|
+
})
|
|
723
|
+
});
|
|
724
|
+
}
|
|
702
725
|
case 'association.take':
|
|
703
726
|
case 'flow.take':
|
|
704
727
|
case 'activity.restart':
|
|
705
728
|
case 'activity.enter':
|
|
706
729
|
return this.run({
|
|
707
730
|
message: content.message,
|
|
708
|
-
inbound
|
|
731
|
+
inbound: [(0, _messageHelper.cloneContent)(content)]
|
|
709
732
|
});
|
|
710
733
|
case 'activity.discard':
|
|
711
734
|
{
|
|
712
735
|
return this._runDiscard({
|
|
713
|
-
inbound
|
|
736
|
+
inbound: [(0, _messageHelper.cloneContent)(content)]
|
|
714
737
|
});
|
|
715
738
|
}
|
|
716
739
|
}
|
|
@@ -740,26 +763,9 @@ Activity.prototype._onInboundEvent = function onInboundEvent(routingKey, message
|
|
|
740
763
|
{
|
|
741
764
|
const linkName = content.message?.linkName;
|
|
742
765
|
if (!this[K_FLAGS].linkNames?.includes(linkName)) break;
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
type: this.type,
|
|
747
|
-
executionId,
|
|
748
|
-
state: 'enter',
|
|
749
|
-
message: {
|
|
750
|
-
...content.message
|
|
751
|
-
}
|
|
752
|
-
}));
|
|
753
|
-
inboundQ.queueMessage({
|
|
754
|
-
routingKey: 'activity.relink'
|
|
755
|
-
}, (0, _messageHelper.cloneContent)(content, {
|
|
756
|
-
id: this.id,
|
|
757
|
-
executionId,
|
|
758
|
-
message: {
|
|
759
|
-
...content.message
|
|
760
|
-
}
|
|
761
|
-
}), properties);
|
|
762
|
-
return;
|
|
766
|
+
return this.init({
|
|
767
|
+
inbound: [(0, _messageHelper.cloneContent)(content)]
|
|
768
|
+
});
|
|
763
769
|
}
|
|
764
770
|
case 'association.take':
|
|
765
771
|
case 'flow.take':
|
|
@@ -942,7 +948,7 @@ Activity.prototype._continueRunMessage = function continueRunMessage(routingKey,
|
|
|
942
948
|
case 'run.next':
|
|
943
949
|
message.ack();
|
|
944
950
|
this._pauseRunQ();
|
|
945
|
-
return this.
|
|
951
|
+
return this.consumeInbound();
|
|
946
952
|
}
|
|
947
953
|
if (!step) message.ack();
|
|
948
954
|
};
|
package/dist/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.K_TARGETS = exports.K_STOPPED = exports.K_STATUS = exports.K_STATE_MESSAGE = exports.K_REFERENCE_INFO = exports.K_REFERENCE_ELEMENT = exports.K_MESSAGE_Q = exports.K_MESSAGE_HANDLERS = exports.K_EXTENSIONS = exports.K_EXECUTION = exports.K_EXECUTE_MESSAGE = exports.K_COUNTERS = exports.K_CONSUMING = exports.K_COMPLETED = exports.K_ACTIVATED = void 0;
|
|
6
|
+
exports.STATE_VERSION = exports.K_TARGETS = exports.K_STOPPED = exports.K_STATUS = exports.K_STATE_MESSAGE = exports.K_REFERENCE_INFO = exports.K_REFERENCE_ELEMENT = exports.K_MESSAGE_Q = exports.K_MESSAGE_HANDLERS = exports.K_EXTENSIONS = exports.K_EXECUTION = exports.K_EXECUTE_MESSAGE = exports.K_COUNTERS = exports.K_CONSUMING = exports.K_COMPLETED = exports.K_ACTIVATED = void 0;
|
|
7
7
|
const K_ACTIVATED = exports.K_ACTIVATED = Symbol.for('activated');
|
|
8
8
|
const K_COMPLETED = exports.K_COMPLETED = Symbol.for('completed');
|
|
9
9
|
const K_CONSUMING = exports.K_CONSUMING = Symbol.for('consuming');
|
|
@@ -18,4 +18,10 @@ const K_REFERENCE_INFO = exports.K_REFERENCE_INFO = Symbol.for('referenceInfo');
|
|
|
18
18
|
const K_STATE_MESSAGE = exports.K_STATE_MESSAGE = Symbol.for('stateMessage');
|
|
19
19
|
const K_STATUS = exports.K_STATUS = Symbol.for('status');
|
|
20
20
|
const K_STOPPED = exports.K_STOPPED = Symbol.for('stopped');
|
|
21
|
-
const K_TARGETS = exports.K_TARGETS = Symbol.for('targets');
|
|
21
|
+
const K_TARGETS = exports.K_TARGETS = Symbol.for('targets');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* State version. Tracks the package major; bump on each major. Recovering an older major triggers
|
|
25
|
+
* migrations. Unstamped legacy states are treated as version 0.
|
|
26
|
+
*/
|
|
27
|
+
const STATE_VERSION = exports.STATE_VERSION = 18;
|
|
@@ -176,6 +176,7 @@ Definition.prototype.resume = function resume(callback) {
|
|
|
176
176
|
*/
|
|
177
177
|
Definition.prototype.getState = function getState() {
|
|
178
178
|
return this._createMessage({
|
|
179
|
+
stateVersion: _constants.STATE_VERSION,
|
|
179
180
|
status: this.status,
|
|
180
181
|
stopped: this.stopped,
|
|
181
182
|
counters: this.counters,
|
|
@@ -194,6 +195,10 @@ Definition.prototype.getState = function getState() {
|
|
|
194
195
|
Definition.prototype.recover = function recover(state) {
|
|
195
196
|
if (this.isRunning) throw new Error('cannot recover running definition');
|
|
196
197
|
if (!state) return this;
|
|
198
|
+
const recoveredVersion = state.stateVersion || 0;
|
|
199
|
+
if (recoveredVersion !== _constants.STATE_VERSION) {
|
|
200
|
+
this.logger.debug(`<${this.id}> recover state version ${recoveredVersion} into runtime state version ${_constants.STATE_VERSION}`);
|
|
201
|
+
}
|
|
197
202
|
this[_constants.K_STOPPED] = !!state.stopped;
|
|
198
203
|
this[_constants.K_STATUS] = state.status;
|
|
199
204
|
const exec = this[_constants.K_EXECUTION];
|
|
@@ -206,7 +211,7 @@ Definition.prototype.recover = function recover(state) {
|
|
|
206
211
|
}
|
|
207
212
|
this.environment.recover(state.environment);
|
|
208
213
|
if (state.execution) {
|
|
209
|
-
exec.set('execution', new _DefinitionExecution.DefinitionExecution(this, this.context).recover(state.execution));
|
|
214
|
+
exec.set('execution', new _DefinitionExecution.DefinitionExecution(this, this.context).recover(state.execution, recoveredVersion));
|
|
210
215
|
}
|
|
211
216
|
this.broker.recover(state.broker);
|
|
212
217
|
return this;
|
|
@@ -186,9 +186,10 @@ DefinitionExecution.prototype.resume = function resume() {
|
|
|
186
186
|
/**
|
|
187
187
|
* Restore execution state captured by getState. Reinstates running processes from the snapshot.
|
|
188
188
|
* @param {import('#types').DefinitionExecutionState} [state]
|
|
189
|
+
* @param {number} [recoveredVersion] State version
|
|
189
190
|
* @returns {this}
|
|
190
191
|
*/
|
|
191
|
-
DefinitionExecution.prototype.recover = function recover(state) {
|
|
192
|
+
DefinitionExecution.prototype.recover = function recover(state, recoveredVersion) {
|
|
192
193
|
if (!state) return this;
|
|
193
194
|
this.executionId = state.executionId;
|
|
194
195
|
this[_constants.K_STOPPED] = state.stopped;
|
|
@@ -208,7 +209,7 @@ DefinitionExecution.prototype.recover = function recover(state) {
|
|
|
208
209
|
}
|
|
209
210
|
if (!bp) continue;
|
|
210
211
|
ids.add(bpid);
|
|
211
|
-
bp.recover(bpState);
|
|
212
|
+
bp.recover(bpState, recoveredVersion);
|
|
212
213
|
running.add(bp);
|
|
213
214
|
}
|
|
214
215
|
return this;
|
|
@@ -44,9 +44,9 @@ Object.defineProperty(BoundaryEventBehaviour.prototype, 'executionId', {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
Object.defineProperty(BoundaryEventBehaviour.prototype, 'cancelActivity', {
|
|
47
|
+
/** @returns {boolean} */
|
|
47
48
|
get() {
|
|
48
|
-
|
|
49
|
-
return 'cancelActivity' in behaviour ? behaviour.cancelActivity : true;
|
|
49
|
+
return this.activity.behaviour?.cancelActivity ?? true;
|
|
50
50
|
}
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -76,11 +76,15 @@ function ParallelGatewayBehaviour(activity) {
|
|
|
76
76
|
this.type = activity.type;
|
|
77
77
|
this.activity = activity;
|
|
78
78
|
this.broker = activity.broker;
|
|
79
|
+
/**
|
|
80
|
+
* Inbound taken sequence flow sequences
|
|
81
|
+
* @type {Set<import('#types').ElementMessageContent}
|
|
82
|
+
*/
|
|
79
83
|
this.inbound = new Set();
|
|
80
|
-
this.isConverging = true;
|
|
81
84
|
this[_constants.K_EXECUTE_MESSAGE] = undefined;
|
|
82
85
|
}
|
|
83
86
|
Object.defineProperty(ParallelGatewayBehaviour.prototype, 'executionId', {
|
|
87
|
+
/** @returns {string | undefined} */
|
|
84
88
|
get() {
|
|
85
89
|
return this[_constants.K_EXECUTE_MESSAGE]?.content.executionId;
|
|
86
90
|
}
|
|
@@ -110,6 +114,12 @@ ParallelGatewayBehaviour.prototype.execute = function execute(executeMessage) {
|
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Setup peer monitor
|
|
120
|
+
* @param {import('#types').ElementBrokerMessage} executeMessage
|
|
121
|
+
* @returns {void}
|
|
122
|
+
*/
|
|
113
123
|
ParallelGatewayBehaviour.prototype.setup = function setup(executeMessage) {
|
|
114
124
|
const peerIds = new Set([...this.activity[K_PEERS].values()].map(v => [...v]).flat());
|
|
115
125
|
this[_constants.K_TARGETS] = new Map([...peerIds].map(pid => [pid, this.activity.getActivityById(pid)]));
|
|
@@ -142,9 +152,7 @@ ParallelGatewayBehaviour.prototype.setup = function setup(executeMessage) {
|
|
|
142
152
|
exclusive: true,
|
|
143
153
|
prefetch: 10000
|
|
144
154
|
});
|
|
145
|
-
|
|
146
|
-
this.broker.publish('event', 'activity.converge', (0, _messageHelper.cloneContent)(executeContent));
|
|
147
|
-
}
|
|
155
|
+
this.broker.publish('event', 'activity.converge', (0, _messageHelper.cloneContent)(executeContent));
|
|
148
156
|
return this.broker.publish('execution', 'execute.start', (0, _messageHelper.cloneContent)(executeMessage.content, {
|
|
149
157
|
preventComplete: true,
|
|
150
158
|
state: STATE_SETUP
|
|
@@ -179,17 +187,19 @@ ParallelGatewayBehaviour.prototype._stop = function stop() {
|
|
|
179
187
|
this.broker.cancel('_parallel-execution-peer-enter-tag');
|
|
180
188
|
this.peerMonitor.stop();
|
|
181
189
|
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Peer monitor
|
|
193
|
+
* @param {import('#types').Activity} activity parallel gateway activity
|
|
194
|
+
* @param {Map<string, import('#types').Activity} targets parallel gateway peer target activities
|
|
195
|
+
*/
|
|
182
196
|
function PeerMonitor(activity, targets) {
|
|
183
197
|
this.activity = activity;
|
|
184
198
|
this.id = activity.id;
|
|
185
199
|
this.broker = activity.broker;
|
|
186
|
-
this.running = 0;
|
|
187
|
-
this.index = 0;
|
|
188
|
-
this.discarded = 0;
|
|
189
200
|
this.running = new Map();
|
|
190
201
|
this.watching = new Map();
|
|
191
202
|
this.targets = targets;
|
|
192
|
-
this.touched = new Set();
|
|
193
203
|
this.inbound = [];
|
|
194
204
|
}
|
|
195
205
|
Object.defineProperty(PeerMonitor.prototype, 'isRunning', {
|
|
@@ -197,6 +207,12 @@ Object.defineProperty(PeerMonitor.prototype, 'isRunning', {
|
|
|
197
207
|
return this.running.size > 0;
|
|
198
208
|
}
|
|
199
209
|
});
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Execute peer monitor
|
|
213
|
+
* @param {import('#types').ElementBrokerMessage} executeMessage
|
|
214
|
+
* @returns {number} number of running peers
|
|
215
|
+
*/
|
|
200
216
|
PeerMonitor.prototype.execute = function execute(executeMessage) {
|
|
201
217
|
const message = (0, _messageHelper.cloneMessage)(executeMessage);
|
|
202
218
|
const inbound = message.content.inbound.pop();
|
|
@@ -208,12 +224,16 @@ PeerMonitor.prototype.execute = function execute(executeMessage) {
|
|
|
208
224
|
state: STATE_MONTITORING,
|
|
209
225
|
preventComplete: true
|
|
210
226
|
});
|
|
211
|
-
this.touched.add(inbound.sourceId);
|
|
212
227
|
for (const target of this.targets.values()) {
|
|
213
228
|
this.monitor(target);
|
|
214
229
|
}
|
|
215
230
|
return this.running.size;
|
|
216
231
|
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Monitor peer activity
|
|
235
|
+
* @param {import('#types').Activity} peerActivity
|
|
236
|
+
*/
|
|
217
237
|
PeerMonitor.prototype.monitor = function monitor(peerActivity) {
|
|
218
238
|
if (this.watching.has(peerActivity.id)) return;
|
|
219
239
|
this.activity.logger.debug(`<${this.id}> monitor <${peerActivity.id}> with status: ${peerActivity.status}`);
|
package/dist/process/Process.js
CHANGED
|
@@ -191,10 +191,11 @@ Process.prototype.getState = function getState() {
|
|
|
191
191
|
/**
|
|
192
192
|
* Restore process state captured by getState.
|
|
193
193
|
* @param {import('#types').ProcessState} [state]
|
|
194
|
+
* @param {number} [recoveredVersion] State version
|
|
194
195
|
* @returns {this}
|
|
195
196
|
* @throws {Error} when called on a running process
|
|
196
197
|
*/
|
|
197
|
-
Process.prototype.recover = function recover(state) {
|
|
198
|
+
Process.prototype.recover = function recover(state, recoveredVersion) {
|
|
198
199
|
if (this.isRunning) throw new Error(`cannot recover running process <${this.id}>`);
|
|
199
200
|
if (!state) return this;
|
|
200
201
|
this[_constants.K_STOPPED] = !!state.stopped;
|
|
@@ -207,7 +208,7 @@ Process.prototype.recover = function recover(state) {
|
|
|
207
208
|
};
|
|
208
209
|
this.environment.recover(state.environment);
|
|
209
210
|
if (state.execution) {
|
|
210
|
-
exec.set('execution', new _ProcessExecution.ProcessExecution(this, this.context).recover(state.execution));
|
|
211
|
+
exec.set('execution', new _ProcessExecution.ProcessExecution(this, this.context).recover(state.execution, recoveredVersion));
|
|
211
212
|
}
|
|
212
213
|
this.broker.recover(state.broker);
|
|
213
214
|
return this;
|
|
@@ -14,6 +14,7 @@ const K_ELEMENTS = Symbol.for('elements');
|
|
|
14
14
|
const K_PARENT = Symbol.for('parent');
|
|
15
15
|
const K_TRACKER = Symbol.for('activity tracker');
|
|
16
16
|
const K_PEERS_DISCOVERED = Symbol.for('peers discovered');
|
|
17
|
+
const K_RECOVERED_VERSION = Symbol.for('recovered version');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Drives the execution of a single process or sub-process: activates children, routes activity
|
|
@@ -163,6 +164,8 @@ ProcessExecution.prototype.resume = function resume() {
|
|
|
163
164
|
activity.resume();
|
|
164
165
|
}
|
|
165
166
|
if (this[_constants.K_COMPLETED]) return;
|
|
167
|
+
this._reconcileStartEvents();
|
|
168
|
+
if (this[_constants.K_COMPLETED]) return;
|
|
166
169
|
if (!postponed.size && status === 'executing') return this._complete('completed');
|
|
167
170
|
};
|
|
168
171
|
|
|
@@ -208,11 +211,13 @@ ProcessExecution.prototype.getState = function getState() {
|
|
|
208
211
|
/**
|
|
209
212
|
* Restore execution state captured by getState.
|
|
210
213
|
* @param {import('#types').ProcessExecutionState} [state]
|
|
214
|
+
* @param {number} [recoveredVersion] State version
|
|
211
215
|
* @returns {this}
|
|
212
216
|
*/
|
|
213
|
-
ProcessExecution.prototype.recover = function recover(state) {
|
|
217
|
+
ProcessExecution.prototype.recover = function recover(state, recoveredVersion) {
|
|
214
218
|
if (!state) return this;
|
|
215
219
|
this.executionId = state.executionId;
|
|
220
|
+
this[K_RECOVERED_VERSION] = recoveredVersion;
|
|
216
221
|
this[_constants.K_STOPPED] = state.stopped;
|
|
217
222
|
this[_constants.K_COMPLETED] = state.completed;
|
|
218
223
|
this[_constants.K_STATUS] = state.status;
|
|
@@ -386,7 +391,7 @@ ProcessExecution.prototype._start = function start() {
|
|
|
386
391
|
this._shakeOnStart();
|
|
387
392
|
for (const a of startActivities) a.init();
|
|
388
393
|
this[_constants.K_STATUS] = 'executing';
|
|
389
|
-
for (const a of startActivities) a.
|
|
394
|
+
for (const a of startActivities) a.consumeInbound();
|
|
390
395
|
if (!startActivities.size) {
|
|
391
396
|
for (const a of this[K_ELEMENTS].triggeredByEvent) {
|
|
392
397
|
if (a.isCatching && !a.isRunning) a.run();
|
|
@@ -551,7 +556,7 @@ ProcessExecution.prototype._shakeElements = function shakeElements(fromId) {
|
|
|
551
556
|
}) => {
|
|
552
557
|
if (content.parent.id !== this.id) return;
|
|
553
558
|
switch (routingKey) {
|
|
554
|
-
case 'activity.shake.
|
|
559
|
+
case 'activity.shake.converge':
|
|
555
560
|
{
|
|
556
561
|
const join = convergingGateways.get(content.join);
|
|
557
562
|
if (!join) {
|
|
@@ -740,16 +745,9 @@ ProcessExecution.prototype._onChildMessage = function onChildMessage(routingKey,
|
|
|
740
745
|
}
|
|
741
746
|
case 'activity.end':
|
|
742
747
|
{
|
|
743
|
-
if (!content.isStartEvent) break;
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const startPeers = new Set();
|
|
747
|
-
for (const msg of elements.postponed) {
|
|
748
|
-
const peerId = msg.content.id;
|
|
749
|
-
if (peerId !== content.id && msg.content.isStartEvent) startPeers.add(msg);
|
|
750
|
-
}
|
|
751
|
-
elements.startEventCount = 0;
|
|
752
|
-
for (const msg of startPeers) this._getChildApi(msg).discard();
|
|
748
|
+
if (!(content.isStartEvent || this.getActivityById(content.id)?.isStartEvent)) break;
|
|
749
|
+
if (this[K_ELEMENTS].startEventCount <= 1) break;
|
|
750
|
+
this._discardArmedStartEvents(content.id);
|
|
753
751
|
break;
|
|
754
752
|
}
|
|
755
753
|
case 'activity.error':
|
|
@@ -1041,6 +1039,42 @@ ProcessExecution.prototype._getChildById = function getChildById(childId) {
|
|
|
1041
1039
|
return this.getActivityById(childId) || this._getFlowById(childId);
|
|
1042
1040
|
};
|
|
1043
1041
|
|
|
1042
|
+
/**
|
|
1043
|
+
* Discard the other armed start events once one mutually exclusive entry point wins.
|
|
1044
|
+
* Resolves the start-event flag from the live activity so recovered pre-flag state is handled.
|
|
1045
|
+
* @internal
|
|
1046
|
+
*/
|
|
1047
|
+
ProcessExecution.prototype._discardArmedStartEvents = function discardArmedStartEvents(winnerId) {
|
|
1048
|
+
const elements = this[K_ELEMENTS];
|
|
1049
|
+
const startPeers = [];
|
|
1050
|
+
for (const msg of elements.postponed) {
|
|
1051
|
+
const peerId = msg.content.id;
|
|
1052
|
+
if (peerId === winnerId) continue;
|
|
1053
|
+
if (this.getActivityById(peerId)?.isStartEvent) startPeers.push(msg);
|
|
1054
|
+
}
|
|
1055
|
+
if (!startPeers.length) return;
|
|
1056
|
+
elements.startEventCount = 0;
|
|
1057
|
+
for (const msg of startPeers) this._getChildApi(msg).discard();
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* On resume of a state from an older major, discard start events left armed when another entry
|
|
1062
|
+
* point already won before recovery. The winning start event's `activity.end` cannot replay, so
|
|
1063
|
+
* the live discard trigger never fires.
|
|
1064
|
+
* @internal
|
|
1065
|
+
*/
|
|
1066
|
+
ProcessExecution.prototype._reconcileStartEvents = function reconcileStartEvents() {
|
|
1067
|
+
const elements = this[K_ELEMENTS];
|
|
1068
|
+
if (elements.startEventCount <= 1) return;
|
|
1069
|
+
if (!(this[K_RECOVERED_VERSION] < _constants.STATE_VERSION)) return;
|
|
1070
|
+
for (const child of elements.children) {
|
|
1071
|
+
if (child.isStartEvent && child.counters.taken) {
|
|
1072
|
+
this._discardArmedStartEvents();
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1044
1078
|
/** @internal */
|
|
1045
1079
|
ProcessExecution.prototype._getChildApi = function getChildApi(message) {
|
|
1046
1080
|
const content = message.content;
|
package/package.json
CHANGED
package/src/activity/Activity.js
CHANGED
|
@@ -91,8 +91,10 @@ export function Activity(Behaviour, activityDef, context) {
|
|
|
91
91
|
outboundEvaluator: new OutboundEvaluator(this, outboundSequenceFlows),
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
const isThrowingLink = activityDef.isThrowing && activityDef.linkNames?.length;
|
|
95
|
+
|
|
94
96
|
this[K_FLAGS] = {
|
|
95
|
-
isEnd: !outboundSequenceFlows.length,
|
|
97
|
+
isEnd: !outboundSequenceFlows.length && !isThrowingLink,
|
|
96
98
|
isStart: !hasInboundTrigger && !behaviour.triggeredByEvent && !activityDef.isCatching,
|
|
97
99
|
isSubProcess: activityDef.isSubProcess,
|
|
98
100
|
isMultiInstance: !!behaviour.loopCharacteristics,
|
|
@@ -262,7 +264,7 @@ Object.defineProperties(Activity.prototype, {
|
|
|
262
264
|
},
|
|
263
265
|
initialized: {
|
|
264
266
|
get() {
|
|
265
|
-
return
|
|
267
|
+
return this[K_EXEC].get('initialized') > 0;
|
|
266
268
|
},
|
|
267
269
|
},
|
|
268
270
|
});
|
|
@@ -274,7 +276,25 @@ Object.defineProperties(Activity.prototype, {
|
|
|
274
276
|
Activity.prototype.activate = function activate() {
|
|
275
277
|
if (this[K_ACTIVATED]) return;
|
|
276
278
|
this[K_ACTIVATED] = true;
|
|
277
|
-
|
|
279
|
+
this.addInboundListeners();
|
|
280
|
+
return this.consumeInbound();
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Assert the inbound queue consumer when the activity has a trigger or is initialized.
|
|
285
|
+
* Idempotent: asserting the consumer again while one is active is a no-op.
|
|
286
|
+
* @returns {void}
|
|
287
|
+
*/
|
|
288
|
+
Activity.prototype.consumeInbound = function consumeInbound() {
|
|
289
|
+
if (!this[K_ACTIVATED]) return;
|
|
290
|
+
|
|
291
|
+
if (this.status) return;
|
|
292
|
+
|
|
293
|
+
if (!this._getInboundTriggers().length && !this.initialized) return;
|
|
294
|
+
|
|
295
|
+
const onInbound = this[K_MESSAGE_HANDLERS].onInbound;
|
|
296
|
+
|
|
297
|
+
return this.broker.getQueue('inbound-q').assertConsumer(onInbound, { consumerTag: '_run-on-inbound' });
|
|
278
298
|
};
|
|
279
299
|
|
|
280
300
|
/** @internal */
|
|
@@ -319,14 +339,18 @@ Activity.prototype.deactivate = function deactivate() {
|
|
|
319
339
|
/**
|
|
320
340
|
* Initialise activity executionId and emit init event without starting the run.
|
|
321
341
|
* @param {Record<string, any>} [initContent] Optional content merged into the init message
|
|
342
|
+
* @param {import('smqp').MessageProperties} [properties] Optional message properties merged into the init message properties
|
|
322
343
|
*/
|
|
323
|
-
Activity.prototype.init = function init(initContent) {
|
|
344
|
+
Activity.prototype.init = function init(initContent, properties) {
|
|
324
345
|
const id = this.id;
|
|
325
346
|
const exec = this[K_EXEC];
|
|
326
|
-
|
|
327
|
-
|
|
347
|
+
exec.set('initialized', (exec.get('initialized') || 0) + 1);
|
|
348
|
+
const executionId = getUniqueId(id);
|
|
328
349
|
this.logger.debug(`<${id}> initialized with executionId <${executionId}>`);
|
|
329
350
|
this._publishEvent('init', this._createMessage({ ...initContent, executionId }));
|
|
351
|
+
this.broker
|
|
352
|
+
.getQueue('inbound-q')
|
|
353
|
+
.queueMessage({ routingKey: 'activity.init' }, { ...initContent, id, executionId }, { persistent: false, ...properties });
|
|
330
354
|
};
|
|
331
355
|
|
|
332
356
|
/**
|
|
@@ -338,14 +362,13 @@ Activity.prototype.run = function run(runContent) {
|
|
|
338
362
|
const id = this.id;
|
|
339
363
|
if (this.isRunning) throw new Error(`activity <${id}> is already running`);
|
|
340
364
|
|
|
341
|
-
const
|
|
342
|
-
const executionId =
|
|
343
|
-
|
|
344
|
-
exec.delete('initExecutionId');
|
|
365
|
+
const { initExecutionId, ...runMessage } = runContent || {};
|
|
366
|
+
const executionId = runMessage?.id === id && initExecutionId ? initExecutionId : getUniqueId(id);
|
|
367
|
+
this[K_EXEC].set('executionId', executionId);
|
|
345
368
|
|
|
346
369
|
this._consumeApi();
|
|
347
370
|
|
|
348
|
-
const content = this._createMessage({ ...
|
|
371
|
+
const content = this._createMessage({ ...runMessage, executionId });
|
|
349
372
|
const broker = this.broker;
|
|
350
373
|
|
|
351
374
|
broker.publish('run', 'run.enter', content);
|
|
@@ -541,10 +564,8 @@ Activity.prototype.getActivityById = function getActivityById(elementId) {
|
|
|
541
564
|
|
|
542
565
|
/** @internal */
|
|
543
566
|
Activity.prototype._runDiscard = function runDiscard(discardContent) {
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
exec.set('executionId', executionId);
|
|
547
|
-
exec.delete('initExecutionId');
|
|
567
|
+
const executionId = getUniqueId(this.id);
|
|
568
|
+
this[K_EXEC].set('executionId', executionId);
|
|
548
569
|
|
|
549
570
|
this._consumeApi();
|
|
550
571
|
|
|
@@ -594,7 +615,7 @@ Activity.prototype._onShakeMessage = function _onShakeMessage(sourceMessage) {
|
|
|
594
615
|
if (this[K_FLAGS].isParallelGateway) {
|
|
595
616
|
const message = cloneMessage(sourceMessage, { join: this.id });
|
|
596
617
|
message.content.sequence.push({ id: this.id, type: this.type });
|
|
597
|
-
return this.broker.publish('event', 'activity.shake.
|
|
618
|
+
return this.broker.publish('event', 'activity.shake.converge', message.content, {
|
|
598
619
|
persistent: false,
|
|
599
620
|
type: 'shake',
|
|
600
621
|
});
|
|
@@ -641,18 +662,6 @@ Activity.prototype._shakeOutbound = function shakeOutbound(sourceMessage) {
|
|
|
641
662
|
for (const t of targets.values()) t.shake(message);
|
|
642
663
|
};
|
|
643
664
|
|
|
644
|
-
/** @internal */
|
|
645
|
-
Activity.prototype._consumeInbound = function consumeInbound() {
|
|
646
|
-
if (!this[K_ACTIVATED]) return;
|
|
647
|
-
|
|
648
|
-
if (this.status || !this._getInboundTriggers().length) return;
|
|
649
|
-
|
|
650
|
-
const inboundQ = this.broker.getQueue('inbound-q');
|
|
651
|
-
const onInbound = this[K_MESSAGE_HANDLERS].onInbound;
|
|
652
|
-
|
|
653
|
-
return inboundQ.assertConsumer(onInbound, { consumerTag: '_run-on-inbound' });
|
|
654
|
-
};
|
|
655
|
-
|
|
656
665
|
/** @internal */
|
|
657
666
|
Activity.prototype._onInbound = function onInbound(routingKey, message) {
|
|
658
667
|
message.ack();
|
|
@@ -660,25 +669,28 @@ Activity.prototype._onInbound = function onInbound(routingKey, message) {
|
|
|
660
669
|
broker.cancel('_run-on-inbound');
|
|
661
670
|
|
|
662
671
|
const content = message.content;
|
|
663
|
-
const inbound = [cloneContent(content)];
|
|
664
672
|
|
|
665
673
|
switch (routingKey) {
|
|
666
|
-
case 'activity.
|
|
667
|
-
|
|
674
|
+
case 'activity.init': {
|
|
675
|
+
const exec = this[K_EXEC];
|
|
676
|
+
exec.set('initialized', (exec.get('initialized') || 0) - 1);
|
|
668
677
|
return this.run({
|
|
678
|
+
initExecutionId: content.executionId,
|
|
679
|
+
id: content.id,
|
|
669
680
|
message: content.message,
|
|
670
|
-
inbound,
|
|
681
|
+
...(content.inbound?.length && { inbound: content.inbound }),
|
|
671
682
|
});
|
|
683
|
+
}
|
|
672
684
|
case 'association.take':
|
|
673
685
|
case 'flow.take':
|
|
674
686
|
case 'activity.restart':
|
|
675
687
|
case 'activity.enter':
|
|
676
688
|
return this.run({
|
|
677
689
|
message: content.message,
|
|
678
|
-
inbound,
|
|
690
|
+
inbound: [cloneContent(content)],
|
|
679
691
|
});
|
|
680
692
|
case 'activity.discard': {
|
|
681
|
-
return this._runDiscard({ inbound });
|
|
693
|
+
return this._runDiscard({ inbound: [cloneContent(content)] });
|
|
682
694
|
}
|
|
683
695
|
}
|
|
684
696
|
};
|
|
@@ -702,18 +714,7 @@ Activity.prototype._onInboundEvent = function onInboundEvent(routingKey, message
|
|
|
702
714
|
case 'activity.link': {
|
|
703
715
|
const linkName = content.message?.linkName;
|
|
704
716
|
if (!this[K_FLAGS].linkNames?.includes(linkName)) break;
|
|
705
|
-
|
|
706
|
-
this.broker.publish(
|
|
707
|
-
'event',
|
|
708
|
-
'activity.enter',
|
|
709
|
-
cloneContent(content, { id: this.id, type: this.type, executionId, state: 'enter', message: { ...content.message } })
|
|
710
|
-
);
|
|
711
|
-
inboundQ.queueMessage(
|
|
712
|
-
{ routingKey: 'activity.relink' },
|
|
713
|
-
cloneContent(content, { id: this.id, executionId, message: { ...content.message } }),
|
|
714
|
-
properties
|
|
715
|
-
);
|
|
716
|
-
return;
|
|
717
|
+
return this.init({ inbound: [cloneContent(content)] });
|
|
717
718
|
}
|
|
718
719
|
case 'association.take':
|
|
719
720
|
case 'flow.take':
|
|
@@ -890,7 +891,7 @@ Activity.prototype._continueRunMessage = function continueRunMessage(routingKey,
|
|
|
890
891
|
case 'run.next':
|
|
891
892
|
message.ack();
|
|
892
893
|
this._pauseRunQ();
|
|
893
|
-
return this.
|
|
894
|
+
return this.consumeInbound();
|
|
894
895
|
}
|
|
895
896
|
|
|
896
897
|
if (!step) message.ack();
|
package/src/constants.js
CHANGED
|
@@ -13,3 +13,9 @@ export const K_STATE_MESSAGE = Symbol.for('stateMessage');
|
|
|
13
13
|
export const K_STATUS = Symbol.for('status');
|
|
14
14
|
export const K_STOPPED = Symbol.for('stopped');
|
|
15
15
|
export const K_TARGETS = Symbol.for('targets');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* State version. Tracks the package major; bump on each major. Recovering an older major triggers
|
|
19
|
+
* migrations. Unstamped legacy states are treated as version 0.
|
|
20
|
+
*/
|
|
21
|
+
export const STATE_VERSION = 18;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
K_STATE_MESSAGE,
|
|
14
14
|
K_STATUS,
|
|
15
15
|
K_STOPPED,
|
|
16
|
+
STATE_VERSION,
|
|
16
17
|
} from '../constants.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -181,6 +182,7 @@ Definition.prototype.resume = function resume(callback) {
|
|
|
181
182
|
*/
|
|
182
183
|
Definition.prototype.getState = function getState() {
|
|
183
184
|
return this._createMessage({
|
|
185
|
+
stateVersion: STATE_VERSION,
|
|
184
186
|
status: this.status,
|
|
185
187
|
stopped: this.stopped,
|
|
186
188
|
counters: this.counters,
|
|
@@ -200,6 +202,11 @@ Definition.prototype.recover = function recover(state) {
|
|
|
200
202
|
if (this.isRunning) throw new Error('cannot recover running definition');
|
|
201
203
|
if (!state) return this;
|
|
202
204
|
|
|
205
|
+
const recoveredVersion = state.stateVersion || 0;
|
|
206
|
+
if (recoveredVersion !== STATE_VERSION) {
|
|
207
|
+
this.logger.debug(`<${this.id}> recover state version ${recoveredVersion} into runtime state version ${STATE_VERSION}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
203
210
|
this[K_STOPPED] = !!state.stopped;
|
|
204
211
|
this[K_STATUS] = state.status;
|
|
205
212
|
|
|
@@ -212,7 +219,7 @@ Definition.prototype.recover = function recover(state) {
|
|
|
212
219
|
this.environment.recover(state.environment);
|
|
213
220
|
|
|
214
221
|
if (state.execution) {
|
|
215
|
-
exec.set('execution', new DefinitionExecution(this, this.context).recover(state.execution));
|
|
222
|
+
exec.set('execution', new DefinitionExecution(this, this.context).recover(state.execution, recoveredVersion));
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
this.broker.recover(state.broker);
|
|
@@ -189,9 +189,10 @@ DefinitionExecution.prototype.resume = function resume() {
|
|
|
189
189
|
/**
|
|
190
190
|
* Restore execution state captured by getState. Reinstates running processes from the snapshot.
|
|
191
191
|
* @param {import('#types').DefinitionExecutionState} [state]
|
|
192
|
+
* @param {number} [recoveredVersion] State version
|
|
192
193
|
* @returns {this}
|
|
193
194
|
*/
|
|
194
|
-
DefinitionExecution.prototype.recover = function recover(state) {
|
|
195
|
+
DefinitionExecution.prototype.recover = function recover(state, recoveredVersion) {
|
|
195
196
|
if (!state) return this;
|
|
196
197
|
this.executionId = state.executionId;
|
|
197
198
|
|
|
@@ -216,7 +217,7 @@ DefinitionExecution.prototype.recover = function recover(state) {
|
|
|
216
217
|
if (!bp) continue;
|
|
217
218
|
|
|
218
219
|
ids.add(bpid);
|
|
219
|
-
bp.recover(bpState);
|
|
220
|
+
bp.recover(bpState, recoveredVersion);
|
|
220
221
|
running.add(bp);
|
|
221
222
|
}
|
|
222
223
|
|
|
@@ -41,9 +41,9 @@ Object.defineProperty(BoundaryEventBehaviour.prototype, 'executionId', {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
Object.defineProperty(BoundaryEventBehaviour.prototype, 'cancelActivity', {
|
|
44
|
+
/** @returns {boolean} */
|
|
44
45
|
get() {
|
|
45
|
-
|
|
46
|
-
return 'cancelActivity' in behaviour ? behaviour.cancelActivity : true;
|
|
46
|
+
return this.activity.behaviour?.cancelActivity ?? true;
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -74,13 +74,17 @@ export function ParallelGatewayBehaviour(activity) {
|
|
|
74
74
|
this.type = activity.type;
|
|
75
75
|
this.activity = activity;
|
|
76
76
|
this.broker = activity.broker;
|
|
77
|
+
/**
|
|
78
|
+
* Inbound taken sequence flow sequences
|
|
79
|
+
* @type {Set<import('#types').ElementMessageContent}
|
|
80
|
+
*/
|
|
77
81
|
this.inbound = new Set();
|
|
78
82
|
|
|
79
|
-
this.isConverging = true;
|
|
80
83
|
this[K_EXECUTE_MESSAGE] = undefined;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
Object.defineProperty(ParallelGatewayBehaviour.prototype, 'executionId', {
|
|
87
|
+
/** @returns {string | undefined} */
|
|
84
88
|
get() {
|
|
85
89
|
return this[K_EXECUTE_MESSAGE]?.content.executionId;
|
|
86
90
|
},
|
|
@@ -112,6 +116,11 @@ ParallelGatewayBehaviour.prototype.execute = function execute(executeMessage) {
|
|
|
112
116
|
}
|
|
113
117
|
};
|
|
114
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Setup peer monitor
|
|
121
|
+
* @param {import('#types').ElementBrokerMessage} executeMessage
|
|
122
|
+
* @returns {void}
|
|
123
|
+
*/
|
|
115
124
|
ParallelGatewayBehaviour.prototype.setup = function setup(executeMessage) {
|
|
116
125
|
const peerIds = new Set([...this.activity[K_PEERS].values()].map((v) => [...v]).flat());
|
|
117
126
|
this[K_TARGETS] = new Map([...peerIds].map((pid) => [pid, this.activity.getActivityById(pid)]));
|
|
@@ -152,9 +161,7 @@ ParallelGatewayBehaviour.prototype.setup = function setup(executeMessage) {
|
|
|
152
161
|
{ consumerTag: '_converging-inbound', exclusive: true, prefetch: 10000 }
|
|
153
162
|
);
|
|
154
163
|
|
|
155
|
-
|
|
156
|
-
this.broker.publish('event', 'activity.converge', cloneContent(executeContent));
|
|
157
|
-
}
|
|
164
|
+
this.broker.publish('event', 'activity.converge', cloneContent(executeContent));
|
|
158
165
|
|
|
159
166
|
return this.broker.publish(
|
|
160
167
|
'execution',
|
|
@@ -197,17 +204,18 @@ ParallelGatewayBehaviour.prototype._stop = function stop() {
|
|
|
197
204
|
this.peerMonitor.stop();
|
|
198
205
|
};
|
|
199
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Peer monitor
|
|
209
|
+
* @param {import('#types').Activity} activity parallel gateway activity
|
|
210
|
+
* @param {Map<string, import('#types').Activity} targets parallel gateway peer target activities
|
|
211
|
+
*/
|
|
200
212
|
function PeerMonitor(activity, targets) {
|
|
201
213
|
this.activity = activity;
|
|
202
214
|
this.id = activity.id;
|
|
203
215
|
this.broker = activity.broker;
|
|
204
|
-
this.running = 0;
|
|
205
|
-
this.index = 0;
|
|
206
|
-
this.discarded = 0;
|
|
207
216
|
this.running = new Map();
|
|
208
217
|
this.watching = new Map();
|
|
209
218
|
this.targets = targets;
|
|
210
|
-
this.touched = new Set();
|
|
211
219
|
this.inbound = [];
|
|
212
220
|
}
|
|
213
221
|
|
|
@@ -217,6 +225,11 @@ Object.defineProperty(PeerMonitor.prototype, 'isRunning', {
|
|
|
217
225
|
},
|
|
218
226
|
});
|
|
219
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Execute peer monitor
|
|
230
|
+
* @param {import('#types').ElementBrokerMessage} executeMessage
|
|
231
|
+
* @returns {number} number of running peers
|
|
232
|
+
*/
|
|
220
233
|
PeerMonitor.prototype.execute = function execute(executeMessage) {
|
|
221
234
|
const message = cloneMessage(executeMessage);
|
|
222
235
|
const inbound = message.content.inbound.pop();
|
|
@@ -231,8 +244,6 @@ PeerMonitor.prototype.execute = function execute(executeMessage) {
|
|
|
231
244
|
preventComplete: true,
|
|
232
245
|
});
|
|
233
246
|
|
|
234
|
-
this.touched.add(inbound.sourceId);
|
|
235
|
-
|
|
236
247
|
for (const target of this.targets.values()) {
|
|
237
248
|
this.monitor(target);
|
|
238
249
|
}
|
|
@@ -240,6 +251,10 @@ PeerMonitor.prototype.execute = function execute(executeMessage) {
|
|
|
240
251
|
return this.running.size;
|
|
241
252
|
};
|
|
242
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Monitor peer activity
|
|
256
|
+
* @param {import('#types').Activity} peerActivity
|
|
257
|
+
*/
|
|
243
258
|
PeerMonitor.prototype.monitor = function monitor(peerActivity) {
|
|
244
259
|
if (this.watching.has(peerActivity.id)) return;
|
|
245
260
|
|
package/src/process/Process.js
CHANGED
|
@@ -190,10 +190,11 @@ Process.prototype.getState = function getState() {
|
|
|
190
190
|
/**
|
|
191
191
|
* Restore process state captured by getState.
|
|
192
192
|
* @param {import('#types').ProcessState} [state]
|
|
193
|
+
* @param {number} [recoveredVersion] State version
|
|
193
194
|
* @returns {this}
|
|
194
195
|
* @throws {Error} when called on a running process
|
|
195
196
|
*/
|
|
196
|
-
Process.prototype.recover = function recover(state) {
|
|
197
|
+
Process.prototype.recover = function recover(state, recoveredVersion) {
|
|
197
198
|
if (this.isRunning) throw new Error(`cannot recover running process <${this.id}>`);
|
|
198
199
|
if (!state) return this;
|
|
199
200
|
|
|
@@ -205,7 +206,7 @@ Process.prototype.recover = function recover(state) {
|
|
|
205
206
|
this.environment.recover(state.environment);
|
|
206
207
|
|
|
207
208
|
if (state.execution) {
|
|
208
|
-
exec.set('execution', new ProcessExecution(this, this.context).recover(state.execution));
|
|
209
|
+
exec.set('execution', new ProcessExecution(this, this.context).recover(state.execution, recoveredVersion));
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
this.broker.recover(state.broker);
|
|
@@ -2,13 +2,14 @@ import { ProcessApi } from '../Api.js';
|
|
|
2
2
|
import { cloneContent, cloneMessage, pushParent } from '../messageHelper.js';
|
|
3
3
|
import { getUniqueId } from '../shared.js';
|
|
4
4
|
import { ActivityTracker } from '../Tracker.js';
|
|
5
|
-
import { K_ACTIVATED, K_COMPLETED, K_EXECUTE_MESSAGE, K_MESSAGE_HANDLERS, K_STATUS, K_STOPPED } from '../constants.js';
|
|
5
|
+
import { K_ACTIVATED, K_COMPLETED, K_EXECUTE_MESSAGE, K_MESSAGE_HANDLERS, K_STATUS, K_STOPPED, STATE_VERSION } from '../constants.js';
|
|
6
6
|
|
|
7
7
|
const K_ACTIVITY_Q = Symbol.for('activityQ');
|
|
8
8
|
const K_ELEMENTS = Symbol.for('elements');
|
|
9
9
|
const K_PARENT = Symbol.for('parent');
|
|
10
10
|
const K_TRACKER = Symbol.for('activity tracker');
|
|
11
11
|
const K_PEERS_DISCOVERED = Symbol.for('peers discovered');
|
|
12
|
+
const K_RECOVERED_VERSION = Symbol.for('recovered version');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Drives the execution of a single process or sub-process: activates children, routes activity
|
|
@@ -166,6 +167,10 @@ ProcessExecution.prototype.resume = function resume() {
|
|
|
166
167
|
|
|
167
168
|
if (this[K_COMPLETED]) return;
|
|
168
169
|
|
|
170
|
+
this._reconcileStartEvents();
|
|
171
|
+
|
|
172
|
+
if (this[K_COMPLETED]) return;
|
|
173
|
+
|
|
169
174
|
if (!postponed.size && status === 'executing') return this._complete('completed');
|
|
170
175
|
};
|
|
171
176
|
|
|
@@ -204,11 +209,13 @@ ProcessExecution.prototype.getState = function getState() {
|
|
|
204
209
|
/**
|
|
205
210
|
* Restore execution state captured by getState.
|
|
206
211
|
* @param {import('#types').ProcessExecutionState} [state]
|
|
212
|
+
* @param {number} [recoveredVersion] State version
|
|
207
213
|
* @returns {this}
|
|
208
214
|
*/
|
|
209
|
-
ProcessExecution.prototype.recover = function recover(state) {
|
|
215
|
+
ProcessExecution.prototype.recover = function recover(state, recoveredVersion) {
|
|
210
216
|
if (!state) return this;
|
|
211
217
|
this.executionId = state.executionId;
|
|
218
|
+
this[K_RECOVERED_VERSION] = recoveredVersion;
|
|
212
219
|
|
|
213
220
|
this[K_STOPPED] = state.stopped;
|
|
214
221
|
this[K_COMPLETED] = state.completed;
|
|
@@ -393,7 +400,7 @@ ProcessExecution.prototype._start = function start() {
|
|
|
393
400
|
|
|
394
401
|
for (const a of startActivities) a.init();
|
|
395
402
|
this[K_STATUS] = 'executing';
|
|
396
|
-
for (const a of startActivities) a.
|
|
403
|
+
for (const a of startActivities) a.consumeInbound();
|
|
397
404
|
|
|
398
405
|
if (!startActivities.size) {
|
|
399
406
|
for (const a of this[K_ELEMENTS].triggeredByEvent) {
|
|
@@ -564,7 +571,7 @@ ProcessExecution.prototype._shakeElements = function shakeElements(fromId) {
|
|
|
564
571
|
if (content.parent.id !== this.id) return;
|
|
565
572
|
|
|
566
573
|
switch (routingKey) {
|
|
567
|
-
case 'activity.shake.
|
|
574
|
+
case 'activity.shake.converge': {
|
|
568
575
|
const join = convergingGateways.get(content.join);
|
|
569
576
|
if (!join) {
|
|
570
577
|
convergingGateways.set(content.join, content);
|
|
@@ -738,17 +745,9 @@ ProcessExecution.prototype._onChildMessage = function onChildMessage(routingKey,
|
|
|
738
745
|
break;
|
|
739
746
|
}
|
|
740
747
|
case 'activity.end': {
|
|
741
|
-
if (!content.isStartEvent) break;
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const startPeers = new Set();
|
|
746
|
-
for (const msg of elements.postponed) {
|
|
747
|
-
const peerId = msg.content.id;
|
|
748
|
-
if (peerId !== content.id && msg.content.isStartEvent) startPeers.add(msg);
|
|
749
|
-
}
|
|
750
|
-
elements.startEventCount = 0;
|
|
751
|
-
for (const msg of startPeers) this._getChildApi(msg).discard();
|
|
748
|
+
if (!(content.isStartEvent || this.getActivityById(content.id)?.isStartEvent)) break;
|
|
749
|
+
if (this[K_ELEMENTS].startEventCount <= 1) break;
|
|
750
|
+
this._discardArmedStartEvents(content.id);
|
|
752
751
|
break;
|
|
753
752
|
}
|
|
754
753
|
case 'activity.error': {
|
|
@@ -1055,6 +1054,43 @@ ProcessExecution.prototype._getChildById = function getChildById(childId) {
|
|
|
1055
1054
|
return this.getActivityById(childId) || this._getFlowById(childId);
|
|
1056
1055
|
};
|
|
1057
1056
|
|
|
1057
|
+
/**
|
|
1058
|
+
* Discard the other armed start events once one mutually exclusive entry point wins.
|
|
1059
|
+
* Resolves the start-event flag from the live activity so recovered pre-flag state is handled.
|
|
1060
|
+
* @internal
|
|
1061
|
+
*/
|
|
1062
|
+
ProcessExecution.prototype._discardArmedStartEvents = function discardArmedStartEvents(winnerId) {
|
|
1063
|
+
const elements = this[K_ELEMENTS];
|
|
1064
|
+
const startPeers = [];
|
|
1065
|
+
for (const msg of elements.postponed) {
|
|
1066
|
+
const peerId = msg.content.id;
|
|
1067
|
+
if (peerId === winnerId) continue;
|
|
1068
|
+
if (this.getActivityById(peerId)?.isStartEvent) startPeers.push(msg);
|
|
1069
|
+
}
|
|
1070
|
+
if (!startPeers.length) return;
|
|
1071
|
+
elements.startEventCount = 0;
|
|
1072
|
+
for (const msg of startPeers) this._getChildApi(msg).discard();
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* On resume of a state from an older major, discard start events left armed when another entry
|
|
1077
|
+
* point already won before recovery. The winning start event's `activity.end` cannot replay, so
|
|
1078
|
+
* the live discard trigger never fires.
|
|
1079
|
+
* @internal
|
|
1080
|
+
*/
|
|
1081
|
+
ProcessExecution.prototype._reconcileStartEvents = function reconcileStartEvents() {
|
|
1082
|
+
const elements = this[K_ELEMENTS];
|
|
1083
|
+
if (elements.startEventCount <= 1) return;
|
|
1084
|
+
if (!(this[K_RECOVERED_VERSION] < STATE_VERSION)) return;
|
|
1085
|
+
|
|
1086
|
+
for (const child of elements.children) {
|
|
1087
|
+
if (child.isStartEvent && child.counters.taken) {
|
|
1088
|
+
this._discardArmedStartEvents();
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1058
1094
|
/** @internal */
|
|
1059
1095
|
ProcessExecution.prototype._getChildApi = function getChildApi(message) {
|
|
1060
1096
|
const content = message.content;
|
package/types/index.d.ts
CHANGED
|
@@ -407,6 +407,8 @@ declare module 'bpmn-elements' {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
export interface DefinitionState extends ElementState {
|
|
410
|
+
/** State version. Absent on states saved before versioning. */
|
|
411
|
+
stateVersion?: number;
|
|
410
412
|
status: DefinitionStatus;
|
|
411
413
|
stopped: boolean;
|
|
412
414
|
executionId?: string;
|
|
@@ -578,6 +580,11 @@ declare module 'bpmn-elements' {
|
|
|
578
580
|
* Subscribe to inbound flows and start consuming the inbound queue.
|
|
579
581
|
* */
|
|
580
582
|
activate(): void;
|
|
583
|
+
/**
|
|
584
|
+
* Assert the inbound queue consumer when the activity has a trigger or is initialized.
|
|
585
|
+
* Idempotent: asserting the consumer again while one is active is a no-op.
|
|
586
|
+
* */
|
|
587
|
+
consumeInbound(): void;
|
|
581
588
|
/**
|
|
582
589
|
* Cancel inbound subscriptions and any pending run/format consumers.
|
|
583
590
|
*/
|
|
@@ -585,8 +592,9 @@ declare module 'bpmn-elements' {
|
|
|
585
592
|
/**
|
|
586
593
|
* Initialise activity executionId and emit init event without starting the run.
|
|
587
594
|
* @param initContent Optional content merged into the init message
|
|
595
|
+
* @param properties Optional message properties merged into the init message properties
|
|
588
596
|
*/
|
|
589
|
-
init(initContent?: Record<string, any
|
|
597
|
+
init(initContent?: Record<string, any>, properties?: import("smqp").MessageProperties): void;
|
|
590
598
|
/**
|
|
591
599
|
* Start running the activity by publishing run.enter and run.start.
|
|
592
600
|
* @param runContent Optional content merged into the run message
|
|
@@ -1046,8 +1054,9 @@ declare module 'bpmn-elements' {
|
|
|
1046
1054
|
resume(): number | undefined;
|
|
1047
1055
|
/**
|
|
1048
1056
|
* Restore execution state captured by getState. Reinstates running processes from the snapshot.
|
|
1057
|
+
* @param recoveredVersion State version
|
|
1049
1058
|
* */
|
|
1050
|
-
recover(state?: DefinitionExecutionState): this;
|
|
1059
|
+
recover(state?: DefinitionExecutionState, recoveredVersion?: number): this;
|
|
1051
1060
|
/**
|
|
1052
1061
|
* Stop the running execution via the api.
|
|
1053
1062
|
*/
|
|
@@ -1472,9 +1481,10 @@ declare module 'bpmn-elements' {
|
|
|
1472
1481
|
getState(): ProcessState;
|
|
1473
1482
|
/**
|
|
1474
1483
|
* Restore process state captured by getState.
|
|
1484
|
+
* @param recoveredVersion State version
|
|
1475
1485
|
* @throws {Error} when called on a running process
|
|
1476
1486
|
*/
|
|
1477
|
-
recover(state?: ProcessState): this;
|
|
1487
|
+
recover(state?: ProcessState, recoveredVersion?: number): this;
|
|
1478
1488
|
/**
|
|
1479
1489
|
* Walk activity graph from the given start id, or every start activity when omitted.
|
|
1480
1490
|
* */
|
|
@@ -1660,8 +1670,9 @@ declare module 'bpmn-elements' {
|
|
|
1660
1670
|
getState(): ProcessExecutionState;
|
|
1661
1671
|
/**
|
|
1662
1672
|
* Restore execution state captured by getState.
|
|
1673
|
+
* @param recoveredVersion State version
|
|
1663
1674
|
* */
|
|
1664
|
-
recover(state?: ProcessExecutionState): this;
|
|
1675
|
+
recover(state?: ProcessExecutionState, recoveredVersion?: number): this;
|
|
1665
1676
|
/**
|
|
1666
1677
|
* Walk activity graph from the given start id, or every start activity when omitted.
|
|
1667
1678
|
* */
|
|
@@ -1945,7 +1956,7 @@ declare module 'bpmn-elements' {
|
|
|
1945
1956
|
environment: Environment;
|
|
1946
1957
|
broker: ElementBroker<Activity>;
|
|
1947
1958
|
get executionId(): string | undefined;
|
|
1948
|
-
get cancelActivity():
|
|
1959
|
+
get cancelActivity(): boolean;
|
|
1949
1960
|
|
|
1950
1961
|
execute(executeMessage: ElementBrokerMessage): void;
|
|
1951
1962
|
}
|
|
@@ -2101,29 +2112,48 @@ declare module 'bpmn-elements' {
|
|
|
2101
2112
|
type: string;
|
|
2102
2113
|
activity: Activity;
|
|
2103
2114
|
broker: ElementBroker<Activity>;
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2115
|
+
/**
|
|
2116
|
+
* Inbound taken sequence flow sequences
|
|
2117
|
+
* */
|
|
2118
|
+
inbound: Set<ElementMessageContent>;
|
|
2119
|
+
get executionId(): string | undefined;
|
|
2107
2120
|
|
|
2108
2121
|
execute(executeMessage: ElementBrokerMessage): void;
|
|
2109
|
-
|
|
2122
|
+
/**
|
|
2123
|
+
* Setup peer monitor
|
|
2124
|
+
* */
|
|
2125
|
+
setup(executeMessage: ElementBrokerMessage): void;
|
|
2110
2126
|
peerMonitor: PeerMonitor | undefined;
|
|
2111
2127
|
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Peer monitor
|
|
2130
|
+
* @param activity parallel gateway activity
|
|
2131
|
+
* @param targets parallel gateway peer target activities
|
|
2132
|
+
*/
|
|
2112
2133
|
class PeerMonitor {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2134
|
+
/**
|
|
2135
|
+
* Peer monitor
|
|
2136
|
+
* @param activity parallel gateway activity
|
|
2137
|
+
* @param targets parallel gateway peer target activities
|
|
2138
|
+
*/
|
|
2139
|
+
constructor(activity: Activity, targets: Map<string, Activity>);
|
|
2140
|
+
activity: Activity;
|
|
2141
|
+
id: string | undefined;
|
|
2142
|
+
broker: ElementBroker<Activity>;
|
|
2117
2143
|
running: Map<any, any>;
|
|
2118
|
-
index: number;
|
|
2119
|
-
discarded: number;
|
|
2120
2144
|
watching: Map<any, any>;
|
|
2121
|
-
targets:
|
|
2122
|
-
touched: Set<any>;
|
|
2145
|
+
targets: Map<string, Activity>;
|
|
2123
2146
|
inbound: any[];
|
|
2124
2147
|
get isRunning(): boolean;
|
|
2125
|
-
|
|
2126
|
-
|
|
2148
|
+
/**
|
|
2149
|
+
* Execute peer monitor
|
|
2150
|
+
* @returns number of running peers
|
|
2151
|
+
*/
|
|
2152
|
+
execute(executeMessage: ElementBrokerMessage): number;
|
|
2153
|
+
/**
|
|
2154
|
+
* Monitor peer activity
|
|
2155
|
+
* */
|
|
2156
|
+
monitor(peerActivity: Activity): void;
|
|
2127
2157
|
stop(): void;
|
|
2128
2158
|
}
|
|
2129
2159
|
/**
|
package/types/interfaces.d.ts
CHANGED
|
@@ -505,6 +505,8 @@ export interface DefinitionExecutionState {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
export interface DefinitionState extends ElementState {
|
|
508
|
+
/** State version. Absent on states saved before versioning. */
|
|
509
|
+
stateVersion?: number;
|
|
508
510
|
status: DefinitionStatus;
|
|
509
511
|
stopped: boolean;
|
|
510
512
|
executionId?: string;
|