@hotmeshio/hotmesh 0.14.6 → 0.14.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -111
- package/build/modules/enums.d.ts +41 -0
- package/build/modules/enums.js +43 -2
- package/build/package.json +1 -1
- package/build/services/activities/worker.js +14 -0
- package/build/services/durable/exporter.js +2 -2
- package/build/services/durable/schemas/factory.d.ts +1 -19
- package/build/services/durable/schemas/factory.js +122 -524
- package/build/services/durable/worker.js +25 -14
- package/build/services/router/config/index.d.ts +2 -2
- package/build/services/router/config/index.js +3 -1
- package/build/services/router/consumption/index.d.ts +6 -5
- package/build/services/router/consumption/index.js +47 -25
- package/build/services/router/error-handling/index.d.ts +1 -1
- package/build/services/router/error-handling/index.js +12 -11
- package/build/services/store/providers/postgres/exporter-sql.d.ts +4 -0
- package/build/services/store/providers/postgres/exporter-sql.js +11 -1
- package/build/services/store/providers/postgres/postgres.js +24 -0
- package/build/services/stream/providers/postgres/postgres.d.ts +6 -0
- package/build/services/stream/providers/postgres/postgres.js +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,34 +29,17 @@ Install the package:
|
|
|
29
29
|
npm install @hotmeshio/hotmesh
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The repo includes a `docker-compose.yml` that starts Postgres
|
|
32
|
+
The repo includes a `docker-compose.yml` that starts Postgres and a development container:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
docker compose up -d
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
See the [Durable API reference](https://docs.hotmesh.io/classes/services_durable.Durable.html) for the full API surface — workflows, activities, signals, child workflows, and more.
|
|
39
39
|
|
|
40
|
-
##
|
|
40
|
+
## Writing workflows
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
// activities.ts (shared between both approaches)
|
|
46
|
-
export async function checkInventory(itemId: string): Promise<number> {
|
|
47
|
-
return getInventoryCount(itemId);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export async function reserveItem(itemId: string, quantity: number): Promise<string> {
|
|
51
|
-
return createReservation(itemId, quantity);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function notifyBackorder(itemId: string): Promise<void> {
|
|
55
|
-
await sendBackorderEmail(itemId);
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Option 1: Code
|
|
42
|
+
**Define the workflow** — plain TypeScript with branching, loops, and error handling. Activities are proxied so their results are checkpointed and replayed on restart.
|
|
60
43
|
|
|
61
44
|
```typescript
|
|
62
45
|
// workflows.ts
|
|
@@ -76,124 +59,72 @@ export async function orderWorkflow(itemId: string, qty: number) {
|
|
|
76
59
|
return 'backordered';
|
|
77
60
|
}
|
|
78
61
|
}
|
|
62
|
+
```
|
|
79
63
|
|
|
80
|
-
|
|
81
|
-
|
|
64
|
+
**Start a worker** — connects to Postgres and begins processing workflows on the given task queue.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// worker.ts
|
|
68
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
69
|
+
import { Client as Postgres } from 'pg';
|
|
70
|
+
import { orderWorkflow } from './workflows';
|
|
82
71
|
|
|
83
72
|
const connection = {
|
|
84
73
|
class: Postgres,
|
|
85
74
|
options: { connectionString: 'postgresql://localhost:5432/mydb' }
|
|
86
75
|
};
|
|
87
76
|
|
|
88
|
-
await Durable.Worker.create({
|
|
77
|
+
const worker = await Durable.Worker.create({
|
|
89
78
|
connection,
|
|
90
79
|
taskQueue: 'orders',
|
|
91
80
|
workflow: orderWorkflow,
|
|
92
|
-
activities,
|
|
93
81
|
});
|
|
94
82
|
|
|
83
|
+
await worker.run();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Run a workflow** — start an execution and await its result. The client can run in a different process, container, or server.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// client.ts
|
|
90
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
91
|
+
import { Client as Postgres } from 'pg';
|
|
92
|
+
|
|
93
|
+
const connection = {
|
|
94
|
+
class: Postgres,
|
|
95
|
+
options: { connectionString: 'postgresql://localhost:5432/mydb' }
|
|
96
|
+
};
|
|
97
|
+
|
|
95
98
|
const client = new Durable.Client({ connection });
|
|
96
99
|
const handle = await client.workflow.start({
|
|
97
100
|
args: ['item-123', 5],
|
|
98
101
|
taskQueue: 'orders',
|
|
99
102
|
workflowName: 'orderWorkflow',
|
|
100
|
-
workflowId: 'order-456'
|
|
103
|
+
workflowId: 'order-456',
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
const result = await handle.result();
|
|
104
107
|
```
|
|
105
108
|
|
|
106
|
-
###
|
|
107
|
-
|
|
108
|
-
```yaml
|
|
109
|
-
# order.yaml
|
|
110
|
-
activities:
|
|
111
|
-
trigger:
|
|
112
|
-
type: trigger
|
|
113
|
-
|
|
114
|
-
checkInventory:
|
|
115
|
-
type: worker
|
|
116
|
-
topic: inventory.check
|
|
117
|
-
|
|
118
|
-
reserveItem:
|
|
119
|
-
type: worker
|
|
120
|
-
topic: inventory.reserve
|
|
121
|
-
|
|
122
|
-
notifyBackorder:
|
|
123
|
-
type: worker
|
|
124
|
-
topic: inventory.backorder.notify
|
|
125
|
-
|
|
126
|
-
transitions:
|
|
127
|
-
trigger:
|
|
128
|
-
- to: checkInventory
|
|
129
|
-
|
|
130
|
-
checkInventory:
|
|
131
|
-
- to: reserveItem
|
|
132
|
-
conditions:
|
|
133
|
-
match:
|
|
134
|
-
- expected: true
|
|
135
|
-
actual:
|
|
136
|
-
'@pipe':
|
|
137
|
-
- ['{checkInventory.output.data.availableQty}', '{trigger.output.data.requestedQty}']
|
|
138
|
-
- ['{@conditional.gte}']
|
|
139
|
-
|
|
140
|
-
- to: notifyBackorder
|
|
141
|
-
conditions:
|
|
142
|
-
match:
|
|
143
|
-
- expected: false
|
|
144
|
-
actual:
|
|
145
|
-
'@pipe':
|
|
146
|
-
- ['{checkInventory.output.data.availableQty}', '{trigger.output.data.requestedQty}']
|
|
147
|
-
- ['{@conditional.gte}']
|
|
148
|
-
```
|
|
109
|
+
### Activities
|
|
149
110
|
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// main.ts (reuses same activities.ts)
|
|
153
|
-
import * as activities from './activities';
|
|
111
|
+
Activities are your side-effectful functions — database calls, API requests, anything non-deterministic. HotMesh checkpoints their results so they're never re-executed on replay.
|
|
154
112
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
topic: 'inventory.check',
|
|
161
|
-
connection,
|
|
162
|
-
callback: async (data) => {
|
|
163
|
-
const availableQty = await activities.checkInventory(data.data.itemId);
|
|
164
|
-
return { metadata: { ...data.metadata }, data: { availableQty } };
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
topic: 'inventory.reserve',
|
|
169
|
-
connection,
|
|
170
|
-
callback: async (data) => {
|
|
171
|
-
const reservationId = await activities.reserveItem(data.data.itemId, data.data.quantity);
|
|
172
|
-
return { metadata: { ...data.metadata }, data: { reservationId } };
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
topic: 'inventory.backorder.notify',
|
|
177
|
-
connection,
|
|
178
|
-
callback: async (data) => {
|
|
179
|
-
await activities.notifyBackorder(data.data.itemId);
|
|
180
|
-
return { metadata: { ...data.metadata } };
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
});
|
|
113
|
+
```typescript
|
|
114
|
+
// activities.ts
|
|
115
|
+
export async function checkInventory(itemId: string): Promise<number> {
|
|
116
|
+
return getInventoryCount(itemId);
|
|
117
|
+
}
|
|
185
118
|
|
|
186
|
-
|
|
187
|
-
|
|
119
|
+
export async function reserveItem(itemId: string, quantity: number): Promise<string> {
|
|
120
|
+
return createReservation(itemId, quantity);
|
|
121
|
+
}
|
|
188
122
|
|
|
189
|
-
|
|
190
|
-
itemId
|
|
191
|
-
|
|
192
|
-
});
|
|
123
|
+
export async function notifyBackorder(itemId: string): Promise<void> {
|
|
124
|
+
await sendBackorderEmail(itemId);
|
|
125
|
+
}
|
|
193
126
|
```
|
|
194
127
|
|
|
195
|
-
Both compile to the same distributed execution model.
|
|
196
|
-
|
|
197
128
|
## Common patterns
|
|
198
129
|
|
|
199
130
|
All snippets below run inside a workflow function (like `orderWorkflow` above). Durable methods are available as static imports:
|
|
@@ -321,6 +252,12 @@ There is no proprietary dashboard. Workflow state lives in Postgres, so use what
|
|
|
321
252
|
- **Logging** — set `HMSH_LOGLEVEL` (`debug`, `info`, `warn`, `error`, `silent`) to control log verbosity.
|
|
322
253
|
- **OpenTelemetry** — set `HMSH_TELEMETRY=true` to emit spans and metrics. Plug in any OTel-compatible collector.
|
|
323
254
|
|
|
255
|
+
## YAML workflows
|
|
256
|
+
|
|
257
|
+
HotMesh also supports a declarative YAML syntax. The same activities run in both modes — the difference is compilation speed. YAML workflows compile ~10x faster because the execution graph is declared upfront rather than discovered through replay. The tradeoff is expressiveness: YAML uses a functional pipe syntax for conditions and transformations instead of native TypeScript control flow.
|
|
258
|
+
|
|
259
|
+
See the [Quick Start guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/quickstart.md) for YAML examples and the `tests/functional/` directory for working implementations.
|
|
260
|
+
|
|
324
261
|
## Architecture
|
|
325
262
|
|
|
326
263
|
For a deep dive into the transactional execution model — how every step is crash-safe, how the monotonic collation ledger guarantees exactly-once delivery, and how cycles and retries remain correct under arbitrary failure — see the [Collation Design Document](https://github.com/hotmeshio/sdk-typescript/blob/main/services/collator/README.md). The symbolic system (how to design workflows) and lifecycle details (how to deploy workflows) are covered in the [Architectural Overview](https://zenodo.org/records/12168558).
|
package/build/modules/enums.d.ts
CHANGED
|
@@ -162,6 +162,21 @@ export declare const HMSH_XCLAIM_DELAY_MS: number;
|
|
|
162
162
|
export declare const HMSH_XCLAIM_COUNT: number;
|
|
163
163
|
export declare const HMSH_XPENDING_COUNT: number;
|
|
164
164
|
export declare const HMSH_BATCH_SIZE: number;
|
|
165
|
+
/**
|
|
166
|
+
* Minimum batch size under adaptive scaling (default: 1).
|
|
167
|
+
*
|
|
168
|
+
* When stream depth is high, the adaptive logic reduces batch size
|
|
169
|
+
* to relieve back-pressure. This value is the floor — the smallest
|
|
170
|
+
* batch the system will fetch per consume cycle.
|
|
171
|
+
*
|
|
172
|
+
* - 1 (default): fully serial under max stress, safest
|
|
173
|
+
* - 2: retains some parallelism while limiting contention
|
|
174
|
+
*
|
|
175
|
+
* Both values produce equivalent throughput in practice (~233s for
|
|
176
|
+
* 1000 concurrent workflows). The reduction from the configured
|
|
177
|
+
* HMSH_BATCH_SIZE is what matters most — the floor is a safety net.
|
|
178
|
+
*/
|
|
179
|
+
export declare const HMSH_BATCH_SIZE_MIN: number;
|
|
165
180
|
/**
|
|
166
181
|
* Postgres stream reservation timeout in seconds (default: 30).
|
|
167
182
|
*
|
|
@@ -197,6 +212,32 @@ export declare const HMSH_BATCH_SIZE: number;
|
|
|
197
212
|
* HMSH_RESERVATION_TIMEOUT_S=30 (default)
|
|
198
213
|
*/
|
|
199
214
|
export declare const HMSH_RESERVATION_TIMEOUT_S: number;
|
|
215
|
+
/**
|
|
216
|
+
* Maximum reservation timeout in seconds for adaptive scaling (default: 1800).
|
|
217
|
+
*
|
|
218
|
+
* This is the ceiling for the adaptive reservation timeout — how far the
|
|
219
|
+
* system is allowed to stretch under sustained load. The adaptive logic
|
|
220
|
+
* only uses what it needs based on stream depth; this value defines the
|
|
221
|
+
* upper bound, not the steady state.
|
|
222
|
+
*
|
|
223
|
+
* The tradeoff is recovery time after a consumer crash: if a consumer
|
|
224
|
+
* reserves a message and dies, that message is unavailable until the
|
|
225
|
+
* timeout expires. A higher ceiling means longer recovery from crashes
|
|
226
|
+
* but prevents duplicate delivery under heavy sustained load.
|
|
227
|
+
*
|
|
228
|
+
* In practice, crashes are rare and the delay is bounded. The cost of
|
|
229
|
+
* a ceiling that is too low — duplicate delivery, collation errors,
|
|
230
|
+
* wasted CPU, workflow stalls — is far higher than a slightly longer
|
|
231
|
+
* recovery window after a crash.
|
|
232
|
+
*
|
|
233
|
+
* **Tuning guidance:**
|
|
234
|
+
* - Dedicated infrastructure with ample CPU: lower ceiling is fine (600s)
|
|
235
|
+
* - Shared/multi-tenant or CPU-constrained: use the default (1800s)
|
|
236
|
+
* - Long-running batch imports or large workflow graphs: increase (3600s+)
|
|
237
|
+
* - Cloud deployments without CPU contention: the adaptive logic will
|
|
238
|
+
* naturally stay near the starting timeout and rarely approach the ceiling
|
|
239
|
+
*/
|
|
240
|
+
export declare const HMSH_RESERVATION_TIMEOUT_MAX_S: number;
|
|
200
241
|
export declare const HMSH_EXPIRE_DURATION: number;
|
|
201
242
|
export declare const HMSH_FIDELITY_SECONDS: number;
|
|
202
243
|
export declare const HMSH_SCOUT_INTERVAL_SECONDS: number;
|
package/build/modules/enums.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.HMSH_ROUTER_POLL_FALLBACK_INTERVAL = exports.HMSH_NOTIFY_PAYLOAD_LIMIT = exports.DEFAULT_TASK_QUEUE = exports.HMSH_GUID_SIZE = exports.HMSH_ROUTER_SCOUT_INTERVAL_MS = exports.HMSH_ROUTER_SCOUT_INTERVAL_SECONDS = void 0;
|
|
3
|
+
exports.HMSH_EXPIRE_DURATION = exports.HMSH_RESERVATION_TIMEOUT_MAX_S = exports.HMSH_RESERVATION_TIMEOUT_S = exports.HMSH_BATCH_SIZE_MIN = exports.HMSH_BATCH_SIZE = exports.HMSH_XPENDING_COUNT = exports.HMSH_XCLAIM_COUNT = exports.HMSH_XCLAIM_DELAY_MS = exports.HMSH_BLOCK_TIME_MS = exports.HMSH_DURABLE_INITIAL_INTERVAL = exports.HMSH_DURABLE_EXP_BACKOFF = exports.HMSH_DURABLE_MAX_INTERVAL = exports.HMSH_DURABLE_MAX_ATTEMPTS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_POISON_MESSAGE_THRESHOLD = exports.HMSH_MAX_RETRIES = exports.MAX_DELAY = exports.MAX_STREAM_RETRIES = exports.INITIAL_STREAM_BACKOFF = exports.MAX_STREAM_BACKOFF = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_DEPLOYMENT_PAUSE = exports.HMSH_DEPLOYMENT_DELAY = exports.HMSH_ACTIVATION_MAX_RETRY = exports.HMSH_QUORUM_DELAY_MS = exports.HMSH_QUORUM_ROLLCALL_CYCLES = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_DURABLE_RETRYABLE = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_WAIT = exports.HMSH_CODE_DURABLE_CONTINUE = exports.HMSH_CODE_DURABLE_PROXY = exports.HMSH_CODE_DURABLE_CHILD = exports.HMSH_CODE_DURABLE_ALL = exports.HMSH_CODE_DURABLE_SLEEP = exports.HMSH_CODE_UNACKED = exports.HMSH_CODE_TIMEOUT = exports.HMSH_CODE_UNKNOWN = exports.HMSH_CODE_INTERRUPT = exports.HMSH_CODE_NOTFOUND = exports.HMSH_CODE_PENDING = exports.HMSH_CODE_SUCCESS = exports.HMSH_PENDING_SIGNAL_EXPIRE = exports.HMSH_SIGNAL_EXPIRE = exports.HMSH_TELEMETRY = exports.HMSH_LOGLEVEL = void 0;
|
|
4
|
+
exports.HMSH_ROUTER_POLL_FALLBACK_INTERVAL = exports.HMSH_NOTIFY_PAYLOAD_LIMIT = exports.DEFAULT_TASK_QUEUE = exports.HMSH_GUID_SIZE = exports.HMSH_ROUTER_SCOUT_INTERVAL_MS = exports.HMSH_ROUTER_SCOUT_INTERVAL_SECONDS = exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = void 0;
|
|
5
5
|
/**
|
|
6
6
|
* Determines the log level for the application. The default is 'info'.
|
|
7
7
|
*/
|
|
@@ -179,6 +179,21 @@ exports.HMSH_XCLAIM_DELAY_MS = parseInt(process.env.HMSH_XCLAIM_DELAY_MS, 10) ||
|
|
|
179
179
|
exports.HMSH_XCLAIM_COUNT = parseInt(process.env.HMSH_XCLAIM_COUNT, 10) || 3;
|
|
180
180
|
exports.HMSH_XPENDING_COUNT = parseInt(process.env.HMSH_XPENDING_COUNT, 10) || 10;
|
|
181
181
|
exports.HMSH_BATCH_SIZE = parseInt(process.env.HMSH_BATCH_SIZE, 10) || 10;
|
|
182
|
+
/**
|
|
183
|
+
* Minimum batch size under adaptive scaling (default: 1).
|
|
184
|
+
*
|
|
185
|
+
* When stream depth is high, the adaptive logic reduces batch size
|
|
186
|
+
* to relieve back-pressure. This value is the floor — the smallest
|
|
187
|
+
* batch the system will fetch per consume cycle.
|
|
188
|
+
*
|
|
189
|
+
* - 1 (default): fully serial under max stress, safest
|
|
190
|
+
* - 2: retains some parallelism while limiting contention
|
|
191
|
+
*
|
|
192
|
+
* Both values produce equivalent throughput in practice (~233s for
|
|
193
|
+
* 1000 concurrent workflows). The reduction from the configured
|
|
194
|
+
* HMSH_BATCH_SIZE is what matters most — the floor is a safety net.
|
|
195
|
+
*/
|
|
196
|
+
exports.HMSH_BATCH_SIZE_MIN = parseInt(process.env.HMSH_BATCH_SIZE_MIN, 10) || 1;
|
|
182
197
|
/**
|
|
183
198
|
* Postgres stream reservation timeout in seconds (default: 30).
|
|
184
199
|
*
|
|
@@ -214,6 +229,32 @@ exports.HMSH_BATCH_SIZE = parseInt(process.env.HMSH_BATCH_SIZE, 10) || 10;
|
|
|
214
229
|
* HMSH_RESERVATION_TIMEOUT_S=30 (default)
|
|
215
230
|
*/
|
|
216
231
|
exports.HMSH_RESERVATION_TIMEOUT_S = parseInt(process.env.HMSH_RESERVATION_TIMEOUT_S, 10) || 30;
|
|
232
|
+
/**
|
|
233
|
+
* Maximum reservation timeout in seconds for adaptive scaling (default: 1800).
|
|
234
|
+
*
|
|
235
|
+
* This is the ceiling for the adaptive reservation timeout — how far the
|
|
236
|
+
* system is allowed to stretch under sustained load. The adaptive logic
|
|
237
|
+
* only uses what it needs based on stream depth; this value defines the
|
|
238
|
+
* upper bound, not the steady state.
|
|
239
|
+
*
|
|
240
|
+
* The tradeoff is recovery time after a consumer crash: if a consumer
|
|
241
|
+
* reserves a message and dies, that message is unavailable until the
|
|
242
|
+
* timeout expires. A higher ceiling means longer recovery from crashes
|
|
243
|
+
* but prevents duplicate delivery under heavy sustained load.
|
|
244
|
+
*
|
|
245
|
+
* In practice, crashes are rare and the delay is bounded. The cost of
|
|
246
|
+
* a ceiling that is too low — duplicate delivery, collation errors,
|
|
247
|
+
* wasted CPU, workflow stalls — is far higher than a slightly longer
|
|
248
|
+
* recovery window after a crash.
|
|
249
|
+
*
|
|
250
|
+
* **Tuning guidance:**
|
|
251
|
+
* - Dedicated infrastructure with ample CPU: lower ceiling is fine (600s)
|
|
252
|
+
* - Shared/multi-tenant or CPU-constrained: use the default (1800s)
|
|
253
|
+
* - Long-running batch imports or large workflow graphs: increase (3600s+)
|
|
254
|
+
* - Cloud deployments without CPU contention: the adaptive logic will
|
|
255
|
+
* naturally stay near the starting timeout and rarely approach the ceiling
|
|
256
|
+
*/
|
|
257
|
+
exports.HMSH_RESERVATION_TIMEOUT_MAX_S = parseInt(process.env.HMSH_RESERVATION_TIMEOUT_MAX_S, 10) || 1800;
|
|
217
258
|
// TASK WORKER
|
|
218
259
|
exports.HMSH_EXPIRE_DURATION = parseInt(process.env.HMSH_EXPIRE_DURATION, 10) || 1;
|
|
219
260
|
const BASE_FIDELITY_SECONDS = 5;
|
package/build/package.json
CHANGED
|
@@ -181,6 +181,20 @@ class Worker extends activity_1.Activity {
|
|
|
181
181
|
retry: this.config.retry,
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
|
+
// Propagate per-activity retry config as _streamRetryConfig so
|
|
185
|
+
// the engine-level retry mechanism uses it for exponential backoff.
|
|
186
|
+
// The durable module's maximumAttempts means max retries
|
|
187
|
+
// (total executions = 1 + maximumAttempts), while the engine's
|
|
188
|
+
// max_retry_attempts means total attempts. Add 1 to align.
|
|
189
|
+
if (jobData?.maximumAttempts || jobData?.backoffCoefficient || jobData?.maximumInterval || jobData?.initialInterval) {
|
|
190
|
+
const durableMaxAttempts = jobData.maximumAttempts ?? 50;
|
|
191
|
+
streamData._streamRetryConfig = {
|
|
192
|
+
max_retry_attempts: durableMaxAttempts + 1,
|
|
193
|
+
backoff_coefficient: jobData.backoffCoefficient ?? 10,
|
|
194
|
+
maximum_interval_seconds: jobData.maximumInterval ?? 120,
|
|
195
|
+
initialInterval: jobData.initialInterval ?? 1,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
184
198
|
return (await this.engine.router?.publishMessage(topic, streamData, transaction));
|
|
185
199
|
}
|
|
186
200
|
}
|
|
@@ -474,10 +474,10 @@ class ExporterService {
|
|
|
474
474
|
const activityArgsField = this.resolveSymbolField(symbolSets, 'activity_trigger', 'activity_trigger/output/data/arguments');
|
|
475
475
|
const workflowArgsField = this.resolveSymbolField(symbolSets, 'trigger', 'trigger/output/data/arguments');
|
|
476
476
|
// ── 1. Enrich activity inputs ──
|
|
477
|
-
|
|
477
|
+
{
|
|
478
478
|
const activityEvents = execution.events.filter((e) => e.event_type === 'activity_task_scheduled' || e.event_type === 'activity_task_completed' || e.event_type === 'activity_task_failed');
|
|
479
479
|
if (activityEvents.length > 0) {
|
|
480
|
-
const { byJobId, byNameIndex } = await this.store.getActivityInputs(workflowId, activityArgsField);
|
|
480
|
+
const { byJobId, byNameIndex } = await this.store.getActivityInputs(workflowId, activityArgsField || '');
|
|
481
481
|
for (const evt of activityEvents) {
|
|
482
482
|
const attrs = evt.attributes;
|
|
483
483
|
let input = attrs.timeline_key ? byJobId.get(attrs.timeline_key) : undefined;
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
*********** HOTMESH 'DURABLE' MODULE APPLICATION GRAPH **********
|
|
3
|
-
*
|
|
4
|
-
* This HotMesh application spec uses 50 activities and 25 transitions
|
|
5
|
-
* to model a durable workflow engine using a pluggable backend.
|
|
6
|
-
*
|
|
7
|
-
* This YAML file can also serve as a useful starting point for building
|
|
8
|
-
* Integration/BPM/Workflow servers in general (MuleSoft, etc) without the need
|
|
9
|
-
* for a physical application server.
|
|
10
|
-
*
|
|
11
|
-
* Possible use cases include:
|
|
12
|
-
* * Orchestration servers
|
|
13
|
-
* * Integration servers
|
|
14
|
-
* * BPMN engines
|
|
15
|
-
* * Reentrant process servers
|
|
16
|
-
* * Service Meshes
|
|
17
|
-
* * Master Data Management systems
|
|
18
|
-
*/
|
|
19
|
-
declare const APP_VERSION = "11";
|
|
1
|
+
declare const APP_VERSION = "12";
|
|
20
2
|
declare const APP_ID = "durable";
|
|
21
3
|
/**
|
|
22
4
|
* returns a new durable workflow schema
|