@rocicorp/zero 1.8.0 → 1.9.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/shared/src/cache.d.ts +8 -1
- package/out/shared/src/cache.d.ts.map +1 -1
- package/out/shared/src/cache.js +25 -9
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/objects.d.ts +6 -1
- package/out/shared/src/objects.d.ts.map +1 -1
- package/out/shared/src/objects.js +19 -4
- package/out/shared/src/objects.js.map +1 -1
- package/out/z2s/src/compiler.d.ts.map +1 -1
- package/out/z2s/src/compiler.js +1 -22
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/zero/package.js +2 -19
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +4 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +21 -6
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts +6 -0
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +8 -0
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +5 -2
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration.js +3 -1
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.d.ts +11 -0
- package/out/zero-cache/src/db/sqlite-corruption.d.ts.map +1 -0
- package/out/zero-cache/src/db/sqlite-corruption.js +191 -0
- package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -0
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/backup-watermark-reader.js +1 -1
- package/out/zero-cache/src/server/backup-watermark-reader.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 +7 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/logging.d.ts +8 -0
- package/out/zero-cache/src/server/logging.d.ts.map +1 -1
- package/out/zero-cache/src/server/logging.js +27 -5
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +6 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +1 -1
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.d.ts +7 -1
- package/out/zero-cache/src/server/otel-log-sink.d.ts.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js +7 -2
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/reaper.js +1 -1
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +11 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +6 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +8 -5
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +36 -2
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +76 -33
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +15 -0
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +41 -10
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +21 -0
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +34 -5
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +2 -2
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +61 -87
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +17 -12
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +15 -3
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +26 -23
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config.yml +3 -2
- package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +2 -2
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +4 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +29 -16
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +5 -12
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +38 -7
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +8 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +66 -26
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts +28 -0
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-covering.js +159 -0
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +2 -0
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +71 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +4 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +18 -0
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +50 -0
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +1 -0
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js +3 -4
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +6 -7
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +125 -85
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/relationship-builder.js +3 -2
- package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.js +7 -7
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +2 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-server/src/custom.js +10 -3
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/schema.d.ts +8 -0
- package/out/zero-server/src/schema.d.ts.map +1 -1
- package/out/zero-server/src/schema.js +63 -24
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +2 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.d.ts +10 -8
- package/out/zql/src/mutate/crud.d.ts.map +1 -1
- package/out/zql/src/mutate/crud.js +3 -4
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +5 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zqlite/src/db.js +2 -2
- package/out/zqlite/src/internal/statement-cache.d.ts +25 -1
- package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
- package/out/zqlite/src/internal/statement-cache.js +48 -18
- package/out/zqlite/src/internal/statement-cache.js.map +1 -1
- package/out/zqlite/src/query-builder.js +3 -3
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +13 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +5 -5
- package/out/zqlite/src/table-source.js +3 -3
- package/package.json +2 -33
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { populateFromExistingTables } from "../../replicator/schema/column-metadata.js";
|
|
2
2
|
import { listTables } from "../../../db/lite-tables.js";
|
|
3
3
|
import { CREATE_RUNTIME_EVENTS_TABLE, recordEvent } from "../../replicator/schema/replication-state.js";
|
|
4
|
+
import { isSQLiteCorruption, logSQLiteCorruptionDiagnostics } from "../../../db/sqlite-corruption.js";
|
|
4
5
|
import { runSchemaMigrations } from "../../../db/migration-lite.js";
|
|
5
6
|
import { AutoResetSignal } from "../../change-streamer/schema/tables.js";
|
|
6
|
-
import { SqliteError } from "@rocicorp/zero-sqlite3";
|
|
7
7
|
//#region ../zero-cache/src/services/change-source/common/replica-schema.ts
|
|
8
8
|
async function initReplica(log, debugName, dbPath, initialSync) {
|
|
9
9
|
const setupMigration = {
|
|
@@ -13,7 +13,7 @@ async function initReplica(log, debugName, dbPath, initialSync) {
|
|
|
13
13
|
try {
|
|
14
14
|
await runSchemaMigrations(log, debugName, dbPath, setupMigration, schemaVersionMigrationMap);
|
|
15
15
|
} catch (e) {
|
|
16
|
-
throwAutoResetForCorruption(e);
|
|
16
|
+
throwAutoResetForCorruption(log, debugName, dbPath, e);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
async function upgradeReplica(log, debugName, dbPath) {
|
|
@@ -22,11 +22,14 @@ async function upgradeReplica(log, debugName, dbPath) {
|
|
|
22
22
|
throw new Error("This should only be called for already synced replicas");
|
|
23
23
|
} }, schemaVersionMigrationMap);
|
|
24
24
|
} catch (e) {
|
|
25
|
-
throwAutoResetForCorruption(e);
|
|
25
|
+
throwAutoResetForCorruption(log, debugName, dbPath, e);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
function throwAutoResetForCorruption(e) {
|
|
29
|
-
if (
|
|
28
|
+
function throwAutoResetForCorruption(log, debugName, dbPath, e) {
|
|
29
|
+
if (isSQLiteCorruption(e)) {
|
|
30
|
+
logSQLiteCorruptionDiagnostics(log, debugName, dbPath, e);
|
|
31
|
+
throw new AutoResetSignal(`replica database appears corrupt: ${String(e)}`, { cause: e });
|
|
32
|
+
}
|
|
30
33
|
throw e;
|
|
31
34
|
}
|
|
32
35
|
var CREATE_V6_COLUMN_METADATA_TABLE = `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replica-schema.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport
|
|
1
|
+
{"version":3,"file":"replica-schema.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport {listTables} from '../../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration-lite.ts';\nimport {\n isSQLiteCorruption,\n logSQLiteCorruptionDiagnostics,\n} from '../../../db/sqlite-corruption.ts';\nimport {AutoResetSignal} from '../../change-streamer/schema/tables.ts';\nimport {populateFromExistingTables} from '../../replicator/schema/column-metadata.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../../replicator/schema/replication-state.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n throwAutoResetForCorruption(log, debugName, dbPath, e);\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n } catch (e) {\n throwAutoResetForCorruption(log, debugName, dbPath, e);\n }\n}\n\nfunction throwAutoResetForCorruption(\n log: LogContext,\n debugName: string,\n dbPath: string,\n e: unknown,\n): never {\n if (isSQLiteCorruption(e)) {\n logSQLiteCorruptionDiagnostics(log, debugName, dbPath, e);\n throw new AutoResetSignal(\n `replica database appears corrupt: ${String(e)}`,\n {cause: e},\n );\n }\n throw e;\n}\n\nexport const CREATE_V6_COLUMN_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\nexport const CREATE_V7_CHANGE_LOG = /*sql*/ `\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n`;\n\nexport const CREATE_V9_TABLE_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.tableMetadata\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"metadata\" TEXT NOT NULL,\n PRIMARY KEY (\"schema\", \"table\")\n );\n`;\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n // Revised in the migration to v8 because the v6 code was incomplete.\n 6: {},\n\n 7: {\n migrateSchema: (_, db) => {\n // Note: The original \"changeLog\" table is kept so that the replica file\n // is compatible with older zero-caches. However, it is truncated for\n // space savings (since historic changes were never read).\n db.exec(`DELETE FROM \"_zero.changeLog\"`);\n // First version of changeLog2\n db.exec(CREATE_V7_CHANGE_LOG);\n },\n },\n\n 8: {\n migrateSchema: (_, db) => {\n const tableExists = db\n .prepare(\n `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`,\n )\n .get();\n\n if (!tableExists) {\n db.exec(CREATE_V6_COLUMN_METADATA_TABLE);\n }\n },\n migrateData: (_, db) => {\n // Re-populate the ColumnMetadataStore; the original migration\n // at v6 was incomplete, as covered replicas migrated from earlier\n // versions but did not initialize the table for new replicas.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const tables = listTables(db, false, false);\n populateFromExistingTables(db, tables);\n },\n },\n\n 9: {\n migrateSchema: (_, db) => {\n db.exec(\n /*sql*/ `\n ALTER TABLE \"_zero.changeLog2\" \n ADD COLUMN \"backfillingColumnVersions\" TEXT DEFAULT '{}';\n ALTER TABLE \"_zero.column_metadata\"\n ADD COLUMN backfill TEXT;\n ` + CREATE_V9_TABLE_METADATA_TABLE,\n );\n },\n },\n\n 10: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.replicationConfig\" \n ADD COLUMN \"initialSyncContext\" TEXT DEFAULT '{}';\n `);\n },\n },\n\n 11: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.tableMetadata\"\n ADD COLUMN \"minRowVersion\" TEXT NOT NULL DEFAULT '00';\n\n -- Removing the NOT NULL constraint from \"metadata\" requires copying\n -- the column. We piggyback the rename to \"upstreamMetadata\" here.\n ALTER TABLE \"_zero.tableMetadata\"\n ADD COLUMN \"upstreamMetadata\" TEXT;\n UPDATE \"_zero.tableMetadata\" SET \"upstreamMetadata\" = \"metadata\";\n ALTER TABLE \"_zero.tableMetadata\" DROP \"metadata\";\n `);\n },\n },\n\n 12: {\n migrateSchema: (_, db) => {\n // Bring back the \"metadata\" column removed in v11, but as a NULL-able column.\n // It is needed for backwards compatibility.\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.tableMetadata\"\n ADD COLUMN \"metadata\" TEXT;\n `);\n },\n\n migrateData: (_, db) => {\n // For rollback then roll forward, re-copy anything written to metadata.\n db.exec(/*sql*/ `\n UPDATE \"_zero.tableMetadata\" \n SET \"upstreamMetadata\" = COALESCE(\"metadata\", \"upstreamMetadata\"),\n \"metadata\" = NULL;\n `);\n },\n },\n\n 13: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.replicationState\" ADD COLUMN writeTimeMs INTEGER;\n `);\n },\n\n migrateData: (_, db) => {\n db.exec(/*sql*/ `\n UPDATE \"_zero.replicationState\" \n SET writeTimeMs = COALESCE(writeTimeMs, unixepoch('subsec') * 1000)`);\n },\n },\n};\n\n// Referenced in tests.\nexport const CURRENT_SCHEMA_VERSION = Object.keys(\n schemaVersionMigrationMap,\n).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);\n"],"mappings":";;;;;;;AAmBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;CACf,MAAM,iBAA4B;EAChC,gBAAgB,KAAK,OAAO,YAAY,KAAK,EAAE;EAC/C,gBAAgB;CAClB;CAEA,IAAI;EACF,MAAM,oBACJ,KACA,WACA,QACA,gBACA,yBACF;CACF,SAAS,GAAG;EACV,4BAA4B,KAAK,WAAW,QAAQ,CAAC;CACvD;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;CACA,IAAI;EACF,MAAM,oBACJ,KACA,WACA,QAEA,EACE,qBAAqB;GACnB,MAAM,IAAI,MACR,wDACF;EACF,EACF,GACA,yBACF;CACF,SAAS,GAAG;EACV,4BAA4B,KAAK,WAAW,QAAQ,CAAC;CACvD;AACF;AAEA,SAAS,4BACP,KACA,WACA,QACA,GACO;CACP,IAAI,mBAAmB,CAAC,GAAG;EACzB,+BAA+B,KAAK,WAAW,QAAQ,CAAC;EACxD,MAAM,IAAI,gBACR,qCAAqC,OAAO,CAAC,KAC7C,EAAC,OAAO,EAAC,CACX;CACF;CACA,MAAM;AACR;AAEA,IAAa,kCAA0C;;;;;;;;;;;;AAavD,IAAa,uBAA+B;;;;;;;;;;;AAY5C,IAAa,iCAAyC;;;;;;;;AAStD,IAAa,4BAAqD;CAEhE,GAAG;EACD,qBAAqB;GACnB,MAAM,IAAI,gBAAgB,iCAAiC;EAC7D;EACA,gBAAgB;CAClB;CAEA,GAAG;EACD,gBAAgB,GAAG,OAAO;GACxB,GAAG,KAAK,2BAA2B;EACrC;EACA,cAAc,GAAG,OAAO;GACtB,YAAY,IAAI,SAAS;EAC3B;CACF;CAGA,GAAG,CAAC;CAEJ,GAAG,EACD,gBAAgB,GAAG,OAAO;EAIxB,GAAG,KAAK,+BAA+B;EAEvC,GAAG,KAAK,oBAAoB;CAC9B,EACF;CAEA,GAAG;EACD,gBAAgB,GAAG,OAAO;GAOxB,IAAI,CANgB,GACjB,QACC,qFACF,EACC,IAEE,GACH,GAAG,KAAK,+BAA+B;EAE3C;EACA,cAAc,GAAG,OAAO;GAItB,GAAG,KAAa,qCAAqC;GAGrD,2BAA2B,IADZ,WAAW,IAAI,OAAO,KACN,CAAM;EACvC;CACF;CAEA,GAAG,EACD,gBAAgB,GAAG,OAAO;EACxB,GAAG,KACO;;;;;UAKN,8BACJ;CACF,EACF;CAEA,IAAI,EACF,gBAAgB,GAAG,OAAO;EACxB,GAAG,KAAa;;;OAGf;CACH,EACF;CAEA,IAAI,EACF,gBAAgB,GAAG,OAAO;EACxB,GAAG,KAAa;;;;;;;;;;OAUf;CACH,EACF;CAEA,IAAI;EACF,gBAAgB,GAAG,OAAO;GAGxB,GAAG,KAAa;;;OAGf;EACH;EAEA,cAAc,GAAG,OAAO;GAEtB,GAAG,KAAa;;;;OAIf;EACH;CACF;CAEA,IAAI;EACF,gBAAgB,GAAG,OAAO;GACxB,GAAG,KAAa;;OAEf;EACH;EAEA,cAAc,GAAG,OAAO;GACtB,GAAG,KAAa;;8EAEwD;EAC1E;CACF;AACF;AAGsC,OAAO,KAC3C,yBACF,EAAE,QAAQ,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,IAAI,CAAC,GAAG,CAAC"}
|
|
@@ -2,14 +2,16 @@ import type { LogContext } from '@rocicorp/logger';
|
|
|
2
2
|
import * as v from '../../../../../shared/src/valita.ts';
|
|
3
3
|
import type { PublishedTableSpec } from '../../../db/specs.ts';
|
|
4
4
|
import { type LexiVersion } from '../../../types/lexi-version.ts';
|
|
5
|
-
import { type
|
|
5
|
+
import { type PostgresDB } from '../../../types/pg.ts';
|
|
6
|
+
import { type ShardConfig, type ShardID } from '../../../types/shards.ts';
|
|
6
7
|
import type { Sink } from '../../../types/streams.ts';
|
|
7
8
|
import { type SubscriptionState } from '../../replicator/schema/replication-state.ts';
|
|
8
9
|
import type { ChangeSource } from '../change-source.ts';
|
|
9
10
|
import { type Listener } from '../common/change-stream-multiplexer.ts';
|
|
11
|
+
import type { DownstreamStatusMessage } from '../protocol/current.ts';
|
|
10
12
|
import type { ChangeStreamMessage } from '../protocol/current/downstream.ts';
|
|
11
13
|
import { type InitialSyncOptions, type ServerContext } from './initial-sync.ts';
|
|
12
|
-
import type { MessageRelation as PostgresRelation } from './logical-replication/pgoutput.types.ts';
|
|
14
|
+
import type { MessageMessage, MessageRelation as PostgresRelation } from './logical-replication/pgoutput.types.ts';
|
|
13
15
|
/**
|
|
14
16
|
* Initializes a Postgres change source, including the initial sync of the
|
|
15
17
|
* replica, before streaming changes from the corresponding logical replication
|
|
@@ -31,6 +33,38 @@ declare const lagReportSchema: v.ObjectType<{
|
|
|
31
33
|
commitTimeMs: v.Type<number>;
|
|
32
34
|
}, undefined>;
|
|
33
35
|
export type LagReport = v.Infer<typeof lagReportSchema>;
|
|
36
|
+
export declare class LagReporter {
|
|
37
|
+
#private;
|
|
38
|
+
static readonly MESSAGE_SUFFIX = "/lag-report/v1";
|
|
39
|
+
readonly messagePrefix: string;
|
|
40
|
+
constructor(lc: LogContext, shard: ShardID, db: PostgresDB, lagIntervalMs: number);
|
|
41
|
+
get pgVersion(): number | undefined;
|
|
42
|
+
initiateLagReport(log?: boolean): Promise<{
|
|
43
|
+
nextSendTimeMs: number;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* In Postgres < 17, the pg_logical_emit_message lacks an immediate "flush"
|
|
47
|
+
* option, which can cause messages to be missed when the replication stream
|
|
48
|
+
* starts up:
|
|
49
|
+
*
|
|
50
|
+
* ```
|
|
51
|
+
* * emit message → WAL write (buffered, not flushed)
|
|
52
|
+
* * walsender reads up to current flush LSN
|
|
53
|
+
* * emitted message's LSN is beyond flush LSN → not yet visible
|
|
54
|
+
* * stream feedback/acknowledgment advances slot
|
|
55
|
+
* * WAL eventually flushes → but slot has already moved past it
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* This has been seen to happen for the initial `wal_writer_delay` interval
|
|
59
|
+
* of a replication session.
|
|
60
|
+
*
|
|
61
|
+
* To account for this, the last emitted lag report is considered "received"
|
|
62
|
+
* if the stream has advanced beyond the LSN of the report.
|
|
63
|
+
*/
|
|
64
|
+
checkCurrentLSN(lsn: bigint): DownstreamStatusMessage | undefined;
|
|
65
|
+
stop(): void;
|
|
66
|
+
processLagReportMessage(msg: MessageMessage): DownstreamStatusMessage;
|
|
67
|
+
}
|
|
34
68
|
export declare function relationDifferent(a: PublishedTableSpec, b: PostgresRelation): boolean;
|
|
35
69
|
export {};
|
|
36
70
|
//# sourceMappingURL=change-source.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAcjD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AAMzD,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAcjD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AAMzD,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,OAAO,EACb,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,wCAAwC,CAAC;AAEhD,OAAO,KAAK,EAEV,uBAAuB,EAExB,MAAM,wBAAwB,CAAC;AAQhC,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAEV,cAAc,EACd,eAAe,IAAI,gBAAgB,EACpC,MAAM,yCAAyC,CAAC;AA0BjD;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,kBAAkB,EAC/B,OAAO,EAAE,aAAa,EACtB,mBAAmB,SAAI,GACtB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CAuC7E;AAuaD,qBAAa,KAAM,YAAW,QAAQ;;gBAIxB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;IAI9B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAgC3C,GAAG,CAAC,SAAS,EAAE,WAAW;CAoB3B;AAED,QAAA,MAAM,eAAe;;;;aAInB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAIxD,qBAAa,WAAW;;IACtB,MAAM,CAAC,QAAQ,CAAC,cAAc,oBAAoB;IAGlD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAqB7B,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,MAAM;IAiBvB,IAAI,SAAS,uBAEZ;IAEK,iBAAiB,CAAC,GAAG,UAAQ;;;IA0EnC;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS;IAcjE,IAAI;IA2CJ,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,uBAAuB;CAiDtE;AAonBD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,gBAAgB,WAwB3E"}
|
|
@@ -9,6 +9,7 @@ import { stringify } from "../../../../../shared/src/bigint-json.js";
|
|
|
9
9
|
import { upstreamSchema } from "../../../types/shards.js";
|
|
10
10
|
import { Database } from "../../../../../zqlite/src/db.js";
|
|
11
11
|
import { UnsupportedColumnDefaultError, mapPostgresToLiteColumn } from "../../../db/pg-to-lite.js";
|
|
12
|
+
import { getOrCreateCounter } from "../../../observability/metrics.js";
|
|
12
13
|
import { getSubscriptionStateAndContext } from "../../replicator/schema/replication-state.js";
|
|
13
14
|
import { StatementRunner } from "../../../db/statements.js";
|
|
14
15
|
import { connectPgClient, isPostgresError, pgClient } from "../../../types/pg.js";
|
|
@@ -314,6 +315,10 @@ var LagReporter = class LagReporter {
|
|
|
314
315
|
messagePrefix;
|
|
315
316
|
#db;
|
|
316
317
|
#lagIntervalMs;
|
|
318
|
+
#lagReportRetries = getOrCreateCounter("replication", "lag_report_retries", {
|
|
319
|
+
description: "Number of replication lag reports retried because the expected report was not received before the next report interval.",
|
|
320
|
+
unit: "{report}"
|
|
321
|
+
});
|
|
317
322
|
#pgVersion;
|
|
318
323
|
#expectingLagReport = null;
|
|
319
324
|
#timer;
|
|
@@ -347,33 +352,39 @@ var LagReporter = class LagReporter {
|
|
|
347
352
|
this.#expectingLagReport = lagReport;
|
|
348
353
|
let commitTimeMs;
|
|
349
354
|
let lsn;
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
355
|
+
try {
|
|
356
|
+
if (pgVersion >= 17e4) [{commitTimeMs, lsn}] = await this.#db`
|
|
357
|
+
WITH CTE AS (SELECT extract(epoch from now()) * 1000 AS "commitTimeMs")
|
|
358
|
+
SELECT "commitTimeMs", pg_logical_emit_message(
|
|
359
|
+
false,
|
|
360
|
+
${this.messagePrefix},
|
|
361
|
+
json_build_object(
|
|
362
|
+
'id', ${id}::text,
|
|
363
|
+
'sendTimeMs', ${now}::int8,
|
|
364
|
+
'commitTimeMs', "commitTimeMs"
|
|
365
|
+
)::text,
|
|
366
|
+
true
|
|
367
|
+
) as lsn FROM CTE;
|
|
368
|
+
`;
|
|
369
|
+
else [{commitTimeMs, lsn}] = await this.#db`
|
|
370
|
+
WITH CTE AS (SELECT extract(epoch from now()) * 1000 as "commitTimeMs")
|
|
371
|
+
SELECT "commitTimeMs", pg_logical_emit_message(
|
|
372
|
+
false,
|
|
373
|
+
${this.messagePrefix},
|
|
374
|
+
json_build_object(
|
|
375
|
+
'id', ${id}::text,
|
|
376
|
+
'sendTimeMs', ${now}::int8,
|
|
377
|
+
'commitTimeMs', "commitTimeMs"
|
|
378
|
+
)::text
|
|
379
|
+
) as lsn FROM CTE;
|
|
380
|
+
`;
|
|
381
|
+
} catch (e) {
|
|
382
|
+
if (this.#expectingLagReport?.id === id) this.#expectingLagReport = null;
|
|
383
|
+
throw e;
|
|
384
|
+
}
|
|
375
385
|
lagReport.lsn = toBigInt(lsn);
|
|
376
386
|
lagReport.commitTimeMs = commitTimeMs;
|
|
387
|
+
if (this.#expectingLagReport?.id === id) this.#scheduleMissingReportRetry(id);
|
|
377
388
|
if (log) this.#lc.info?.(`initiated lag report at lsn ${lsn}`, {
|
|
378
389
|
id,
|
|
379
390
|
lsn,
|
|
@@ -415,6 +426,7 @@ var LagReporter = class LagReporter {
|
|
|
415
426
|
this.#expectingLagReport = null;
|
|
416
427
|
clearTimeout(this.#timer);
|
|
417
428
|
this.#timer = setTimeout(async () => {
|
|
429
|
+
this.#timer = void 0;
|
|
418
430
|
try {
|
|
419
431
|
await this.initiateLagReport();
|
|
420
432
|
} catch (e) {
|
|
@@ -423,6 +435,26 @@ var LagReporter = class LagReporter {
|
|
|
423
435
|
}
|
|
424
436
|
}, delayMs);
|
|
425
437
|
}
|
|
438
|
+
#scheduleMissingReportRetry(reportID) {
|
|
439
|
+
clearTimeout(this.#timer);
|
|
440
|
+
this.#timer = setTimeout(async () => {
|
|
441
|
+
this.#timer = void 0;
|
|
442
|
+
const missingReport = this.#expectingLagReport;
|
|
443
|
+
if (missingReport?.id !== reportID) return;
|
|
444
|
+
this.#lagReportRetries.add(1);
|
|
445
|
+
this.#lc.warn?.(`retrying missing lag report`, {
|
|
446
|
+
id: missingReport.id,
|
|
447
|
+
lsn: fromBigInt(missingReport.lsn),
|
|
448
|
+
sendTimeMs: missingReport.sendTimeMs
|
|
449
|
+
});
|
|
450
|
+
try {
|
|
451
|
+
await this.initiateLagReport();
|
|
452
|
+
} catch (e) {
|
|
453
|
+
this.#lc.warn?.(`error retrying lag report`, e);
|
|
454
|
+
this.#scheduleNextReport(this.#lagIntervalMs);
|
|
455
|
+
}
|
|
456
|
+
}, this.#lagIntervalMs);
|
|
457
|
+
}
|
|
426
458
|
processLagReportMessage(msg) {
|
|
427
459
|
assert(msg.prefix === this.messagePrefix, `unexpected message prefix: ${msg.prefix}`);
|
|
428
460
|
const report = parseLogicalMessageContent(this.#lc, msg, lagReportSchema);
|
|
@@ -430,9 +462,15 @@ var LagReporter = class LagReporter {
|
|
|
430
462
|
}
|
|
431
463
|
#processLagReport(report, watermark) {
|
|
432
464
|
const now = Date.now();
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
465
|
+
const expectedReport = this.#expectingLagReport;
|
|
466
|
+
let nextSendTimeMs;
|
|
467
|
+
if (report.id === expectedReport?.id) {
|
|
468
|
+
nextSendTimeMs = Math.max(now, report.sendTimeMs + this.#lagIntervalMs);
|
|
469
|
+
this.#scheduleNextReport(nextSendTimeMs - now);
|
|
470
|
+
} else {
|
|
471
|
+
this.#lc.debug?.(`received extraneous lag report`, { report });
|
|
472
|
+
nextSendTimeMs = expectedReport?.sendTimeMs ?? Math.max(now, report.sendTimeMs + this.#lagIntervalMs);
|
|
473
|
+
}
|
|
436
474
|
const { sendTimeMs, commitTimeMs } = report;
|
|
437
475
|
return [
|
|
438
476
|
"status",
|
|
@@ -581,6 +619,11 @@ var ChangeMaker = class {
|
|
|
581
619
|
}
|
|
582
620
|
const prevEvent = this.#lastReplicationEvent;
|
|
583
621
|
this.#lastReplicationEvent = event;
|
|
622
|
+
const { schema } = event;
|
|
623
|
+
if (schema === void 0) {
|
|
624
|
+
lc.debug?.(`received context-only ${msg.prefix}/${type} event`, { event: summarizeReplicationEventForLog(event) });
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
584
627
|
const prevSchema = event.previousSchema === void 0 ? prevEvent?.schema : event.previousSchema;
|
|
585
628
|
if (!prevSchema) {
|
|
586
629
|
lc.info?.(`received ${msg.prefix}/${type} event`, { event: summarizeReplicationEventForLog(event) });
|
|
@@ -588,9 +631,9 @@ var ChangeMaker = class {
|
|
|
588
631
|
}
|
|
589
632
|
const effectiveTag = prevEvent?.type === "ddlStart" ? prevEvent.event.tag : event.type === "ddlUpdate" ? event.event.tag : "UNKNOWN";
|
|
590
633
|
lc.info?.(`processing ${effectiveTag} command from ${msg.prefix}/${type}`, { event: summarizeReplicationEventForLog(event) });
|
|
591
|
-
const changes = this.#makeSchemaChanges(lc, prevSchema, event, effectiveTag).map((change) => ["data", change]);
|
|
634
|
+
const changes = this.#makeSchemaChanges(lc, prevSchema, schema, event, effectiveTag).map((change) => ["data", change]);
|
|
592
635
|
lc.info?.(`${changes.length} schema change(s)`, { changes });
|
|
593
|
-
const replicaIdentities = replicaIdentitiesForTablesWithoutPrimaryKeys(
|
|
636
|
+
const replicaIdentities = replicaIdentitiesForTablesWithoutPrimaryKeys(schema);
|
|
594
637
|
if (replicaIdentities) this.#replicaIdentityTimer = setTimeout(async () => {
|
|
595
638
|
try {
|
|
596
639
|
await replicaIdentities.apply(lc, this.#db);
|
|
@@ -627,10 +670,10 @@ var ChangeMaker = class {
|
|
|
627
670
|
* columns. This, for example, would be needed to properly support changing
|
|
628
671
|
* the type of a column that's indexed.
|
|
629
672
|
*/
|
|
630
|
-
#makeSchemaChanges(lc, preSchema, event, tag) {
|
|
673
|
+
#makeSchemaChanges(lc, preSchema, nextSchema, event, tag) {
|
|
631
674
|
try {
|
|
632
675
|
const [prevTbl, prevIdx] = specsByID(preSchema);
|
|
633
|
-
const [nextTbl, nextIdx] = specsByID(
|
|
676
|
+
const [nextTbl, nextIdx] = specsByID(nextSchema);
|
|
634
677
|
const changes = [];
|
|
635
678
|
for (const table of nextTbl.values()) validate(lc, table);
|
|
636
679
|
const [droppedIdx, createdIdx] = symmetricDifferences(prevIdx, nextIdx);
|
|
@@ -903,7 +946,7 @@ function summarizeReplicationEventForLog(event) {
|
|
|
903
946
|
const { previousSchema, ...eventWithoutSchemas } = rest;
|
|
904
947
|
return {
|
|
905
948
|
...eventWithoutSchemas,
|
|
906
|
-
schema: summarizeSchemaForLog(schema),
|
|
949
|
+
...schema !== void 0 && { schema: summarizeSchemaForLog(schema) },
|
|
907
950
|
...previousSchema !== void 0 && { previousSchema: previousSchema === null ? null : summarizeSchemaForLog(previousSchema) }
|
|
908
951
|
};
|
|
909
952
|
}
|