@rotorsoft/act 1.22.8 → 1.22.9
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.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +28 -2
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/settle.d.ts +10 -0
- package/dist/@types/internal/settle.d.ts.map +1 -1
- package/dist/@types/internal/state-fold.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +13 -0
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/index.cjs +68 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1396,6 +1396,7 @@ async function truncate_and_warm_cache(guarded, seed_states, guard_events, corre
|
|
|
1396
1396
|
}
|
|
1397
1397
|
|
|
1398
1398
|
// src/internal/correlate-cycle.ts
|
|
1399
|
+
var DEFAULT_COLD_START_BACK_SCAN = 1e4;
|
|
1399
1400
|
var CorrelateCycle = class {
|
|
1400
1401
|
_checkpoint = -1;
|
|
1401
1402
|
_initialized = false;
|
|
@@ -1424,8 +1425,24 @@ var CorrelateCycle = class {
|
|
|
1424
1425
|
* `_scoped` (identity for a non-scoped Act), so it's required.
|
|
1425
1426
|
*/
|
|
1426
1427
|
_run_scoped;
|
|
1427
|
-
|
|
1428
|
-
|
|
1428
|
+
/**
|
|
1429
|
+
* Tail re-scan window applied to the cold-start checkpoint (ACT-1207).
|
|
1430
|
+
* See {@link DEFAULT_COLD_START_BACK_SCAN}. Constructor arg (not a
|
|
1431
|
+
* public option) so tests can shrink it; defaults otherwise.
|
|
1432
|
+
*/
|
|
1433
|
+
_cold_start_back_scan;
|
|
1434
|
+
constructor({
|
|
1435
|
+
registry,
|
|
1436
|
+
static_targets,
|
|
1437
|
+
has_dynamic_resolvers,
|
|
1438
|
+
cd,
|
|
1439
|
+
max_subscribed_streams,
|
|
1440
|
+
run_scoped,
|
|
1441
|
+
on_init,
|
|
1442
|
+
on_init_async,
|
|
1443
|
+
cold_start_back_scan = DEFAULT_COLD_START_BACK_SCAN
|
|
1444
|
+
}) {
|
|
1445
|
+
this._subscribed = new LruSet(max_subscribed_streams);
|
|
1429
1446
|
this._registry = registry;
|
|
1430
1447
|
this._static_targets = static_targets;
|
|
1431
1448
|
this._has_dynamic_resolvers = has_dynamic_resolvers;
|
|
@@ -1433,6 +1450,7 @@ var CorrelateCycle = class {
|
|
|
1433
1450
|
this._on_init = on_init;
|
|
1434
1451
|
this._run_scoped = run_scoped;
|
|
1435
1452
|
this._on_init_async = on_init_async;
|
|
1453
|
+
this._cold_start_back_scan = cold_start_back_scan;
|
|
1436
1454
|
}
|
|
1437
1455
|
/** Last correlated event id. */
|
|
1438
1456
|
get checkpoint() {
|
|
@@ -1440,7 +1458,10 @@ var CorrelateCycle = class {
|
|
|
1440
1458
|
}
|
|
1441
1459
|
/**
|
|
1442
1460
|
* Initialize correlation state on first call.
|
|
1443
|
-
* - Reads max(at) from store
|
|
1461
|
+
* - Reads max(at) from store, then floors the cold-start checkpoint at
|
|
1462
|
+
* `watermark - back_scan` when dynamic resolvers exist, so an event
|
|
1463
|
+
* committed-but-not-correlated before a crash is re-scanned on
|
|
1464
|
+
* restart instead of skipped (ACT-1207)
|
|
1444
1465
|
* - Subscribes static resolver targets (idempotent upsert)
|
|
1445
1466
|
* - Populates the subscribed-streams LRU
|
|
1446
1467
|
* - Fires `on_init` once (Act uses this to flag a cold-start drain)
|
|
@@ -1449,7 +1470,7 @@ var CorrelateCycle = class {
|
|
|
1449
1470
|
if (this._initialized) return;
|
|
1450
1471
|
this._initialized = true;
|
|
1451
1472
|
const { watermark } = await store().subscribe([...this._static_targets]);
|
|
1452
|
-
this._checkpoint = watermark;
|
|
1473
|
+
this._checkpoint = this._has_dynamic_resolvers ? Math.max(-1, watermark - this._cold_start_back_scan) : watermark;
|
|
1453
1474
|
this._on_init?.();
|
|
1454
1475
|
for (const { stream } of this._static_targets) {
|
|
1455
1476
|
this._subscribed.add(stream);
|
|
@@ -1900,6 +1921,7 @@ async function load(me, target, callback) {
|
|
|
1900
1921
|
event,
|
|
1901
1922
|
state: state2,
|
|
1902
1923
|
version: event.version,
|
|
1924
|
+
id: event.id,
|
|
1903
1925
|
patches,
|
|
1904
1926
|
snaps,
|
|
1905
1927
|
cache_hit,
|
|
@@ -1921,12 +1943,17 @@ async function load(me, target, callback) {
|
|
|
1921
1943
|
event_id: event.id,
|
|
1922
1944
|
patches,
|
|
1923
1945
|
snaps
|
|
1924
|
-
});
|
|
1946
|
+
}).catch((err) => log().error(err));
|
|
1925
1947
|
}
|
|
1926
1948
|
return {
|
|
1927
1949
|
event,
|
|
1928
1950
|
state: state2,
|
|
1929
1951
|
version: event?.version ?? cached?.version ?? -1,
|
|
1952
|
+
// Head event id, captured atomically with `state`: the last replayed
|
|
1953
|
+
// event's id on a cache miss, or the cached checkpoint's event_id on a
|
|
1954
|
+
// warm hit with no new events. Consumers read this instead of a
|
|
1955
|
+
// separate cache lookup that could race a concurrent commit (ACT-1204).
|
|
1956
|
+
id: event?.id ?? cached?.event_id ?? -1,
|
|
1930
1957
|
patches,
|
|
1931
1958
|
snaps,
|
|
1932
1959
|
cache_hit,
|
|
@@ -2020,6 +2047,7 @@ async function action(me, action2, target, payload, options) {
|
|
|
2020
2047
|
event,
|
|
2021
2048
|
state: state2,
|
|
2022
2049
|
version: event.version,
|
|
2050
|
+
id: event.id,
|
|
2023
2051
|
patches,
|
|
2024
2052
|
snaps: snapshot.snaps,
|
|
2025
2053
|
patch: p,
|
|
@@ -2047,6 +2075,7 @@ async function action(me, action2, target, payload, options) {
|
|
|
2047
2075
|
return snapshots;
|
|
2048
2076
|
} catch (error) {
|
|
2049
2077
|
if (!(error instanceof ConcurrencyError)) throw error;
|
|
2078
|
+
if (expectedVersion !== void 0) throw error;
|
|
2050
2079
|
if (attempt >= max_retries) throw error;
|
|
2051
2080
|
if (opts?.backoff) {
|
|
2052
2081
|
const delay_ms = compute_backoff_delay(attempt, opts.backoff);
|
|
@@ -2569,6 +2598,16 @@ function build_handle_batch(logger) {
|
|
|
2569
2598
|
var SettleLoop = class {
|
|
2570
2599
|
_timer = void 0;
|
|
2571
2600
|
_running = false;
|
|
2601
|
+
/**
|
|
2602
|
+
* Set when a `schedule()` timer fires while a cycle is still running
|
|
2603
|
+
* (ACT-1205). The in-flight cycle's `finally` re-arms one more pass so
|
|
2604
|
+
* the wake-up isn't dropped — a commit landing during the final
|
|
2605
|
+
* no-progress drain pass would otherwise leave armed controllers with
|
|
2606
|
+
* nothing to re-drain on an instance with no lane `cycleMs` and no
|
|
2607
|
+
* polling. Carries the options of the dropped call so the re-armed
|
|
2608
|
+
* pass honors its `debounceMs`/`maxPasses`/drain overrides.
|
|
2609
|
+
*/
|
|
2610
|
+
_pending = void 0;
|
|
2572
2611
|
_deps;
|
|
2573
2612
|
/** Debounce window applied when the caller doesn't override via `SettleOptions.debounceMs`. */
|
|
2574
2613
|
_default_debounce_ms;
|
|
@@ -2593,7 +2632,10 @@ var SettleLoop = class {
|
|
|
2593
2632
|
if (this._timer) clearTimeout(this._timer);
|
|
2594
2633
|
this._timer = setTimeout(() => {
|
|
2595
2634
|
this._timer = void 0;
|
|
2596
|
-
if (this._running)
|
|
2635
|
+
if (this._running) {
|
|
2636
|
+
this._pending = options;
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2597
2639
|
this._running = true;
|
|
2598
2640
|
(async () => {
|
|
2599
2641
|
await this._deps.init();
|
|
@@ -2613,11 +2655,17 @@ var SettleLoop = class {
|
|
|
2613
2655
|
this._deps.breaker.failed(Date.now(), err);
|
|
2614
2656
|
}).finally(() => {
|
|
2615
2657
|
this._running = false;
|
|
2658
|
+
const pending = this._pending;
|
|
2659
|
+
if (pending !== void 0) {
|
|
2660
|
+
this._pending = void 0;
|
|
2661
|
+
this.schedule(pending);
|
|
2662
|
+
}
|
|
2616
2663
|
});
|
|
2617
2664
|
}, debounceMs);
|
|
2618
2665
|
}
|
|
2619
2666
|
/** Cancel any pending or active settle cycle. Idempotent. */
|
|
2620
2667
|
stop() {
|
|
2668
|
+
this._pending = void 0;
|
|
2621
2669
|
if (this._timer) {
|
|
2622
2670
|
clearTimeout(this._timer);
|
|
2623
2671
|
this._timer = void 0;
|
|
@@ -2668,20 +2716,13 @@ function make_fold_handler(me, flush, config2) {
|
|
|
2668
2716
|
cache2.delete(oldest);
|
|
2669
2717
|
}
|
|
2670
2718
|
const snapshot = await load(me, { stream });
|
|
2671
|
-
const entry = await cache().get(stream);
|
|
2672
|
-
const frontier = entry ?? {
|
|
2673
|
-
version: snapshot.event.version,
|
|
2674
|
-
event_id: snapshot.event.id,
|
|
2675
|
-
patches: snapshot.patches,
|
|
2676
|
-
snaps: snapshot.snaps
|
|
2677
|
-
};
|
|
2678
2719
|
fold = {
|
|
2679
2720
|
stream,
|
|
2680
2721
|
state: snapshot.state,
|
|
2681
|
-
version:
|
|
2682
|
-
event_id:
|
|
2683
|
-
patches:
|
|
2684
|
-
snaps:
|
|
2722
|
+
version: snapshot.version,
|
|
2723
|
+
event_id: snapshot.id,
|
|
2724
|
+
patches: snapshot.patches,
|
|
2725
|
+
snaps: snapshot.snaps,
|
|
2685
2726
|
dirty: true
|
|
2686
2727
|
};
|
|
2687
2728
|
cache2.set(stream, fold);
|
|
@@ -3040,22 +3081,22 @@ var Act = class {
|
|
|
3040
3081
|
* except on writer-only instances (`drain: false`).
|
|
3041
3082
|
*/
|
|
3042
3083
|
_build_correlate(options, classification) {
|
|
3043
|
-
return new CorrelateCycle(
|
|
3044
|
-
this.registry,
|
|
3045
|
-
classification.static_targets,
|
|
3046
|
-
classification.has_dynamic_resolvers,
|
|
3047
|
-
this._cd,
|
|
3048
|
-
options.maxSubscribedStreams ?? DEFAULT_MAX_SUBSCRIBED_STREAMS,
|
|
3049
|
-
() => {
|
|
3084
|
+
return new CorrelateCycle({
|
|
3085
|
+
registry: this.registry,
|
|
3086
|
+
static_targets: classification.static_targets,
|
|
3087
|
+
has_dynamic_resolvers: classification.has_dynamic_resolvers,
|
|
3088
|
+
cd: this._cd,
|
|
3089
|
+
max_subscribed_streams: options.maxSubscribedStreams ?? DEFAULT_MAX_SUBSCRIBED_STREAMS,
|
|
3090
|
+
on_init: () => {
|
|
3050
3091
|
if (this._drain && this._reactive_events.size > 0) this._arm_all();
|
|
3051
3092
|
},
|
|
3052
3093
|
// Re-scope the background `start_correlations` timer so its
|
|
3053
3094
|
// correlate resolves the scoped ports, not the singleton (#1191).
|
|
3054
|
-
this._scoped,
|
|
3095
|
+
run_scoped: this._scoped,
|
|
3055
3096
|
// Cold-start defer re-seed (#1221). Skipped on writer-only instances
|
|
3056
3097
|
// (`drain: false`) — they run no local controllers to re-arm.
|
|
3057
|
-
this._drain ? () => this._seed_persisted_defers() : void 0
|
|
3058
|
-
);
|
|
3098
|
+
on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
|
|
3099
|
+
});
|
|
3059
3100
|
}
|
|
3060
3101
|
/**
|
|
3061
3102
|
* Re-seed every active lane controller's process-local defer timer from
|