@rocicorp/zero 1.11.0 → 1.12.0-canary.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.
Files changed (35) hide show
  1. package/out/analyze-query/src/analyze-cli.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/zero/package.js +1 -1
  4. package/out/zero/package.js.map +1 -1
  5. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  6. package/out/zero-cache/src/config/zero-config.js +9 -6
  7. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  8. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  9. package/out/zero-cache/src/server/change-streamer.js +14 -5
  10. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  11. package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
  12. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  13. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  14. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +7 -5
  15. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  16. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +11 -7
  17. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  18. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +37 -30
  19. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  20. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  21. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  22. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  23. package/out/zero-cache/src/services/replicator/replication-status.d.ts +18 -0
  24. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  25. package/out/zero-cache/src/services/replicator/replication-status.js +54 -4
  26. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  27. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  28. package/out/zero-cache/src/services/statz.js +1 -1
  29. package/out/zero-cache/src/services/statz.js.map +1 -1
  30. package/out/zero-cache/src/workers/connection.js +3 -3
  31. package/out/zero-cache/src/workers/connection.js.map +1 -1
  32. package/out/zero-cache/src/workers/syncer.js +2 -2
  33. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  34. package/out/zero-client/src/client/version.js +1 -1
  35. package/package.json +1 -1
@@ -36,12 +36,11 @@ import { getDefaultHighWaterMark } from "node:stream";
36
36
  * Performs initialization and schema migrations to initialize a ChangeStreamerImpl.
37
37
  */
38
38
  async function initializeStreamer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, changeSource, replicationStatusPublisher, subscriptionState, backupConfig, purgeLock, autoReset, opts, setTimeoutFn = setTimeout) {
39
- if (opts.pgChangeLogEnabled) await checkReplicationConfig(lc, changeDB, subscriptionState, shard, autoReset, opts.pgChangeLogBehindSlot);
40
- const changeDBProvider = (applicationName, max) => postgres(defu({
39
+ if (changeDB) await checkReplicationConfig(lc, changeDB, subscriptionState, shard, autoReset, opts.pgChangeLogBehindSlot);
40
+ return new ChangeStreamerImpl(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB ? (applicationName, max) => postgres(defu({
41
41
  max,
42
42
  connection: { ["application_name"]: applicationName }
43
- }, changeDB.options));
44
- return new ChangeStreamerImpl(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDBProvider, subscriptionState, changeSource, replicationStatusPublisher, backupConfig, purgeLock, autoReset, opts, setTimeoutFn);
43
+ }, changeDB.options)) : null, subscriptionState, changeSource, replicationStatusPublisher, backupConfig, purgeLock, autoReset, opts, setTimeoutFn);
45
44
  }
46
45
  var REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS = 5e3;
47
46
  var DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS = 6e4;
