@rotorsoft/act 1.33.16 → 1.33.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/index.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  sleep,
41
41
  store,
42
42
  validate
43
- } from "./chunk-E5H3NRCT.js";
43
+ } from "./chunk-5KTVG7IM.js";
44
44
  import {
45
45
  ActorSchema,
46
46
  CausationEventSchema,
@@ -1168,10 +1168,13 @@ var CorrelateCycle = class {
1168
1168
  _raised = 1;
1169
1169
  _lowered = 0;
1170
1170
  /**
1171
- * Bumped by a release (shutdown) and a restart (restore), so a scan or
1172
- * lease reply that was out across one writes nothing.
1171
+ * Bumped by a release (shutdown), so a scan or lease reply that was out
1172
+ * across one writes nothing.
1173
1173
  */
1174
1174
  _epoch = 0;
1175
+ /** Passes in flight: a restore waits for them, and refuses new ones. */
1176
+ _passes = /* @__PURE__ */ new Set();
1177
+ _restoring = false;
1175
1178
  _init_promise;
1176
1179
  // Bounded by `maxSubscribedStreams`; static records live apart so eviction
1177
1180
  // never drops their +Infinity floor.
@@ -1230,14 +1233,22 @@ var CorrelateCycle = class {
1230
1233
  }
1231
1234
  await this._on_init?.();
1232
1235
  }
1233
- /** After `restore` replaced the log: forget everything and cold-start again. */
1234
- restart() {
1236
+ /**
1237
+ * Replace the log with no pass in flight, then cold-start: a pass that
1238
+ * read the old log writes before the restore, never into the new one.
1239
+ */
1240
+ async restart(restore) {
1241
+ this._restoring = true;
1242
+ try {
1243
+ await Promise.allSettled(this._passes);
1244
+ await restore();
1245
+ } finally {
1246
+ this._restoring = false;
1247
+ }
1235
1248
  this._init_promise = void 0;
1236
1249
  this._checkpoint = -1;
1237
1250
  this._dynamic_subscriptions.clear();
1238
1251
  this._static_subscriptions.clear();
1239
- this._raised++;
1240
- this._epoch++;
1241
1252
  }
1242
1253
  /** Forget dynamic targets whose rows a full close deleted, so they re-subscribe. */
1243
1254
  forget_subscribed(streams) {
@@ -1256,6 +1267,16 @@ var CorrelateCycle = class {
1256
1267
  }
1257
1268
  /** Scan past the checkpoint, subscribe new targets and raise their work marks. */
1258
1269
  async correlate(query, lease = false) {
1270
+ if (this._restoring) return { kind: "idle", last_id: this._checkpoint };
1271
+ const pass = this._scan(query, lease);
1272
+ this._passes.add(pass);
1273
+ try {
1274
+ return await pass;
1275
+ } finally {
1276
+ this._passes.delete(pass);
1277
+ }
1278
+ }
1279
+ async _scan(query, lease) {
1259
1280
  await this._init();
1260
1281
  const epoch = this._epoch;
1261
1282
  const idle = () => ({
@@ -2408,9 +2429,11 @@ var DrainController = class {
2408
2429
  get lane() {
2409
2430
  return this._deps.lane;
2410
2431
  }
2411
- /** Drains every `cycleMs` while armed; the unref'd timer never holds the process. */
2432
+ /**
2433
+ * Drains every `cycleMs` while armed; the unref'd timer never holds the
2434
+ * process. Called once, when the controller is built.
2435
+ */
2412
2436
  start(cycleMs) {
2413
- if (this._worker || this._stopped) return;
2414
2437
  const run = this._deps.run_scoped;
2415
2438
  const tick = async () => {
2416
2439
  if (this.armed) await run(() => this.drain());
@@ -3694,6 +3717,12 @@ var Act = class {
3694
3717
  * adapter's {@link Store.restore} driver supplies the transaction
3695
3718
  * lifecycle and per-event insert.
3696
3719
  *
3720
+ * Safe in a live process: restoring into this Act's own store waits
3721
+ * for its correlate passes in flight, replaces the log, then restarts
3722
+ * correlation and settles. A correlate pass that starts meanwhile
3723
+ * reads nothing. Other processes on the same store keep their
3724
+ * correlation state and must be restarted.
3725
+ *
3697
3726
  * Throws if the adapter has no restore capability. Throws on the
3698
3727
  * first invalid event (negative version, malformed `created`) with
3699
3728
  * the running index in the message; atomic transaction rollback in
@@ -3736,14 +3765,15 @@ var Act = class {
3736
3765
  let kept = 0;
3737
3766
  let migrated = 0;
3738
3767
  let dropped = { closed_streams: 0, snapshots: 0 };
3739
- await target.restore(async (callback) => {
3768
+ const run = () => target.restore(async (callback) => {
3740
3769
  const partial = await scan(source, opts, callback);
3741
3770
  kept = partial.kept;
3742
3771
  migrated = partial.migrated;
3743
3772
  dropped = partial.dropped;
3744
3773
  });
3745
- if (!sink) {
3746
- this._correlate.restart();
3774
+ if (sink) await run();
3775
+ else {
3776
+ await this._correlate.restart(run);
3747
3777
  this._wake();
3748
3778
  }
3749
3779
  return { kept, migrated, dropped, duration_ms: Date.now() - started };