@hotmeshio/hotmesh 0.0.55 → 0.0.57

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.
Files changed (182) hide show
  1. package/README.md +1 -1
  2. package/build/modules/enums.js +1 -10
  3. package/build/modules/key.d.ts +0 -38
  4. package/build/modules/key.js +4 -46
  5. package/build/modules/utils.d.ts +0 -8
  6. package/build/modules/utils.js +0 -14
  7. package/build/package.json +11 -4
  8. package/build/services/activities/activity.d.ts +0 -28
  9. package/build/services/activities/activity.js +1 -46
  10. package/build/services/activities/await.js +0 -4
  11. package/build/services/activities/cycle.d.ts +0 -7
  12. package/build/services/activities/cycle.js +1 -16
  13. package/build/services/activities/hook.d.ts +0 -6
  14. package/build/services/activities/hook.js +2 -12
  15. package/build/services/activities/interrupt.js +0 -8
  16. package/build/services/activities/signal.d.ts +0 -6
  17. package/build/services/activities/signal.js +0 -15
  18. package/build/services/activities/trigger.d.ts +0 -4
  19. package/build/services/activities/trigger.js +1 -7
  20. package/build/services/activities/worker.js +0 -4
  21. package/build/services/collator/index.d.ts +0 -70
  22. package/build/services/collator/index.js +1 -91
  23. package/build/services/compiler/deployer.js +6 -38
  24. package/build/services/compiler/index.d.ts +0 -15
  25. package/build/services/compiler/index.js +0 -20
  26. package/build/services/compiler/validator.d.ts +0 -3
  27. package/build/services/compiler/validator.js +0 -25
  28. package/build/services/connector/clients/ioredis.d.ts +2 -2
  29. package/build/services/connector/clients/ioredis.js +0 -2
  30. package/build/services/connector/clients/redis.d.ts +4 -4
  31. package/build/services/connector/clients/redis.js +1 -3
  32. package/build/services/connector/index.d.ts +1 -1
  33. package/build/services/connector/index.js +0 -2
  34. package/build/services/durable/client.d.ts +1 -26
  35. package/build/services/durable/client.js +0 -56
  36. package/build/services/durable/exporter.d.ts +0 -22
  37. package/build/services/durable/exporter.js +1 -30
  38. package/build/services/durable/handle.d.ts +0 -36
  39. package/build/services/durable/handle.js +0 -46
  40. package/build/services/durable/index.d.ts +0 -4
  41. package/build/services/durable/index.js +0 -4
  42. package/build/services/durable/schemas/factory.d.ts +0 -29
  43. package/build/services/durable/schemas/factory.js +0 -29
  44. package/build/services/durable/search.d.ts +1 -36
  45. package/build/services/durable/search.js +56 -56
  46. package/build/services/durable/worker.js +2 -22
  47. package/build/services/durable/workflow.d.ts +0 -114
  48. package/build/services/durable/workflow.js +1 -141
  49. package/build/services/engine/index.d.ts +1 -6
  50. package/build/services/engine/index.js +1 -43
  51. package/build/services/exporter/index.d.ts +0 -27
  52. package/build/services/exporter/index.js +0 -33
  53. package/build/services/hotmesh/index.d.ts +2 -2
  54. package/build/services/hotmesh/index.js +1 -9
  55. package/build/services/logger/index.js +0 -2
  56. package/build/services/mapper/index.d.ts +0 -14
  57. package/build/services/mapper/index.js +0 -14
  58. package/build/services/pipe/functions/date.d.ts +0 -7
  59. package/build/services/pipe/functions/date.js +0 -7
  60. package/build/services/pipe/functions/math.js +0 -2
  61. package/build/services/pipe/index.d.ts +0 -15
  62. package/build/services/pipe/index.js +2 -23
  63. package/build/services/quorum/index.d.ts +0 -7
  64. package/build/services/quorum/index.js +0 -21
  65. package/build/services/reporter/index.d.ts +0 -5
  66. package/build/services/reporter/index.js +0 -9
  67. package/build/services/router/index.d.ts +0 -9
  68. package/build/services/router/index.js +2 -38
  69. package/build/services/serializer/index.js +7 -26
  70. package/build/services/store/cache.d.ts +0 -18
  71. package/build/services/store/cache.js +0 -18
  72. package/build/services/store/clients/ioredis.d.ts +1 -1
  73. package/build/services/store/clients/ioredis.js +0 -1
  74. package/build/services/store/clients/redis.d.ts +1 -1
  75. package/build/services/store/index.d.ts +0 -55
  76. package/build/services/store/index.js +5 -81
  77. package/build/services/stream/clients/ioredis.d.ts +1 -1
  78. package/build/services/stream/clients/ioredis.js +1 -4
  79. package/build/services/stream/clients/redis.d.ts +1 -1
  80. package/build/services/sub/clients/ioredis.d.ts +1 -1
  81. package/build/services/sub/clients/redis.d.ts +1 -1
  82. package/build/services/task/index.d.ts +0 -9
  83. package/build/services/task/index.js +0 -31
  84. package/build/services/telemetry/index.d.ts +0 -7
  85. package/build/services/telemetry/index.js +1 -13
  86. package/build/services/worker/index.d.ts +0 -4
  87. package/build/services/worker/index.js +2 -6
  88. package/build/types/activity.d.ts +0 -81
  89. package/build/types/durable.d.ts +26 -177
  90. package/build/types/exporter.d.ts +0 -13
  91. package/build/types/hotmesh.d.ts +4 -16
  92. package/build/types/hotmesh.js +0 -3
  93. package/build/types/index.d.ts +4 -6
  94. package/build/types/index.js +4 -3
  95. package/build/types/job.d.ts +1 -86
  96. package/build/types/pipe.d.ts +0 -65
  97. package/build/types/quorum.d.ts +15 -10
  98. package/build/types/redis.d.ts +225 -7
  99. package/build/types/redis.js +9 -0
  100. package/build/types/stream.d.ts +0 -58
  101. package/build/types/stream.js +0 -4
  102. package/package.json +11 -4
  103. package/types/durable.ts +131 -4
  104. package/types/hotmesh.ts +3 -6
  105. package/types/index.ts +23 -10
  106. package/types/job.ts +1 -1
  107. package/types/quorum.ts +22 -0
  108. package/types/redis.ts +267 -18
  109. package/build/types/ioredisclient.d.ts +0 -5
  110. package/build/types/ioredisclient.js +0 -5
  111. package/build/types/redisclient.d.ts +0 -26
  112. package/build/types/redisclient.js +0 -2
  113. package/modules/enums.ts +0 -62
  114. package/modules/errors.ts +0 -280
  115. package/modules/key.ts +0 -101
  116. package/modules/storage.ts +0 -3
  117. package/modules/utils.ts +0 -242
  118. package/services/activities/activity.ts +0 -589
  119. package/services/activities/await.ts +0 -113
  120. package/services/activities/cycle.ts +0 -115
  121. package/services/activities/hook.ts +0 -197
  122. package/services/activities/index.ts +0 -19
  123. package/services/activities/interrupt.ts +0 -172
  124. package/services/activities/signal.ts +0 -148
  125. package/services/activities/trigger.ts +0 -295
  126. package/services/activities/worker.ts +0 -107
  127. package/services/collator/README.md +0 -102
  128. package/services/collator/index.ts +0 -291
  129. package/services/compiler/deployer.ts +0 -504
  130. package/services/compiler/index.ts +0 -98
  131. package/services/compiler/validator.ts +0 -158
  132. package/services/connector/clients/ioredis.ts +0 -57
  133. package/services/connector/clients/redis.ts +0 -72
  134. package/services/connector/index.ts +0 -42
  135. package/services/durable/client.ts +0 -266
  136. package/services/durable/connection.ts +0 -10
  137. package/services/durable/exporter.ts +0 -232
  138. package/services/durable/handle.ts +0 -160
  139. package/services/durable/index.ts +0 -27
  140. package/services/durable/schemas/factory.ts +0 -2358
  141. package/services/durable/search.ts +0 -196
  142. package/services/durable/worker.ts +0 -401
  143. package/services/durable/workflow.ts +0 -557
  144. package/services/engine/index.ts +0 -761
  145. package/services/exporter/index.ts +0 -146
  146. package/services/hotmesh/index.ts +0 -237
  147. package/services/logger/index.ts +0 -79
  148. package/services/mapper/index.ts +0 -89
  149. package/services/pipe/functions/array.ts +0 -78
  150. package/services/pipe/functions/bitwise.ts +0 -27
  151. package/services/pipe/functions/conditional.ts +0 -35
  152. package/services/pipe/functions/date.ts +0 -220
  153. package/services/pipe/functions/index.ts +0 -27
  154. package/services/pipe/functions/json.ts +0 -11
  155. package/services/pipe/functions/logical.ts +0 -11
  156. package/services/pipe/functions/math.ts +0 -217
  157. package/services/pipe/functions/number.ts +0 -75
  158. package/services/pipe/functions/object.ts +0 -98
  159. package/services/pipe/functions/string.ts +0 -86
  160. package/services/pipe/functions/symbol.ts +0 -39
  161. package/services/pipe/functions/unary.ts +0 -19
  162. package/services/pipe/index.ts +0 -216
  163. package/services/quorum/index.ts +0 -319
  164. package/services/reporter/index.ts +0 -387
  165. package/services/router/index.ts +0 -426
  166. package/services/serializer/README.md +0 -10
  167. package/services/serializer/index.ts +0 -285
  168. package/services/store/cache.ts +0 -172
  169. package/services/store/clients/ioredis.ts +0 -145
  170. package/services/store/clients/redis.ts +0 -191
  171. package/services/store/index.ts +0 -1091
  172. package/services/stream/clients/ioredis.ts +0 -157
  173. package/services/stream/clients/redis.ts +0 -158
  174. package/services/stream/index.ts +0 -58
  175. package/services/sub/clients/ioredis.ts +0 -83
  176. package/services/sub/clients/redis.ts +0 -74
  177. package/services/sub/index.ts +0 -25
  178. package/services/task/index.ts +0 -250
  179. package/services/telemetry/index.ts +0 -273
  180. package/services/worker/index.ts +0 -248
  181. package/types/ioredisclient.ts +0 -10
  182. package/types/redisclient.ts +0 -30
