@rotorsoft/act 1.28.1 → 1.29.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/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +9 -0
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +20 -0
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/settle.d.ts +1 -0
- package/dist/@types/internal/settle.d.ts.map +1 -1
- package/dist/{chunk-7WFKIQKY.js → chunk-PX3IBVLW.js} +2 -14
- package/dist/chunk-PX3IBVLW.js.map +1 -0
- package/dist/index.cjs +71 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +71 -18
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +1 -17
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-7WFKIQKY.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1028,19 +1028,7 @@ var InMemoryStore = class {
|
|
|
1028
1028
|
*/
|
|
1029
1029
|
async claim(lagging, leading, by, millis, lane) {
|
|
1030
1030
|
await sleep();
|
|
1031
|
-
const has_work = (s) =>
|
|
1032
|
-
if (s.correlated_at !== void 0) return s.at < s.correlated_at;
|
|
1033
|
-
if (!s.source) return s.at < this._max_non_snap_event_id;
|
|
1034
|
-
if (is_literal_source(s.source)) {
|
|
1035
|
-
const max_id = this._max_event_id_by_stream.get(s.source);
|
|
1036
|
-
return max_id !== void 0 && max_id > s.at;
|
|
1037
|
-
}
|
|
1038
|
-
const re = new RegExp(s.source);
|
|
1039
|
-
for (const [stream_name, max_id] of this._max_event_id_by_stream) {
|
|
1040
|
-
if (max_id > s.at && re.test(stream_name)) return true;
|
|
1041
|
-
}
|
|
1042
|
-
return false;
|
|
1043
|
-
};
|
|
1031
|
+
const has_work = (s) => s.correlated_at !== void 0 && s.at < s.correlated_at;
|
|
1044
1032
|
const available = [...this._streams.values()].filter(
|
|
1045
1033
|
(s) => s.is_available && has_work(s) && (lane === void 0 || s.lane === lane)
|
|
1046
1034
|
);
|
|
@@ -2972,9 +2960,11 @@ async function run_close_cycle(targets, deps) {
|
|
|
2972
2960
|
return { truncated, skipped };
|
|
2973
2961
|
}
|
|
2974
2962
|
async function run_windowed_closes(windowed, deps, skipped) {
|
|
2963
|
+
const checkpoint = deps.reactive_events_size > 0 ? await deps.catch_up_correlation(Number.MAX_SAFE_INTEGER) : -1;
|
|
2975
2964
|
const min_at = deps.reactive_events_size > 0 ? await probe_min_watermarks(
|
|
2976
2965
|
windowed.map((t) => t.stream),
|
|
2977
|
-
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE
|
|
2966
|
+
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
|
|
2967
|
+
checkpoint
|
|
2978
2968
|
) : /* @__PURE__ */ new Map();
|
|
2979
2969
|
const with_lock = deps.with_stream_lock ?? ((_stream, work) => work());
|
|
2980
2970
|
const truncated = /* @__PURE__ */ new Map();
|
|
@@ -3007,7 +2997,7 @@ async function windowed_prune_pending(stream, before, max_id) {
|
|
|
3007
2997
|
);
|
|
3008
2998
|
return boundary_id !== void 0 && min_id < boundary_id;
|
|
3009
2999
|
}
|
|
3010
|
-
async function probe_min_watermarks(streams, page_size) {
|
|
3000
|
+
async function probe_min_watermarks(streams, page_size, checkpoint) {
|
|
3011
3001
|
const min_at = /* @__PURE__ */ new Map();
|
|
3012
3002
|
const source_regex = /* @__PURE__ */ new Map();
|
|
3013
3003
|
const get_regex = (source) => {
|
|
@@ -3025,11 +3015,12 @@ async function probe_min_watermarks(streams, page_size) {
|
|
|
3025
3015
|
(position) => {
|
|
3026
3016
|
last = position.stream;
|
|
3027
3017
|
const source_re = position.source ? get_regex(position.source) : void 0;
|
|
3018
|
+
const pending = position.correlated_at === void 0 || position.at < position.correlated_at;
|
|
3019
|
+
const cap = pending ? position.at : Math.max(position.at, checkpoint);
|
|
3028
3020
|
for (const stream of streams) {
|
|
3029
3021
|
if (!source_re || source_re.test(stream)) {
|
|
3030
3022
|
const prev = min_at.get(stream);
|
|
3031
|
-
if (prev === void 0 ||
|
|
3032
|
-
min_at.set(stream, position.at);
|
|
3023
|
+
if (prev === void 0 || cap < prev) min_at.set(stream, cap);
|
|
3033
3024
|
}
|
|
3034
3025
|
}
|
|
3035
3026
|
},
|
|
@@ -3089,7 +3080,7 @@ async function partition_by_safety(stream_info, reactive_events_size, skipped, p
|
|
|
3089
3080
|
(position) => {
|
|
3090
3081
|
last = position.stream;
|
|
3091
3082
|
const source_re = position.source ? get_regex(position.source) : void 0;
|
|
3092
|
-
const has_work = position.correlated_at
|
|
3083
|
+
const has_work = position.correlated_at !== void 0 && position.at < position.correlated_at;
|
|
3093
3084
|
if (!has_work) return;
|
|
3094
3085
|
for (const [stream, info] of stream_info) {
|
|
3095
3086
|
if ((!source_re || source_re.test(stream)) && position.at < info.max_id) {
|
|
@@ -3199,6 +3190,17 @@ var PATTERN_CACHE_SIZE = 32;
|
|
|
3199
3190
|
var CorrelateCycle = class {
|
|
3200
3191
|
_checkpoint = -1;
|
|
3201
3192
|
_initialized = false;
|
|
3193
|
+
/**
|
|
3194
|
+
* Whether a scan might find anything. The drain has carried the same flag
|
|
3195
|
+
* since it was written — a commit raises it, an empty claim lowers it, and
|
|
3196
|
+
* a disarmed drain returns without touching the store. Correlate had no
|
|
3197
|
+
* equivalent, so a settle pass always scanned, including the final pass
|
|
3198
|
+
* whose only job is to confirm nothing changed (#1510).
|
|
3199
|
+
*
|
|
3200
|
+
* Starts armed: the log may already hold events this process has never
|
|
3201
|
+
* correlated, and only a scan can find out.
|
|
3202
|
+
*/
|
|
3203
|
+
_armed = true;
|
|
3202
3204
|
/** In-flight init, memoized for single-flight and cleared on failure. */
|
|
3203
3205
|
_init_promise;
|
|
3204
3206
|
_timer = void 0;
|
|
@@ -3262,6 +3264,14 @@ var CorrelateCycle = class {
|
|
|
3262
3264
|
get checkpoint() {
|
|
3263
3265
|
return this._checkpoint;
|
|
3264
3266
|
}
|
|
3267
|
+
/**
|
|
3268
|
+
* Signal that a commit (local or remote) may have produced events this
|
|
3269
|
+
* process has not correlated. Cheap and idempotent — the orchestrator calls
|
|
3270
|
+
* it on every commit and every notification.
|
|
3271
|
+
*/
|
|
3272
|
+
arm() {
|
|
3273
|
+
this._armed = true;
|
|
3274
|
+
}
|
|
3265
3275
|
/**
|
|
3266
3276
|
* Initialize correlation state on first call.
|
|
3267
3277
|
* - Reads the durable correlate checkpoint (and max(at)) from the store,
|
|
@@ -3347,6 +3357,13 @@ var CorrelateCycle = class {
|
|
|
3347
3357
|
*/
|
|
3348
3358
|
async correlate(query = { after: -1, limit: 10 }) {
|
|
3349
3359
|
await this.init();
|
|
3360
|
+
if (!this._armed)
|
|
3361
|
+
return {
|
|
3362
|
+
subscribed: 0,
|
|
3363
|
+
last_id: this._checkpoint,
|
|
3364
|
+
marked: 0,
|
|
3365
|
+
scanned: false
|
|
3366
|
+
};
|
|
3350
3367
|
const after = Math.max(this._checkpoint, query.after || -1);
|
|
3351
3368
|
const correlated = /* @__PURE__ */ new Map();
|
|
3352
3369
|
let last_id = after;
|
|
@@ -3395,6 +3412,9 @@ var CorrelateCycle = class {
|
|
|
3395
3412
|
}
|
|
3396
3413
|
if (streams.length) {
|
|
3397
3414
|
const { subscribed } = await this._cd.subscribe(streams, last_id);
|
|
3415
|
+
const marked = streams.filter(
|
|
3416
|
+
(entry) => entry.correlated_at !== void 0
|
|
3417
|
+
).length;
|
|
3398
3418
|
this._checkpoint = last_id;
|
|
3399
3419
|
for (const { stream, priority, lane } of streams) {
|
|
3400
3420
|
if (correlated.get(stream)?.upgraded)
|
|
@@ -3404,10 +3424,11 @@ var CorrelateCycle = class {
|
|
|
3404
3424
|
lane
|
|
3405
3425
|
});
|
|
3406
3426
|
}
|
|
3407
|
-
return { subscribed, last_id };
|
|
3427
|
+
return { subscribed, last_id, marked, scanned: true };
|
|
3408
3428
|
}
|
|
3409
3429
|
this._checkpoint = last_id;
|
|
3410
|
-
|
|
3430
|
+
this._armed = false;
|
|
3431
|
+
return { subscribed: 0, last_id, marked: 0, scanned: true };
|
|
3411
3432
|
}
|
|
3412
3433
|
/**
|
|
3413
3434
|
* Start a periodic correlation worker. Returns false if one is already
|
|
@@ -3418,9 +3439,14 @@ var CorrelateCycle = class {
|
|
|
3418
3439
|
if (this._timer) return false;
|
|
3419
3440
|
const limit = query.limit || 100;
|
|
3420
3441
|
this._timer = setInterval(
|
|
3421
|
-
() => this._run_scoped(
|
|
3422
|
-
|
|
3423
|
-
|
|
3442
|
+
() => this._run_scoped(() => {
|
|
3443
|
+
this.arm();
|
|
3444
|
+
return this.correlate({
|
|
3445
|
+
...query,
|
|
3446
|
+
after: this._checkpoint,
|
|
3447
|
+
limit
|
|
3448
|
+
});
|
|
3449
|
+
}).then((result) => {
|
|
3424
3450
|
if (callback && result.subscribed) callback(result.subscribed);
|
|
3425
3451
|
}).catch((err) => log().error(err)),
|
|
3426
3452
|
frequency
|
|
@@ -4706,11 +4732,11 @@ var SettleLoop = class {
|
|
|
4706
4732
|
let settled_drain;
|
|
4707
4733
|
for (let i = 0; i < maxPasses; i++) {
|
|
4708
4734
|
const after_before = this._deps.checkpoint();
|
|
4709
|
-
const { subscribed, last_id } = await this._deps.correlate({
|
|
4735
|
+
const { subscribed, last_id, scanned } = await this._deps.correlate({
|
|
4710
4736
|
...correlate_query,
|
|
4711
4737
|
after: after_before
|
|
4712
4738
|
});
|
|
4713
|
-
this._deps.breaker.passed();
|
|
4739
|
+
if (scanned) this._deps.breaker.passed();
|
|
4714
4740
|
const drain = await this._deps.drain(drain_options);
|
|
4715
4741
|
settled_drain = settled_drain ? {
|
|
4716
4742
|
fetched: [...settled_drain.fetched, ...drain.fetched],
|
|
@@ -5207,7 +5233,7 @@ var Act = class {
|
|
|
5207
5233
|
// the scoped store and `_initialized` then blocks a retry (#1191).
|
|
5208
5234
|
init: () => this._scoped(() => this._correlate.init()),
|
|
5209
5235
|
checkpoint: () => this._correlate.checkpoint,
|
|
5210
|
-
correlate: (q) => this.
|
|
5236
|
+
correlate: (q) => this._correlate_scanned(q),
|
|
5211
5237
|
drain: (o) => this.drain(o),
|
|
5212
5238
|
on_settled: (drain) => this.emit("settled", drain),
|
|
5213
5239
|
breaker: this._breaker
|
|
@@ -5620,6 +5646,7 @@ var Act = class {
|
|
|
5620
5646
|
}
|
|
5621
5647
|
/** Arm every active lane controller (ACT-1103). */
|
|
5622
5648
|
_arm_all() {
|
|
5649
|
+
this._correlate.arm();
|
|
5623
5650
|
for (const c of this._drain_controllers.values()) c.arm();
|
|
5624
5651
|
}
|
|
5625
5652
|
/**
|
|
@@ -5643,6 +5670,7 @@ var Act = class {
|
|
|
5643
5670
|
for (const lane of set) to_arm.add(lane);
|
|
5644
5671
|
}
|
|
5645
5672
|
if (to_arm.size === 0) return false;
|
|
5673
|
+
this._correlate.arm();
|
|
5646
5674
|
for (const lane of to_arm) this._drain_controllers.get(lane)?.arm();
|
|
5647
5675
|
return true;
|
|
5648
5676
|
}
|
|
@@ -5716,12 +5744,24 @@ var Act = class {
|
|
|
5716
5744
|
* @see {@link stop_correlations} to stop automatic correlation
|
|
5717
5745
|
*/
|
|
5718
5746
|
async correlate(query = { after: -1, limit: 10 }) {
|
|
5719
|
-
|
|
5747
|
+
const { subscribed, last_id } = await this._correlate_scanned(query);
|
|
5748
|
+
return { subscribed, last_id };
|
|
5749
|
+
}
|
|
5750
|
+
/**
|
|
5751
|
+
* `correlate` plus whether the pass actually read the store (#1510).
|
|
5752
|
+
*
|
|
5753
|
+
* The settle loop needs that extra bit to decide whether the pass carries a
|
|
5754
|
+
* circuit-breaker health signal, and a disarmed pass carries none. It stays
|
|
5755
|
+
* internal rather than widening the public `correlate` return, which is
|
|
5756
|
+
* charter-covered and has no use for it.
|
|
5757
|
+
*/
|
|
5758
|
+
async _correlate_scanned(query) {
|
|
5759
|
+
if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
|
|
5720
5760
|
return this._scoped(async () => {
|
|
5721
|
-
const
|
|
5722
|
-
if (
|
|
5761
|
+
const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query);
|
|
5762
|
+
if ((subscribed > 0 || marked > 0) && this._reactive_events.size > 0)
|
|
5723
5763
|
this._arm_all();
|
|
5724
|
-
return
|
|
5764
|
+
return { subscribed, last_id, scanned };
|
|
5725
5765
|
});
|
|
5726
5766
|
}
|
|
5727
5767
|
/**
|
|
@@ -6154,6 +6194,7 @@ var Act = class {
|
|
|
6154
6194
|
async _catch_up_correlation(until) {
|
|
6155
6195
|
for (let pass = 0; pass < CLOSE_CATCH_UP_PASSES && this._correlate.checkpoint < until; pass++) {
|
|
6156
6196
|
const before = this._correlate.checkpoint;
|
|
6197
|
+
this._correlate.arm();
|
|
6157
6198
|
await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
|
|
6158
6199
|
if (this._correlate.checkpoint <= before) break;
|
|
6159
6200
|
}
|