@rotorsoft/act 1.32.16 → 1.32.17
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/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +10 -46
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/internal/close-cycle.d.ts +45 -3
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +1 -1
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +15 -27
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +8 -20
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/index.cjs +68 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -110
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2834,6 +2834,31 @@ var CircuitBreaker = class {
|
|
|
2834
2834
|
};
|
|
2835
2835
|
|
|
2836
2836
|
// src/internal/close-cycle.ts
|
|
2837
|
+
var CLOSE_CATCH_UP_LIMIT = 1e3;
|
|
2838
|
+
var CLOSE_CATCH_UP_PASSES = 20;
|
|
2839
|
+
async function catch_up_correlation(cursor, correlate, until) {
|
|
2840
|
+
for (let pass = 0; pass < CLOSE_CATCH_UP_PASSES && cursor.checkpoint < until; pass++) {
|
|
2841
|
+
const before = cursor.checkpoint;
|
|
2842
|
+
cursor.arm();
|
|
2843
|
+
await correlate(CLOSE_CATCH_UP_LIMIT);
|
|
2844
|
+
if (cursor.checkpoint <= before) break;
|
|
2845
|
+
}
|
|
2846
|
+
return cursor.checkpoint;
|
|
2847
|
+
}
|
|
2848
|
+
var CloseLock = class {
|
|
2849
|
+
_tails = /* @__PURE__ */ new Map();
|
|
2850
|
+
/** Run `work` after every earlier close of `stream` has settled. */
|
|
2851
|
+
run(stream, work) {
|
|
2852
|
+
const prev = this._tails.get(stream) ?? Promise.resolve();
|
|
2853
|
+
const next = prev.then(work, work);
|
|
2854
|
+
this._tails.set(stream, next);
|
|
2855
|
+
const cleanup = () => {
|
|
2856
|
+
if (this._tails.get(stream) === next) this._tails.delete(stream);
|
|
2857
|
+
};
|
|
2858
|
+
next.then(cleanup, cleanup);
|
|
2859
|
+
return next;
|
|
2860
|
+
}
|
|
2861
|
+
};
|
|
2837
2862
|
var SAFETY_PROBE_PAGE_SIZE = 1e3;
|
|
2838
2863
|
async function run_close_cycle(targets, deps) {
|
|
2839
2864
|
const target_map = new Map(targets.map((t) => [t.stream, t]));
|
|
@@ -2855,6 +2880,8 @@ async function run_close_cycle(targets, deps) {
|
|
|
2855
2880
|
deps,
|
|
2856
2881
|
() => run_full_closes(full, target_map, deps, skipped)
|
|
2857
2882
|
);
|
|
2883
|
+
const retired = [...truncated.entries()].filter(([, r]) => r.committed.name === TOMBSTONE_EVENT).map(([stream]) => stream);
|
|
2884
|
+
if (retired.length) deps.forget_subscribed?.(retired);
|
|
2858
2885
|
for (const [stream, entry] of windowed_result) truncated.set(stream, entry);
|
|
2859
2886
|
return { truncated, skipped };
|
|
2860
2887
|
}
|
|
@@ -3013,12 +3040,12 @@ async function scan_stream_heads(streams) {
|
|
|
3013
3040
|
}
|
|
3014
3041
|
return out;
|
|
3015
3042
|
}
|
|
3016
|
-
async function partition_by_safety(stream_info, reactive_events_size, skipped, page_size,
|
|
3043
|
+
async function partition_by_safety(stream_info, reactive_events_size, skipped, page_size, catch_up_correlation2) {
|
|
3017
3044
|
if (reactive_events_size === 0) return [...stream_info.keys()];
|
|
3018
3045
|
let needed = -1;
|
|
3019
3046
|
for (const info of stream_info.values())
|
|
3020
3047
|
needed = Math.max(needed, info.max_id);
|
|
3021
|
-
const checkpoint = await
|
|
3048
|
+
const checkpoint = await catch_up_correlation2(needed);
|
|
3022
3049
|
const uncorrelated = /* @__PURE__ */ new Set();
|
|
3023
3050
|
for (const [stream, info] of stream_info) {
|
|
3024
3051
|
if (checkpoint < info.max_id) uncorrelated.add(stream);
|
|
@@ -4949,8 +4976,6 @@ function build_handle_batch(logger) {
|
|
|
4949
4976
|
}
|
|
4950
4977
|
|
|
4951
4978
|
// src/act.ts
|
|
4952
|
-
var CLOSE_CATCH_UP_LIMIT = 1e3;
|
|
4953
|
-
var CLOSE_CATCH_UP_PASSES = 20;
|
|
4954
4979
|
function validate_only_lanes(options, lanes) {
|
|
4955
4980
|
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
4956
4981
|
const declared = /* @__PURE__ */ new Set(["default", ...lanes.map((l) => l.name)]);
|
|
@@ -5103,28 +5128,10 @@ var Act = class {
|
|
|
5103
5128
|
_handle_batch;
|
|
5104
5129
|
/** Declared drain lanes. */
|
|
5105
5130
|
_lanes;
|
|
5106
|
-
/**
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
*/
|
|
5111
|
-
_close_locks = /* @__PURE__ */ new Map();
|
|
5112
|
-
/**
|
|
5113
|
-
* Run `work` under the per-stream close lock. Serializes close
|
|
5114
|
-
* critical sections for the same stream (windowed prunes and full
|
|
5115
|
-
* closes) while letting different streams proceed in parallel.
|
|
5116
|
-
*/
|
|
5117
|
-
_with_close_lock(stream, work) {
|
|
5118
|
-
const prev = this._close_locks.get(stream) ?? Promise.resolve();
|
|
5119
|
-
const next = prev.then(work, work);
|
|
5120
|
-
this._close_locks.set(stream, next);
|
|
5121
|
-
const cleanup = () => {
|
|
5122
|
-
if (this._close_locks.get(stream) === next)
|
|
5123
|
-
this._close_locks.delete(stream);
|
|
5124
|
-
};
|
|
5125
|
-
next.then(cleanup, cleanup);
|
|
5126
|
-
return next;
|
|
5127
|
-
}
|
|
5131
|
+
/** Serializes close critical sections per stream. */
|
|
5132
|
+
_close_lock = new CloseLock();
|
|
5133
|
+
/** Every lane a controller can exist for: `"default"` plus each declared one. */
|
|
5134
|
+
_declared_lanes;
|
|
5128
5135
|
/** Drain lanes declared via `.withLane(...)`. Implicit default not included. */
|
|
5129
5136
|
get lanes() {
|
|
5130
5137
|
return this._lanes;
|
|
@@ -5154,6 +5161,7 @@ var Act = class {
|
|
|
5154
5161
|
this._states = states;
|
|
5155
5162
|
this._batch_handlers = batch_handlers;
|
|
5156
5163
|
this._lanes = lanes;
|
|
5164
|
+
this._declared_lanes = ["default", ...lanes.map((l) => l.name)];
|
|
5157
5165
|
validate_only_lanes(options, lanes);
|
|
5158
5166
|
this._ports = options.scoped ?? default_scope();
|
|
5159
5167
|
const ports = this._ports;
|
|
@@ -5191,7 +5199,7 @@ var Act = class {
|
|
|
5191
5199
|
this._drain = options.drain !== false;
|
|
5192
5200
|
this._breaker = this._build_breaker(options);
|
|
5193
5201
|
this._drain_controllers = this._build_drain_controllers(options, lanes);
|
|
5194
|
-
this._advise_orphaned_lanes(options
|
|
5202
|
+
this._advise_orphaned_lanes(options);
|
|
5195
5203
|
this._audit_deps = this._build_audit_deps();
|
|
5196
5204
|
this._correlate = this._build_correlate(options, classification);
|
|
5197
5205
|
this._settle = this._build_settle(options);
|
|
@@ -5222,7 +5230,7 @@ var Act = class {
|
|
|
5222
5230
|
* to `claim`.
|
|
5223
5231
|
*/
|
|
5224
5232
|
_build_drain_controllers(options, lanes) {
|
|
5225
|
-
const all_lanes =
|
|
5233
|
+
const all_lanes = this._declared_lanes;
|
|
5226
5234
|
const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
|
|
5227
5235
|
const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
|
|
5228
5236
|
const single_default_lane = lanes.length === 0;
|
|
@@ -5240,19 +5248,7 @@ var Act = class {
|
|
|
5240
5248
|
on_blocked: (blocked) => this.emit("blocked", blocked),
|
|
5241
5249
|
// Reaction-requested close: the same machinery as `app.close`.
|
|
5242
5250
|
on_close: async (targets) => {
|
|
5243
|
-
|
|
5244
|
-
const result = await run_close_cycle(targets, {
|
|
5245
|
-
reactive_events_size: this._reactive_events.size,
|
|
5246
|
-
catch_up_correlation: (until) => this._catch_up_correlation(until),
|
|
5247
|
-
event_to_state: this._event_to_state,
|
|
5248
|
-
load: this._es.load,
|
|
5249
|
-
tombstone: this._es.tombstone,
|
|
5250
|
-
logger: this._logger,
|
|
5251
|
-
correlation: close_correlation(this._correlator, close_actor),
|
|
5252
|
-
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
5253
|
-
});
|
|
5254
|
-
this._forget_closed_subscriptions(result);
|
|
5255
|
-
this.emit("closed", result);
|
|
5251
|
+
await this._run_close(targets);
|
|
5256
5252
|
},
|
|
5257
5253
|
breaker: this._breaker,
|
|
5258
5254
|
run_scoped: this._scoped,
|
|
@@ -5273,12 +5269,10 @@ var Act = class {
|
|
|
5273
5269
|
* controller here. One process can't check that the cluster covers every
|
|
5274
5270
|
* lane, so it reports its own gaps.
|
|
5275
5271
|
*/
|
|
5276
|
-
_advise_orphaned_lanes(options
|
|
5272
|
+
_advise_orphaned_lanes(options) {
|
|
5277
5273
|
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
5278
5274
|
const active = new Set(this._drain_controllers.keys());
|
|
5279
|
-
const orphaned =
|
|
5280
|
-
(name) => !active.has(name)
|
|
5281
|
-
);
|
|
5275
|
+
const orphaned = this._declared_lanes.filter((name) => !active.has(name));
|
|
5282
5276
|
if (orphaned.length === 0) return;
|
|
5283
5277
|
const list = orphaned.map((name) => `"${name}"`).join(", ");
|
|
5284
5278
|
this._logger.info(
|
|
@@ -5294,7 +5288,7 @@ var Act = class {
|
|
|
5294
5288
|
states: this._states,
|
|
5295
5289
|
// Declared lanes, not this process's controllers: a lane another
|
|
5296
5290
|
// worker drains is not `unknown-lane`.
|
|
5297
|
-
declared_lanes:
|
|
5291
|
+
declared_lanes: new Set(this._declared_lanes),
|
|
5298
5292
|
routed_events: new Set(this._event_to_lanes.keys())
|
|
5299
5293
|
};
|
|
5300
5294
|
}
|
|
@@ -5311,10 +5305,7 @@ var Act = class {
|
|
|
5311
5305
|
max_subscribed_streams: options.maxSubscribedStreams ?? DEFAULT_MAX_SUBSCRIBED_STREAMS,
|
|
5312
5306
|
// Declared lanes (not active ones): a lane another process claims is
|
|
5313
5307
|
// still a valid destination.
|
|
5314
|
-
declared_lanes:
|
|
5315
|
-
"default",
|
|
5316
|
-
...this._lanes.map((l) => l.name)
|
|
5317
|
-
]),
|
|
5308
|
+
declared_lanes: new Set(this._declared_lanes),
|
|
5318
5309
|
on_init: () => {
|
|
5319
5310
|
if (this._drain && this._reactive_events.size > 0) this._arm_all();
|
|
5320
5311
|
},
|
|
@@ -6204,6 +6195,31 @@ var Act = class {
|
|
|
6204
6195
|
async prioritize(filter, priority) {
|
|
6205
6196
|
return this._scoped(() => store2().prioritize(filter, priority));
|
|
6206
6197
|
}
|
|
6198
|
+
/**
|
|
6199
|
+
* Run one close: the cycle with this Act's ports, then emit `closed`. Shared by `close()` and the drain's
|
|
6200
|
+
* reaction-requested closes. The close actor lets a custom correlator
|
|
6201
|
+
* still tag tenant context or trace ids.
|
|
6202
|
+
*/
|
|
6203
|
+
async _run_close(targets) {
|
|
6204
|
+
const close_actor = { id: "$close", name: "close" };
|
|
6205
|
+
const result = await run_close_cycle(targets, {
|
|
6206
|
+
reactive_events_size: this._reactive_events.size,
|
|
6207
|
+
catch_up_correlation: (until) => catch_up_correlation(
|
|
6208
|
+
this._correlate,
|
|
6209
|
+
(limit) => this.correlate({ limit }),
|
|
6210
|
+
until
|
|
6211
|
+
),
|
|
6212
|
+
event_to_state: this._event_to_state,
|
|
6213
|
+
load: this._es.load,
|
|
6214
|
+
tombstone: this._es.tombstone,
|
|
6215
|
+
logger: this._logger,
|
|
6216
|
+
correlation: close_correlation(this._correlator, close_actor),
|
|
6217
|
+
with_stream_lock: (stream, work) => this._close_lock.run(stream, work),
|
|
6218
|
+
forget_subscribed: (streams) => this._correlate.forget_subscribed(streams)
|
|
6219
|
+
});
|
|
6220
|
+
this.emit("closed", result);
|
|
6221
|
+
return result;
|
|
6222
|
+
}
|
|
6207
6223
|
/**
|
|
6208
6224
|
* Close the books — guard, archive, truncate, and optionally restart streams.
|
|
6209
6225
|
*
|
|
@@ -6262,69 +6278,11 @@ var Act = class {
|
|
|
6262
6278
|
* ]);
|
|
6263
6279
|
* ```
|
|
6264
6280
|
*/
|
|
6265
|
-
/**
|
|
6266
|
-
* After a close, forget any target whose subscription row the truncate
|
|
6267
|
-
* removed, so a later correlate can re-subscribe it. Restart
|
|
6268
|
-
* targets keep their row, so only fully-retired streams are forgotten.
|
|
6269
|
-
*/
|
|
6270
|
-
/**
|
|
6271
|
-
* Advance correlation until the read cursor reaches `until`, and report
|
|
6272
|
-
* where it landed. Used by the close cycle's safety probe, which
|
|
6273
|
-
* cannot judge a subscription's pending work over events correlate has
|
|
6274
|
-
* not resolved yet.
|
|
6275
|
-
*
|
|
6276
|
-
* Bounded on both ends: it stops as soon as a pass makes no progress (the
|
|
6277
|
-
* log has no more to give), and after {@link CLOSE_CATCH_UP_PASSES}
|
|
6278
|
-
* windows, so a close behind an enormous backlog degrades to skipping the
|
|
6279
|
-
* stream — the documented retryable outcome — instead of scanning the
|
|
6280
|
-
* whole log inside an operator call.
|
|
6281
|
-
*/
|
|
6282
|
-
async _catch_up_correlation(until) {
|
|
6283
|
-
for (let pass = 0; pass < CLOSE_CATCH_UP_PASSES && this._correlate.checkpoint < until; pass++) {
|
|
6284
|
-
const before = this._correlate.checkpoint;
|
|
6285
|
-
this._correlate.arm();
|
|
6286
|
-
await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
|
|
6287
|
-
if (this._correlate.checkpoint <= before) break;
|
|
6288
|
-
}
|
|
6289
|
-
return this._correlate.checkpoint;
|
|
6290
|
-
}
|
|
6291
|
-
/**
|
|
6292
|
-
* Drop retired streams from correlate's in-process "already subscribed"
|
|
6293
|
-
* set, so a later scan re-issues `subscribe()` for them.
|
|
6294
|
-
*
|
|
6295
|
-
* A tombstone seed means the stream was retired; a snapshot seed means it
|
|
6296
|
-
* was restarted and is still consuming.
|
|
6297
|
-
*
|
|
6298
|
-
* `truncate` no longer removes the subscription row, so this is no
|
|
6299
|
-
* longer repairing damage the close itself did. It still matters, because
|
|
6300
|
-
* the row can disappear later: reclaiming retired subscriptions is an
|
|
6301
|
-
* operator job now, and that `DELETE` can land while this process is
|
|
6302
|
-
* running. Forgetting here keeps the in-process view from outliving a row
|
|
6303
|
-
* an operator removed, which is a silent-no-delivery failure — a reaction whose target is named after the stream would never
|
|
6304
|
-
* be re-registered.
|
|
6305
|
-
*/
|
|
6306
|
-
_forget_closed_subscriptions(result) {
|
|
6307
|
-
const retired = [...result.truncated.entries()].filter(([, r]) => r.committed.name === TOMBSTONE_EVENT).map(([stream]) => stream);
|
|
6308
|
-
if (retired.length) this._correlate.forget_subscribed(retired);
|
|
6309
|
-
}
|
|
6310
6281
|
async close(targets) {
|
|
6311
6282
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|
|
6312
6283
|
return this._scoped(async () => {
|
|
6313
|
-
await this.correlate({ limit:
|
|
6314
|
-
|
|
6315
|
-
const result = await run_close_cycle(targets, {
|
|
6316
|
-
reactive_events_size: this._reactive_events.size,
|
|
6317
|
-
catch_up_correlation: (until) => this._catch_up_correlation(until),
|
|
6318
|
-
event_to_state: this._event_to_state,
|
|
6319
|
-
load: this._es.load,
|
|
6320
|
-
tombstone: this._es.tombstone,
|
|
6321
|
-
logger: this._logger,
|
|
6322
|
-
correlation: close_correlation(this._correlator, close_actor),
|
|
6323
|
-
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
6324
|
-
});
|
|
6325
|
-
this._forget_closed_subscriptions(result);
|
|
6326
|
-
this.emit("closed", result);
|
|
6327
|
-
return result;
|
|
6284
|
+
await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
|
|
6285
|
+
return this._run_close(targets);
|
|
6328
6286
|
});
|
|
6329
6287
|
}
|
|
6330
6288
|
/**
|