@@ -1,295 +0,0 @@
1
- import { DuplicateJobError } from '../../modules/errors';
2
- import {
3
- formatISODate,
4
- getTimeSeries,
5
- guid } from '../../modules/utils';
6
- import { Activity } from './activity';
7
- import { CollatorService } from '../collator';
8
- import { EngineService } from '../engine';
9
- import { Pipe } from '../pipe';
10
- import { ReporterService } from '../reporter';
11
- import { MDATA_SYMBOLS } from '../serializer';
12
- import { TelemetryService } from '../telemetry';
13
- import {
14
- ActivityData,
15
- ActivityMetadata,
16
- ActivityType,
17
- TriggerActivity } from '../../types/activity';
18
- import { JobState, ExtensionType } from '../../types/job';
19
- import { RedisMulti } from '../../types/redis';
20
- import { StringScalarType } from '../../types/serializer';
21
- import { WorkListTaskType } from '../../types/task';
22
-
23
- class Trigger extends Activity {
24
- config: TriggerActivity;
25
-
26
- constructor(
27
- config: ActivityType,
28
- data: ActivityData,
29
- metadata: ActivityMetadata,
30
- hook: ActivityData | null,
31
- engine: EngineService,
32
- context?: JobState) {
33
- super(config, data, metadata, hook, engine, context);
34
- }
35
-
36
- async process(options?: ExtensionType): Promise<string> {
37
- this.logger.debug('trigger-process', { subscribes: this.config.subscribes });
38
- let telemetry: TelemetryService;
39
- try {
40
- this.setLeg(2);
41
- await this.getState();
42
-
43
- telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
44
- telemetry.startJobSpan();
45
- telemetry.startActivitySpan(this.leg);
46
- this.mapJobData();
47
- await this.setStateNX();
48
- this.adjacencyList = await this.filterAdjacent();
49
- await this.setStatus(this.adjacencyList.length);
50
-
51
- this.bindSearchData(options);
52
- this.bindMarkerData(options);
53
-
54
- const multi = this.store.getMulti();
55
- await this.setState(multi);
56
- await this.setStats(multi);
57
- await this.registerJobDependency(multi);
58
- await multi.exec();
59
-
60
- //if the parent (spawner) chose not to await,
61
- // emit the job_id as the data payload { job_id }
62
- this.execAdjacentParent();
63
-
64
- telemetry.mapActivityAttributes();
65
- const jobStatus = Number(this.context.metadata.js);
66
- telemetry.setJobAttributes({ 'app.job.jss': jobStatus });
67
- const attrs: StringScalarType = { 'app.job.jss': jobStatus };
68
- const messageIds = await this.transition(this.adjacencyList, jobStatus);
69
- if (messageIds.length) {
70
- attrs['app.activity.mids'] = messageIds.join(',')
71
- }
72
- telemetry.setActivityAttributes(attrs);
73
- return this.context.metadata.jid;
74
- } catch (error) {
75
- if (error instanceof DuplicateJobError) {
76
- this.logger.error('duplicate-job-error', { job_id: error.jobId });
77
- } else {
78
- this.logger.error('trigger-process-error', { ...error });
79
- }
80
- telemetry.setActivityError(error.message);
81
- throw error;
82
- } finally {
83
- telemetry?.endJobSpan();
84
- telemetry?.endActivitySpan();
85
- this.logger.debug('trigger-process-end', { subscribes: this.config.subscribes, jid: this.context.metadata.jid, gid: this.context.metadata.gid });
86
- }
87
- }
88
-
89
- safeKey(key:string): string {
90
- return `_${key}`;
91
- }
92
-
93
- bindSearchData(options?: ExtensionType): void {
94
- if (options?.search) {
95
- Object.keys(options.search).forEach((key) => {
96
- this.context.data[this.safeKey(key)] = options.search[key].toString();
97
- });
98
- }
99
- }
100
-
101
- bindMarkerData(options?: ExtensionType): void {
102
- if (options?.marker) {
103
- Object.keys(options.marker).forEach((key) => {
104
- if (key.startsWith('-')) {
105
- this.context.data[key] = options.marker[key].toString();
106
- }
107
- });
108
- }
109
- }
110
-
111
- async setStatus(amount: number): Promise<void> {
112
- this.context.metadata.js = amount;
113
- }
114
-
115
- async execAdjacentParent() {
116
- if (this.context.metadata.px) {
117
- const timestamp = formatISODate(new Date());
118
- const jobStartedConfirmationMessage = {
119
- metadata: this.context.metadata,
120
- data: {
121
- job_id: this.context.metadata.jid,
122
- jc: timestamp,
123
- ju: timestamp,
124
- }
125
- };
126
- await this.engine.execAdjacentParent(this.context, jobStartedConfirmationMessage);
127
- }
128
- }
129
-
130
- createInputContext(): Partial<JobState> {
131
- const input = {
132
- [this.metadata.aid]: {
133
- input: { data: this.data }
134
- },
135
- '$self': {
136
- input: { data: this.data },
137
- output: { data: this.data }
138
- },
139
- } as Partial<JobState>;
140
- return input
141
- }
142
-
143
- async getState(): Promise<void> {
144
- const inputContext = this.createInputContext();
145
- const jobId = this.resolveJobId(inputContext);
146
- const jobKey = this.resolveJobKey(inputContext);
147
-
148
- const utc = formatISODate(new Date());
149
- const { id, version } = await this.engine.getVID();
150
- this.initDimensionalAddress(CollatorService.getDimensionalSeed());
151
- const activityMetadata = {
152
- ...this.metadata,
153
- jid: jobId,
154
- key: jobKey,
155
- as: CollatorService.getTriggerSeed(),
156
- };
157
- this.context = {
158
- metadata: {
159
- ...this.metadata,
160
- gid: guid(),
161
- ngn: this.context.metadata.ngn,
162
- pj: this.context.metadata.pj,
163
- pg: this.context.metadata.pg,
164
- pd: this.context.metadata.pd,
165
- pa: this.context.metadata.pa,
166
- px: this.context.metadata.px,
167
- app: id,
168
- vrs: version,
169
- tpc: this.config.subscribes,
170
- trc: this.context.metadata.trc,
171
- spn: this.context.metadata.spn,
172
- jid: jobId,
173
- dad: CollatorService.getDimensionalSeed(), //top-level job implicitly uses `,0`
174
- key: jobKey,
175
- jc: utc,
176
- ju: utc,
177
- ts: getTimeSeries(this.resolveGranularity()),
178
- js: 0,
179
- },
180
- data: {},
181
- [this.metadata.aid]: {
182
- input: {
183
- data: this.data,
184
- metadata: activityMetadata
185
- },
186
- output: {
187
- data: this.data,
188
- metadata: activityMetadata
189
- },
190
- settings: { data: {} },
191
- errors: { data: {} },
192
- },
193
- };
194
- this.context['$self'] = this.context[this.metadata.aid];
195
- this.context['$job'] = this.context; //NEVER call STRINGIFY! (circular)
196
- }
197
-
198
- bindJobMetadataPaths(): string[] {
199
- return MDATA_SYMBOLS.JOB.KEYS.map((key) => `metadata/${key}`);
200
- }
201
-
202
- bindActivityMetadataPaths(): string[] {
203
- return MDATA_SYMBOLS.ACTIVITY.KEYS.map((key) => `output/metadata/${key}`);
204
- }
205
-
206
- resolveGranularity(): string {
207
- return this.config.stats?.granularity || ReporterService.DEFAULT_GRANULARITY;
208
- }
209
-
210
- getJobStatus(): number {
211
- return this.context.metadata.js;
212
- }
213
-
214
- resolveJobId(context: Partial<JobState>): string {
215
- const jobId = this.config.stats?.id;
216
- return jobId ? Pipe.resolve(jobId, context) : guid();
217
- }
218
-
219
- resolveJobKey(context: Partial<JobState>): string {
220
- const jobKey = this.config.stats?.key;
221
- return jobKey ? Pipe.resolve(jobKey, context) : '';
222
- }
223
-
224
- async setStateNX(): Promise<void> {
225
- const jobId = this.context.metadata.jid;
226
- if (!await this.store.setStateNX(jobId, this.engine.appId)) {
227
- throw new DuplicateJobError(jobId);
228
- }
229
- }
230
-
231
- /**
232
- * Registers this job as a dependent of the parent job; when the
233
- * parent job is interrupted, this job will be interrupted
234
- */
235
- async registerJobDependency(multi?: RedisMulti): Promise<void> {
236
- const depKey = this.config.stats?.parent ?? this.context.metadata.pj;
237
- let resolvedDepKey = depKey ? Pipe.resolve(depKey, this.context) : '';
238
- const adjKey = this.config.stats?.adjacent;
239
- let resolvedAdjKey = depKey ? Pipe.resolve(adjKey, this.context) : '';
240
- if (!resolvedDepKey) {
241
- resolvedDepKey = this.context.metadata.pj;
242
- }
243
- if (resolvedDepKey) {
244
- const isParentOrigin = (resolvedDepKey === this.context.metadata.pj) || (resolvedDepKey === resolvedAdjKey);
245
- let type: WorkListTaskType;
246
- if (isParentOrigin) {
247
- if (this.context.metadata.px) {
248
- type = 'child'
249
- } else {
250
- type = 'expire-child'
251
- }
252
- } else {
253
- type = 'expire';
254
- }
255
- await this.store.registerJobDependency(
256
- type,
257
- resolvedDepKey,
258
- this.context.metadata.tpc,
259
- this.context.metadata.jid,
260
- this.context.metadata.gid,
261
- this.context.metadata.pd,
262
- multi,
263
- );
264
- }
265
- if (resolvedAdjKey && resolvedAdjKey !== resolvedDepKey) {
266
- await this.store.registerJobDependency(
267
- 'child',
268
- resolvedAdjKey,
269
- this.context.metadata.tpc,
270
- this.context.metadata.jid,
271
- this.context.metadata.gid,
272
- this.context.metadata.pd,
273
- multi,
274
- );
275
- }
276
- }
277
-
278
- async setStats(multi?: RedisMulti): Promise<void> {
279
- const md = this.context.metadata;
280
- if (md.key && this.config.stats?.measures) {
281
- const config = await this.engine.getVID();
282
- const reporter = new ReporterService(config, this.store, this.logger);
283
- await this.store.setStats(
284
- md.key,
285
- md.jid,
286
- md.ts,
287
- reporter.resolveTriggerStatistics(this.config, this.context),
288
- config,
289
- multi
290
- );
291
- }
292
- }
293
- }
294
-
295
- export { Trigger };
@@ -1,107 +0,0 @@
1
- import {
2
- GenerationalError,
3
- GetStateError,
4
- InactiveJobError } from '../../modules/errors';
5
- import { guid } from '../../modules/utils';
6
- import { Activity } from './activity';
7
- import { CollatorService } from '../collator';
8
- import { EngineService } from '../engine';
9
- import { Pipe } from '../pipe';
10
- import { TelemetryService } from '../telemetry';
11
- import {
12
- ActivityData,
13
- ActivityMetadata,
14
- ActivityType,
15
- WorkerActivity } from '../../types/activity';
16
- import { JobState } from '../../types/job';
17
- import { MultiResponseFlags, RedisMulti } from '../../types/redis';
18
- import { StreamData} from '../../types/stream';
19
-
20
- class Worker extends Activity {
21
- config: WorkerActivity;
22
-
23
- constructor(
24
- config: ActivityType,
25
- data: ActivityData,
26
- metadata: ActivityMetadata,
27
- hook: ActivityData | null,
28
- engine: EngineService,
29
- context?: JobState) {
30
- super(config, data, metadata, hook, engine, context);
31
- }
32
-
33
- //******** INITIAL ENTRY POINT (A) ********//
34
- async process(): Promise<string> {
35
- this.logger.debug('worker-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
36
- let telemetry: TelemetryService;
37
- try {
38
- await this.verifyEntry();
39
-
40
- telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
41
- telemetry.startActivitySpan(this.leg);
42
- this.mapInputData();
43
-
44
- //save state and authorize reentry
45
- const multi = this.store.getMulti();
46
- //todo: await this.registerTimeout();
47
- const messageId = await this.execActivity(multi);
48
- await CollatorService.authorizeReentry(this, multi);
49
- await this.setState(multi);
50
- await this.setStatus(0, multi);
51
- const multiResponse = await multi.exec() as MultiResponseFlags;
52
-
53
- //telemetry
54
- telemetry.mapActivityAttributes();
55
- const jobStatus = this.resolveStatus(multiResponse);
56
- telemetry.setActivityAttributes({
57
- 'app.activity.mid': messageId,
58
- 'app.job.jss': jobStatus
59
- });
60
-
61
- return this.context.metadata.aid;
62
- } catch (error) {
63
- if (error instanceof InactiveJobError) {
64
- this.logger.error('await-inactive-job-error', { ...error });
65
- return;
66
- } else if (error instanceof GenerationalError) {
67
- this.logger.info('process-event-generational-job-error', { ...error });
68
- return;
69
- } else if (error instanceof GetStateError) {
70
- this.logger.error('worker-get-state-error', { ...error });
71
- return;
72
- } else {
73
- this.logger.error('worker-process-error', { ...error });
74
- }
75
- telemetry.setActivityError(error.message);
76
- throw error;
77
- } finally {
78
- telemetry?.endActivitySpan();
79
- this.logger.debug('worker-process-end', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
80
- }
81
- }
82
-
83
- async execActivity(multi: RedisMulti): Promise<string> {
84
- const topic = Pipe.resolve(this.config.subtype, this.context);
85
- const streamData: StreamData = {
86
- metadata: {
87
- guid: guid(),
88
- jid: this.context.metadata.jid,
89
- gid: this.context.metadata.gid,
90
- dad: this.metadata.dad,
91
- aid: this.metadata.aid,
92
- topic,
93
- spn: this.context['$self'].output.metadata.l1s,
94
- trc: this.context.metadata.trc,
95
- },
96
- data: this.context.data
97
- };
98
- if (this.config.retry) {
99
- streamData.policies = {
100
- retry: this.config.retry
101
- };
102
- }
103
- return (await this.engine.router?.publishMessage(topic, streamData, multi)) as string;
104
- }
105
- }
106
-
107
- export { Worker };
@@ -1,102 +0,0 @@
1
- # Activity State Tracking
2
-
3
- This document explains how HotMesh guarantees activity state management using a 15-digit integer. This mechanism is crucial for monitoring and controlling the activity's lifecycle across two legs (Leg1 and Leg2) and ensures that duplicate activities are never processed. The process further ensures that catastrophic, in-process failures will be automatically resolved with respect to idempotency, makig it possible to build 100% fault-tolerant workflows.
4
-
5
- ## Overview
6
-
7
- The 15-digit integer serves as a semaphore, starting with the value `999000000000000`. Each digit has a specific purpose:
8
-
9
- - The first three digits track the lifecycle status for Legs 1 and 2.
10
- - The last 12 digits represent 1 million possible dimensional threads that can be spawned by this activity.
11
-
12
- ### Digit Details
13
-
14
- 999000000000000
15
- ^-------------- Leg1 Entry Status
16
- ^------------- Leg1 Exit Status
17
- ^^^^^^------ Leg2 Dimensional Thread Entry Count
18
- ^^^^^^ Leg2 Dimensional Thread Exit Count
19
- ^------------ Leg2 Exit Status
20
-
21
- >*Dimensional Threads* isolate and track those activities in the workflow that run in a *cycle*. They ensure that no naming collisions occur, even if the same activity is run multiple times.
22
-
23
- ## Leg1 Lifecycle
24
-
25
- ### Initialization
26
-
27
- The parent activity initializes the integer value (`999000000000000`) and saves it to Redis upon saving its own state. The parent then adds the child activity to the proper stream channel for eventual processing.
28
-
29
- >Streams are used when executing an activity (such as transitioning to a child activity) as they guarantee that the child activity will be fully created and initialized before the request is marked for deletion. Even if the system has a catastrophic failure, the chain of custody can be guaranteed through the use of streams when the system comes online.
30
-
31
- ### Beginning of Leg1
32
-
33
- When Leg1 begins (when an activity is dequeued from its stream channel), the integer is decremented by 100 trillion:
34
-
35
- HINCRBY -100000000000000
36
-
37
- Result:
38
-
39
- 899000000000000
40
-
41
- ### Conclusion of Leg1
42
-
43
- At the conclusion of Leg1, the integer is decremented by 10 trillion:
44
-
45
- HINCRBY -10000000000000
46
-
47
- Result:
48
-
49
- 889000000000000
50
-
51
- ### Error Handling
52
-
53
- - If the value upon entering is `799############` (after decrementing the integer), Leg1 began but crashed before completion. The activity will perform the necessary cleanup and re-run the activity.
54
- - If the value upon entering is `789############` (after decrementing the integer), Leg1 completed successfully and the activity should end. It is likely that the system crashed last time before acking and deleting. Verify transitions succeeded and resolve as necessary.
55
-
56
- ## Leg2 Lifecycle
57
-
58
- Leg2 supports multiple inputs and repeated responses. A worker can respond with a 'pending' status, allowing multiple inputs.
59
-
60
- ### Beginning of Leg2
61
-
62
- On the first Leg2 input, the integer is incremented by 1 million:
63
-
64
- HINCRBY 1000000
65
-
66
- Result:
67
-
68
- 889000001000000
69
-
70
- ### Pending Status
71
-
72
- If the call status is 'pending', the digit is updated to reflect the successful completion of Leg2 but the third digit from the left will be untouched as the activity is pending and Leg2 should not end quite yet:
73
-
74
- HINCRBY 1
75
-
76
- Result:
77
-
78
- 889000001000001
79
-
80
- ### Success Status
81
-
82
- If the next message call to Leg2 entry is 'success', the process will begin as before, and the integer will be incremented by 1 million:
83
-
84
- HINCRBY 1000000
85
-
86
- Result:
87
-
88
- 889000002000001
89
-
90
- But because this is not a 'pending' message, the integer should be updated differently, targeting both the dimensional thread counter as well as the third digit from the left that tracks Leg2 status (e.g., `1 - 1000000000 = -999999999`).
91
-
92
- HINCRBY -999999999999
93
-
94
- Result:
95
-
96
- 888000002000002
97
-
98
- >If an additional Leg2 call were to be received after this point, the result would be `888000003000002`. However, becuase the third digit is `8`, Leg2 access is not allowed and the system will conclude by decrementing the value it just set and returning immediately, so that the calling worker can mark and delete the stream entry.
99
-
100
- ## Conclusion
101
-
102
- This 15-digit integer allows sophisticated tracking of complex activities across two legs, with error handling and support for cycles using multiple dimensional threads.