@@ -190,8 +189,8 @@ var ChangeStreamerImpl = class {
190
189
  #changeDBProvider;
191
190
  #replicaVersion;
192
191
  #source;
192
+ /** The PG change log's storer, or `null` when the PG change log is disabled. */
193
193
  #storer;
194
- #pgChangeLogEnabled;
195
194
  #pgChangeLogBehindSlot;
196
195
  #forwarder;
197
196
  #reservations;
@@ -248,14 +247,13 @@ var ChangeStreamerImpl = class {
248
247
  const { replicaVersion } = subscriptionState;
249
248
  this.#replicaVersion = replicaVersion;
250
249
  this.#source = source;
251
- this.#pgChangeLogEnabled = opts.pgChangeLogEnabled;
252
250
  this.#pgChangeLogBehindSlot = opts.pgChangeLogBehindSlot;
251
+ this.#storer = changeDBProvider ? new Storer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDBProvider, subscriptionState, (consumed) => this.#acker.trackPgChangeLog(consumed[2].watermark), (err) => this.stop(err), opts) : null;
253
252
  if (!this.#pgChangeLogEnabled) {
254
- assert(opts.sqliteChangeLogWriter && opts.sqliteChangeLogPurge && opts.sqliteCatchup && opts.sqliteChangeLogServe?.readPercent === 100 && opts.sqliteChangeLogServe.coldReadPercent === 100 && backupConfig?.litestreamVersion === "v5", "disabling the PG change log requires SQLite serving at 100 percent and a v5 backup");
253
+ assert(opts.sqliteChangeLogWriter && opts.sqliteChangeLogPurge && opts.sqliteCatchup && opts.sqliteChangeLogServe?.readPercent === 100 && opts.sqliteChangeLogServe.coldReadPercent === 100, "disabling the PG change log requires SQLite serving at 100 percent and a v5 backup");
255
254
  assert(opts.sqliteChangeLogCompare === void 0, "SQLite change-log comparison requires the PG change log");
256
255
  }
257
256
  assert(!(this.#pgChangeLogEnabled && opts.pgChangeLogBehindSlot && !backupConfig?.replicaFile), "the PG change log is behind the replication slot, but there is no restored replica to re-initialize it from");
258
- this.#storer = new Storer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDBProvider, subscriptionState, (consumed) => this.#acker.trackPgChangeLog(consumed[2].watermark), (err) => this.stop(err), opts);
259
257
  this.#forwarder = new Forwarder(lc, {
260
258
  flowControlConsensusTimeoutProportion: opts.flowControlConsensusTimeoutProportion,
261
259
  flowControlSlowSubscriberGracePeriodMs: opts.flowControlSlowSubscriberGracePeriodMs
@@ -305,7 +303,7 @@ var ChangeStreamerImpl = class {
305
303
  this.#purgeScheduler = opts.sqliteChangeLogPurge && this.#changeLogWriter ? new SQLiteChangeLogPurgeScheduler(lc, () => this.#changeLogWriter?.connection, () => this.#forwarder.getAcks(), opts.sqliteChangeLogPurge) : void 0;
306
304
  this.#acker = new UpstreamAcker({
307
305
  trackPgChangeLog: this.#pgChangeLogEnabled,
308
- trackBackup: backupConfig?.litestreamVersion === "v5"
306
+ trackBackup: backupConfig ? backupConfig.litestreamVersion === "v5" : !this.#pgChangeLogEnabled
309
307
  });
310
308
  const replicaFileForInitialization = opts.sqliteChangeLogCompare?.replicaFile ?? (!this.#pgChangeLogEnabled ? opts.sqliteChangeLogWriter?.replicaFile : void 0);
311
309
  const replicaFileForReinitialization = this.#pgChangeLogEnabled && opts.pgChangeLogBehindSlot ? backupConfig?.replicaFile : void 0;
@@ -315,17 +313,17 @@ var ChangeStreamerImpl = class {
315
313
  initFromPgChangeLog: this.#pgChangeLogEnabled,
316
314
  initFromReplica: replicaFileForInitialization !== void 0
317
315
  }, {
318
- pgChangeLog: () => this.#storer.getStartStreamInitializationParameters(),
316
+ pgChangeLog: () => must(this.#storer).getStartStreamInitializationParameters(),
319
317
  replica: () => must(replicaSource)(),
320
318
  reconcileChangeLog: (resumeFrom, seed) => resumeFrom ? this.#changeLogWriter?.reconcile(resumeFrom) : this.#changeLogWriter?.reconcileFromLog(seed),
321
319
  changeLog: () => this.#changeLogWriter?.connection,
322
- reinitializePgChangeLog: replicaFileForReinitialization ? (params) => this.#storer.reinitializeFromReplica(params) : void 0
320
+ reinitializePgChangeLog: replicaFileForReinitialization ? (params) => must(this.#storer).reinitializeFromReplica(params) : void 0
323
321
  });
324
322
  this.#sqliteCatchupOptions = opts.sqliteCatchup ? {
325
323
  ...opts.sqliteCatchup,
326
324
  cleanupGuard: opts.sqliteCatchup.cleanupGuard ?? this.#purgeScheduler?.cleanupGuard
327
325
  } : void 0;
328
- this.#comparator = this.#pgChangeLogEnabled && opts.sqliteChangeLogCompare && opts.sqliteChangeLogWriter && opts.sqliteCatchup ? new SQLiteChangeLogComparator(lc, shard, opts.sqliteCatchup.changeLogFile, opts.sqliteChangeLogWriter.identity, this.#storer, {
326
+ this.#comparator = this.#storer && opts.sqliteChangeLogCompare && opts.sqliteChangeLogWriter && opts.sqliteCatchup ? new SQLiteChangeLogComparator(lc, shard, opts.sqliteCatchup.changeLogFile, opts.sqliteChangeLogWriter.identity, this.#storer, {
329
327
  setTimeoutFn,
330
328
  ...opts.sqliteChangeLogCompare
331
329
  }) : void 0;
@@ -333,21 +331,29 @@ var ChangeStreamerImpl = class {
333
331
  this.#autoReset = autoReset;
334
332
  this.#state = new RunningState(this.id, void 0, setTimeoutFn);
335
333
  }
334
+ /**
335
+ * Whether the legacy Postgres change log is enabled, which is determined
336
+ * solely by the presence of its {@link Storer} (i.e. of a change DB).
337
+ */
338
+ get #pgChangeLogEnabled() {
339
+ return this.#storer !== null;
340
+ }
336
341
  async run() {
337
342
  this.#lc.info?.("starting change stream");
338
343
  this.#forwarder.startProgressMonitor();
339
344
  const lagReportInit = await this.#source.startLagReporter();
340
345
  this.#lagReportingDisabled.resolve(!lagReportInit);
341
346
  if (lagReportInit) this.#latestLagReportCommitTimeMs = lagReportInit.firstCommitTimeMs;
342
- let takeoverPending = this.#pgChangeLogEnabled;
347
+ const storer = this.#storer;
348
+ let takeoverPending = storer !== null;
343
349
  const flushBytesThreshold = getDefaultHighWaterMark(false);
344
350
  while (this.#state.shouldRun()) {
345
351
  let err;
346
352
  let watermark = null;
347
353
  let unflushedBytes = 0;
348
354
  try {
349
- if (takeoverPending) {
350
- await this.#storer.assumeOwnershipAtHead({
355
+ if (storer && takeoverPending) {
356
+ await storer.assumeOwnershipAtHead({
351
357
  purgeLock: this.#purgeLock,
352
358
  requestRestart: (reason) => this.#backfills.requestRestart(reason),
353
359
  autoReset: this.#autoReset,
@@ -362,7 +368,7 @@ var ChangeStreamerImpl = class {
362
368
  const stream = await this.#source.startStream(lastWatermark, backfillRequests);
363
369
  this.#stream = stream;
364
370
  if (!this.#state.shouldRun()) break;
365
- if (this.#pgChangeLogEnabled) this.#storer.run().catch((e) => stream.changes.cancel(e));
371
+ storer?.run().catch((e) => stream.changes.cancel(e));
366
372
  if (this.#state.resetBackoff() > REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS) this.#replicationStatusPublisher.publish(this.#lc, "Replicating", `Replicating from ${lastWatermark}`);
367
373
  watermark = null;
368
374
  this.#acker.reset(stream.acks, lastWatermark);
@@ -372,7 +378,7 @@ var ChangeStreamerImpl = class {
372
378
  if (type === "status" && watermark === null && this.#mayRestart()) break;
373
379
  switch (type) {
374
380
  case "status":
375
- if (watermark === null) this.#storer.status(change[2].watermark);
381
+ if (watermark === null) storer?.status(change[2].watermark);
376
382
  if (msg.lagReport && msg.lagReport.lastTimings.commitTimeMs >= this.#latestLagReportCommitTimeMs) {
377
383
  this.#latestLagReportCommitTimeMs = msg.lagReport.lastTimings.commitTimeMs;
378
384
  this.#forwarder.sendStatus({
@@ -399,7 +405,7 @@ var ChangeStreamerImpl = class {
399
405
  this.#backfills.track(change);
400
406
  const serialized = serializeChangeStreamDataWithChange(change);
401
407
  const { json } = serialized;
402
- if (this.#pgChangeLogEnabled) this.#storer.store(watermark, change, serialized);
408
+ storer?.store(watermark, change, serialized);
403
409
  this.#changeLogWriter?.write(change, json, serialized.change);
404
410
  const entry = [
405
411
  watermark,
@@ -416,7 +422,7 @@ var ChangeStreamerImpl = class {
416
422
  await promiseOrAbort(flowControl, stream.changes.signal, this.#state.signal);
417
423
  unflushedBytes = 0;
418
424
  }
419
- const readyForMore = this.#pgChangeLogEnabled ? this.#storer.readyForMore() : void 0;
425
+ const readyForMore = storer?.readyForMore();
420
426
  if (readyForMore) await promiseOrAbort(readyForMore, stream.changes.signal, this.#state.signal);
421
427
  if (type === "commit" && this.#mayRestart()) break;
422
428
  }
@@ -428,7 +434,7 @@ var ChangeStreamerImpl = class {
428
434
  }
429
435
  if (watermark) {
430
436
  this.#lc.warn?.(`aborting interrupted transaction ${watermark}`);
431
- if (this.#pgChangeLogEnabled) this.#storer.abort();
437
+ storer?.abort();
432
438
  this.#changeLogWriter?.abort();
433
439
  this.#purgeScheduler?.onWriterIdle();
434
440
  this.#forwarder.forward([
@@ -441,9 +447,9 @@ var ChangeStreamerImpl = class {
441
447
  }
442
448
  if (this.#backfills.restartReason !== null && err === void 0) {
443
449
  this.#backfills.recordRestart();
444
- await this.#storer.stop();
450
+ await storer?.stop();
445
451
  } else await Promise.all([
446
- this.#storer.stop(),
452
+ storer?.stop(),
447
453
  this.#state.backoff(this.#lc, err),
448
454
  this.#state.retryDelay > REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS ? publishCriticalEvent(this.#lc, replicationStatusError(this.#lc, "Replicating", err)) : promiseVoid
449
455
  ]);
@@ -456,7 +462,7 @@ var ChangeStreamerImpl = class {
456
462
  const { tag } = msg;
457
463
  switch (tag) {
458
464
  case "reset-required":
459
- if (this.#pgChangeLogEnabled) await markResetRequired(this.#changeDBProvider("change-streamer-reset", 1), this.#shard);
465
+ if (this.#changeDBProvider) await markResetRequired(this.#changeDBProvider("change-streamer-reset", 1), this.#shard);
460
466
  await publishReplicationError(this.#lc, "Replicating", msg.message ?? "Resync required", msg.errorDetails);
461
467
  if (this.#autoReset) {
462
468
  this.#lc.warn?.("shutting down for auto-reset");
@@ -533,10 +539,10 @@ var ChangeStreamerImpl = class {
533
539
  this.#forwarder.remove(subscriber);
534
540
  };
535
541
  const catchupFromPG = () => {
536
- assert(this.#pgChangeLogEnabled, "cannot catch up from a disabled PG change log");
542
+ const storer = must(this.#storer, "cannot catch up from a disabled PG change log");
537
543
  try {
538
544
  this.#forwarder.add(subscriber);
539
- this.#storer.catchup(subscriber, ctx.mode);
545
+ storer.catchup(subscriber, ctx.mode);
540
546
  } catch (e) {
541
547
  removeFromForwarder();
542
548
  throw e;
@@ -683,7 +689,7 @@ var ChangeStreamerImpl = class {
683
689
  }, delay);
684
690
  }
685
691
  async #getChangeLogState() {
686
- const minWatermark = await this.#storer.getMinWatermarkForCatchup();
692
+ const minWatermark = await must(this.#storer, "the PG change log is disabled").getMinWatermarkForCatchup();
687
693
  if (!minWatermark) this.#lc.warn?.(`Unexpected empty changeLog. Resync if "Local replica watermark" errors arise`);
688
694
  return {
689
695
  replicaVersion: this.#replicaVersion,
@@ -701,7 +707,8 @@ var ChangeStreamerImpl = class {
701
707
  };
702
708
  }
703
709
  async #purgePGChangeLog() {
704
- if (this.#storer.ownershipPending) return;
710
+ const storer = this.#storer;
711
+ if (!storer || storer.ownershipPending) return;
705
712
  try {
706
713
  const { backupWatermark, purgeWatermark, current } = this.#getCleanupFloor();
707
714
  if (purgeWatermark < backupWatermark) {
@@ -713,7 +720,7 @@ var ChangeStreamerImpl = class {
713
720
  if (purgeWatermark <= this.#pgPurgedWatermark) return;
714
721
  this.#lc.info?.(`Purging PG changes before ${purgeWatermark} ...`);
715
722
  const start = performance.now();
716
- const deleted = await this.#storer.purgeRecordsBefore(purgeWatermark);
723
+ const deleted = await storer.purgeRecordsBefore(purgeWatermark);
717
724
  const elapsed = (performance.now() - start).toFixed(2);
718
725
  this.#lc.info?.(`Purged ${deleted} PG changes before ${purgeWatermark} (${elapsed} ms)`);
719
726
  this.#pgPurgedWatermark = purgeWatermark;
@@ -747,7 +754,7 @@ var ChangeStreamerImpl = class {
747
754
  * anyway (see Storer.assumeOwnershipAtHead()).
748
755
  */
749
756
  #mayRestart() {
750
- return this.#backfills.restartReason !== null && !this.#storer.ownershipPending;
757
+ return this.#backfills.restartReason !== null && !this.#storer?.ownershipPending;
751
758
  }
752
759
  async stop(err) {
753
760
  this.#state.stop(this.#lc, err);
@@ -758,7 +765,7 @@ var ChangeStreamerImpl = class {
758
765
  this.#comparator?.stop();
759
766
  this.#sqliteCatchup?.close();
760
767
  this.#changeLogWriter?.close();
761
- await Promise.allSettled([this.#storer.stop(), this.#source.stop()]);
768
+ await Promise.allSettled([this.#storer?.stop(), this.#source.stop()]);
762
769
  }
763
770
  #recordForwardedTransactionBoundary(type, watermark) {
764
771
  switch (type) {