@hotmeshio/hotmesh 0.18.1 → 0.19.1
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/build/modules/enums.d.ts +1 -0
- package/build/modules/enums.js +3 -2
- package/build/package.json +1 -1
- package/build/services/collator/index.js +8 -0
- package/build/services/engine/dispatch.js +19 -1
- package/build/services/hotmesh/quorum.js +13 -1
- package/build/services/quorum/index.js +1 -0
- package/build/services/stream/index.d.ts +2 -0
- package/build/services/stream/providers/nats/nats.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.js +3 -0
- package/build/services/stream/providers/postgres/postgres.d.ts +1 -0
- package/build/services/stream/providers/postgres/postgres.js +3 -0
- package/build/types/quorum.d.ts +4 -0
- package/package.json +1 -1
package/build/modules/enums.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ export declare const MAX_STREAM_RETRIES: number;
|
|
|
133
133
|
export declare const MAX_DELAY = 2147483647;
|
|
134
134
|
export declare const HMSH_MAX_RETRIES: number;
|
|
135
135
|
export declare const HMSH_POISON_MESSAGE_THRESHOLD: number;
|
|
136
|
+
export declare const HMSH_MAX_CYCLES: number;
|
|
136
137
|
export declare const HMSH_MAX_TIMEOUT_MS: number;
|
|
137
138
|
export declare const HMSH_GRADUATED_INTERVAL_MS: number;
|
|
138
139
|
/**
|
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 = exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = exports.HMSH_EXPIRE_DURATION = void 0;
|
|
3
|
+
exports.HMSH_RESERVATION_TIMEOUT_S = exports.HMSH_ENGINE_CONCURRENCY = 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_MAX_CYCLES = 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 = exports.HMSH_EXPIRE_DURATION = exports.HMSH_RESERVATION_TIMEOUT_MAX_S = void 0;
|
|
5
5
|
/**
|
|
6
6
|
* Determines the log level for the application. The default is 'info'.
|
|
7
7
|
*/
|
|
@@ -143,6 +143,7 @@ exports.MAX_STREAM_RETRIES = parseInt(process.env.MAX_STREAM_RETRIES, 10) || 2;
|
|
|
143
143
|
exports.MAX_DELAY = 2147483647; // Maximum allowed delay in milliseconds for setTimeout
|
|
144
144
|
exports.HMSH_MAX_RETRIES = parseInt(process.env.HMSH_MAX_RETRIES, 10) || 3;
|
|
145
145
|
exports.HMSH_POISON_MESSAGE_THRESHOLD = parseInt(process.env.HMSH_POISON_MESSAGE_THRESHOLD, 10) || 5;
|
|
146
|
+
exports.HMSH_MAX_CYCLES = parseInt(process.env.HMSH_MAX_CYCLES, 10) || 10000;
|
|
146
147
|
exports.HMSH_MAX_TIMEOUT_MS = parseInt(process.env.HMSH_MAX_TIMEOUT_MS, 10) || 60000;
|
|
147
148
|
exports.HMSH_GRADUATED_INTERVAL_MS = parseInt(process.env.HMSH_GRADUATED_INTERVAL_MS, 10) || 5000;
|
|
148
149
|
// DURABLE
|
package/build/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CollatorService = void 0;
|
|
4
4
|
const errors_1 = require("../../modules/errors");
|
|
5
|
+
const enums_1 = require("../../modules/enums");
|
|
5
6
|
const collator_1 = require("../../types/collator");
|
|
6
7
|
class CollatorService {
|
|
7
8
|
/**
|
|
@@ -38,6 +39,13 @@ class CollatorService {
|
|
|
38
39
|
const ancestors = activity.config.ancestors;
|
|
39
40
|
const ancestorIndex = ancestors.indexOf(targetActivityId);
|
|
40
41
|
const dimensions = activity.metadata.dad.split(','); //e.g., `,0,0,1,0`
|
|
42
|
+
// Safety cap: prevent infinite cycle loops
|
|
43
|
+
const currentCycle = parseInt(dimensions[ancestorIndex] || '0', 10);
|
|
44
|
+
if (currentCycle >= enums_1.HMSH_MAX_CYCLES) {
|
|
45
|
+
throw new Error(`Cycle limit exceeded for job ${activity.context.metadata.jid} ` +
|
|
46
|
+
`(${currentCycle} >= HMSH_MAX_CYCLES=${enums_1.HMSH_MAX_CYCLES}) at DAD ${activity.metadata.dad}. ` +
|
|
47
|
+
`Set HMSH_MAX_CYCLES env var to increase the limit.`);
|
|
48
|
+
}
|
|
41
49
|
dimensions.length = ancestorIndex + 1;
|
|
42
50
|
dimensions.push('0');
|
|
43
51
|
return dimensions.join(',');
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.processStreamMessage = void 0;
|
|
19
|
+
const errors_1 = require("../../modules/errors");
|
|
19
20
|
const stream_1 = require("../../types/stream");
|
|
20
21
|
async function processStreamMessage(instance, streamData) {
|
|
21
22
|
instance.logger.debug('engine-process', {
|
|
@@ -86,7 +87,24 @@ async function dispatchAwait(instance, streamData, context) {
|
|
|
86
87
|
spn: streamData.metadata.spn,
|
|
87
88
|
};
|
|
88
89
|
const handler = (await instance.initActivity(streamData.metadata.topic, streamData.data, context));
|
|
89
|
-
|
|
90
|
+
try {
|
|
91
|
+
await handler.process();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof errors_1.DuplicateJobError) {
|
|
95
|
+
// The child workflow already exists from a prior spawn attempt
|
|
96
|
+
// (crash recovery). This AWAIT message is a replay — the child
|
|
97
|
+
// will deliver its RESULT back to the parent via the normal path.
|
|
98
|
+
// Acknowledge the message so it doesn't loop.
|
|
99
|
+
instance.logger.info('dispatch-await-child-exists', {
|
|
100
|
+
childJobId: error.jobId,
|
|
101
|
+
parentJobId: streamData.metadata.jid,
|
|
102
|
+
parentDad: streamData.metadata.dad,
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
90
108
|
}
|
|
91
109
|
async function dispatchResult(instance, streamData, context) {
|
|
92
110
|
const handler = (await instance.initActivity(`.${context.metadata.aid}`, streamData.data, context));
|
|
@@ -25,7 +25,19 @@ async function throttle(instance, options) {
|
|
|
25
25
|
if (options.guid) {
|
|
26
26
|
throttleMessage.guid = options.guid;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
// Scope determines channel targeting:
|
|
29
|
+
// 'engines' → set topic to null (workers suppress null-topic throttles)
|
|
30
|
+
// 'workers' → requires a topic to route to worker channels only
|
|
31
|
+
// 'all' / undefined → default behavior (engines + workers)
|
|
32
|
+
if (options.scope === 'engines') {
|
|
33
|
+
throttleMessage.topic = null;
|
|
34
|
+
}
|
|
35
|
+
else if (options.scope === 'workers' && !options.topic) {
|
|
36
|
+
// Workers-only without a specific topic: broadcast to all worker channels
|
|
37
|
+
// by omitting topic (workers receive on global channel) but NOT setting null
|
|
38
|
+
// (which would suppress workers). The existing behavior is correct here.
|
|
39
|
+
}
|
|
40
|
+
else if (options.topic !== undefined) {
|
|
29
41
|
throttleMessage.topic = options.topic;
|
|
30
42
|
}
|
|
31
43
|
await instance.engine.store.setThrottleRate(throttleMessage);
|
|
@@ -142,6 +142,7 @@ class QuorumService {
|
|
|
142
142
|
timestamp: (0, utils_1.formatISODate)(new Date()),
|
|
143
143
|
inited: this.engine.inited,
|
|
144
144
|
throttle: this.engine.router.throttle,
|
|
145
|
+
is_scout: this.engine.stream.isScout(),
|
|
145
146
|
reclaimDelay: this.engine.router.reclaimDelay,
|
|
146
147
|
reclaimCount: this.engine.router.reclaimCount,
|
|
147
148
|
system: await (0, utils_1.getSystemHealth)(),
|
|
@@ -44,6 +44,8 @@ export declare abstract class StreamService<ClientProvider extends ProviderClien
|
|
|
44
44
|
}): Promise<StreamMessage[]>;
|
|
45
45
|
reservationTimeout: number;
|
|
46
46
|
abstract getStreamStats(streamName: string): Promise<StreamStats>;
|
|
47
|
+
/** Whether this instance currently holds the scout role. */
|
|
48
|
+
abstract isScout(): boolean;
|
|
47
49
|
abstract getStreamDepth(streamName: string): Promise<number>;
|
|
48
50
|
abstract getStreamDepths(streamName: {
|
|
49
51
|
stream: string;
|
|
@@ -33,6 +33,7 @@ declare class NatsStreamService extends StreamService<NatsClientType, NatsPubAck
|
|
|
33
33
|
maxRetries?: number;
|
|
34
34
|
limit?: number;
|
|
35
35
|
}): Promise<StreamMessage[]>;
|
|
36
|
+
isScout(): boolean;
|
|
36
37
|
getStreamStats(streamName: string): Promise<StreamStats>;
|
|
37
38
|
getStreamDepth(streamName: string): Promise<number>;
|
|
38
39
|
getStreamDepths(streamNames: {
|
|
@@ -153,6 +153,9 @@ class NatsStreamService extends index_1.StreamService {
|
|
|
153
153
|
async retryMessages(streamName, groupName, options) {
|
|
154
154
|
return [];
|
|
155
155
|
}
|
|
156
|
+
isScout() {
|
|
157
|
+
return false; // NATS doesn't use the scout pattern
|
|
158
|
+
}
|
|
156
159
|
async getStreamStats(streamName) {
|
|
157
160
|
try {
|
|
158
161
|
const info = await this.jsm.streams.info(streamName);
|
|
@@ -96,6 +96,7 @@ declare class PostgresStreamService extends StreamService<PostgresClientType & P
|
|
|
96
96
|
maxRetries?: number;
|
|
97
97
|
limit?: number;
|
|
98
98
|
}): Promise<StreamMessage[]>;
|
|
99
|
+
isScout(): boolean;
|
|
99
100
|
getStreamStats(streamName: string): Promise<StreamStats>;
|
|
100
101
|
getStreamDepth(streamName: string): Promise<number>;
|
|
101
102
|
getStreamDepths(streamNames: {
|
|
@@ -290,6 +290,9 @@ class PostgresStreamService extends index_1.StreamService {
|
|
|
290
290
|
async retryMessages(streamName, groupName, options) {
|
|
291
291
|
return Messages.retryMessages(streamName, groupName, options);
|
|
292
292
|
}
|
|
293
|
+
isScout() {
|
|
294
|
+
return this.scoutManager?.isCurrentlyScout() ?? false;
|
|
295
|
+
}
|
|
293
296
|
async getStreamStats(streamName) {
|
|
294
297
|
const target = this.resolveStreamTarget(streamName);
|
|
295
298
|
return Stats.getStreamStats(this.streamClient, target.tableName, target.streamName, this.logger);
|
package/build/types/quorum.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export type ThrottleOptions = {
|
|
|
29
29
|
guid?: string;
|
|
30
30
|
/** target a worker quorum */
|
|
31
31
|
topic?: string;
|
|
32
|
+
/** target engines only, workers only, or all (default: 'all') */
|
|
33
|
+
scope?: 'engines' | 'workers' | 'all';
|
|
32
34
|
/** delay in milliseconds: 0 = resume, -1 = pause, >0 = delay per message */
|
|
33
35
|
throttle: number;
|
|
34
36
|
};
|
|
@@ -78,6 +80,8 @@ export interface QuorumProfile {
|
|
|
78
80
|
reclaimDelay?: number;
|
|
79
81
|
/** Max messages to reclaim per cycle. */
|
|
80
82
|
reclaimCount?: number;
|
|
83
|
+
/** Whether this engine currently holds the scout role (polls for delayed messages). */
|
|
84
|
+
is_scout?: boolean;
|
|
81
85
|
/** Host-level memory, CPU, and network stats. */
|
|
82
86
|
system?: SystemHealth;
|
|
83
87
|
/** Stringified worker callback function (only if `signature: true` in rollcall). */
|