@rocicorp/zero 1.12.0-canary.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.
- package/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +9 -6
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +14 -5
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +7 -5
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +11 -7
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +37 -30
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +1 -1
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- 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 (
|
|
40
|
-
|
|
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
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
450
|
+
await storer?.stop();
|
|
445
451
|
} else await Promise.all([
|
|
446
|
-
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
768
|
+
await Promise.allSettled([this.#storer?.stop(), this.#source.stop()]);
|
|
762
769
|
}
|
|
763
770
|
#recordForwardedTransactionBoundary(type, watermark) {
|
|
764
771
|
switch (type) {
|