@rocicorp/zero 1.12.0-canary.11 → 1.12.0-canary.13
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/replicache/src/btree/diff.d.ts.map +1 -1
- package/out/replicache/src/btree/diff.js +1 -2
- package/out/replicache/src/btree/diff.js.map +1 -1
- package/out/replicache/src/btree/read.d.ts +31 -1
- package/out/replicache/src/btree/read.d.ts.map +1 -1
- package/out/replicache/src/btree/read.js +74 -19
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/btree/splice.d.ts.map +1 -1
- package/out/replicache/src/btree/splice.js +2 -1
- package/out/replicache/src/btree/splice.js.map +1 -1
- package/out/replicache/src/btree/write.d.ts.map +1 -1
- package/out/replicache/src/btree/write.js +2 -1
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +1 -0
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +13 -5
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/scan-iterator.js +1 -1
- package/out/replicache/src/sync/request-id.js +2 -2
- package/out/replicache/src/sync/request-id.js.map +1 -1
- package/out/shared/src/bigint-json.d.ts.map +1 -1
- package/out/shared/src/bigint-json.js +1 -0
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/btree-set.d.ts +21 -0
- package/out/shared/src/btree-set.d.ts.map +1 -1
- package/out/shared/src/btree-set.js +54 -0
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/headers.js +20 -0
- package/out/shared/src/headers.js.map +1 -0
- package/out/shared/src/natural-runs.d.ts +191 -0
- package/out/shared/src/natural-runs.d.ts.map +1 -0
- package/out/shared/src/natural-runs.js +337 -0
- package/out/shared/src/natural-runs.js.map +1 -0
- package/out/shared/src/random-uint64.d.ts.map +1 -1
- package/out/shared/src/random-uint64.js +2 -2
- package/out/shared/src/random-uint64.js.map +1 -1
- package/out/shared/src/random-values.d.ts +19 -1
- package/out/shared/src/random-values.d.ts.map +1 -1
- package/out/shared/src/random-values.js +38 -4
- package/out/shared/src/random-values.js.map +1 -1
- package/out/shared/src/tuple-map.d.ts +22 -0
- package/out/shared/src/tuple-map.d.ts.map +1 -0
- package/out/shared/src/tuple-map.js +48 -0
- package/out/shared/src/tuple-map.js.map +1 -0
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
- package/out/zero-cache/src/auth/auth.js +5 -0
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +21 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +47 -9
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +2 -2
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js +3 -2
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/pg-type-parser.js +55 -2
- package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +5 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +6 -29
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/main.js +5 -0
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/replicator.js +7 -4
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/serving-checkpointer.js +41 -0
- package/out/zero-cache/src/server/serving-checkpointer.js.map +1 -0
- package/out/zero-cache/src/server/syncer.js +5 -4
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +2 -1
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts +1 -0
- package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js +2 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/admin-auth.d.ts +22 -0
- package/out/zero-cache/src/services/admin-auth.d.ts.map +1 -0
- package/out/zero-cache/src/services/admin-auth.js +42 -0
- package/out/zero-cache/src/services/admin-auth.js.map +1 -0
- package/out/zero-cache/src/services/auth-attempt-counter.d.ts +33 -0
- package/out/zero-cache/src/services/auth-attempt-counter.d.ts.map +1 -0
- package/out/zero-cache/src/services/auth-attempt-counter.js +61 -0
- package/out/zero-cache/src/services/auth-attempt-counter.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +110 -13
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +13 -7
- 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 +38 -5
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +10 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +4 -0
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -3
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +23 -2
- 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.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +10 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
- package/out/zero-cache/src/services/heapz.js +4 -5
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/error.d.ts +5 -0
- package/out/zero-cache/src/services/mutagen/error.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/error.js +7 -1
- package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
- package/out/zero-cache/src/services/plannerz.js +3 -7
- package/out/zero-cache/src/services/plannerz.js.map +1 -1
- package/out/zero-cache/src/services/profz.d.ts.map +1 -1
- package/out/zero-cache/src/services/profz.js +4 -13
- package/out/zero-cache/src/services/profz.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +29 -2
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +3 -3
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/serving-checkpointer.js +85 -0
- package/out/zero-cache/src/services/replicator/serving-checkpointer.js.map +1 -0
- package/out/zero-cache/src/services/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +2 -7
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +22 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +40 -2
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +54 -17
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +11 -0
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js +107 -0
- package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +11 -6
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +3 -2
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +16 -14
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js +4 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/init.js +3 -0
- package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +90 -52
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/client-group-log-id.d.ts +9 -0
- package/out/zero-cache/src/types/client-group-log-id.d.ts.map +1 -0
- package/out/zero-cache/src/types/client-group-log-id.js +16 -0
- package/out/zero-cache/src/types/client-group-log-id.js.map +1 -0
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +1 -0
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +13 -2
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +2 -2
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.d.ts +25 -4
- package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.js +46 -5
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +10 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/rehome.d.ts +10 -0
- package/out/zero-cache/src/workers/rehome.d.ts.map +1 -1
- package/out/zero-cache/src/workers/rehome.js +4 -2
- package/out/zero-cache/src/workers/rehome.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +9 -4
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +3 -2
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +15 -15
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.d.ts +4 -0
- package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js +4 -0
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +10 -2
- package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +38 -31
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/query.d.ts +2 -2
- package/out/zero-client/src/client/inspector/query.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/query.js +4 -4
- package/out/zero-client/src/client/inspector/query.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.d.ts +3 -0
- package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +10 -5
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/reconnect-backoff.d.ts +31 -0
- package/out/zero-client/src/client/reconnect-backoff.d.ts.map +1 -0
- package/out/zero-client/src/client/reconnect-backoff.js +44 -0
- package/out/zero-client/src/client/reconnect-backoff.js.map +1 -0
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +7 -0
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/util/nanoid.js +2 -2
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +42 -42
- package/out/zero-server/src/process-mutations.d.ts +10 -0
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +39 -6
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +89 -6
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +37 -3
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-attempt-counter.js","names":[],"sources":["../../../../../zero-cache/src/services/auth-attempt-counter.ts"],"sourcesContent":["/**\n * Counts failed password attempts and blocks further attempts after a few\n * failures, for a period that doubles with every failure, up to a cap.\n *\n * A caller that finds the counter blocked must not check the password at all:\n * it records the attempt as another failure, which extends the block, so that\n * a correct password submitted during the block is refused too and the block\n * cannot be ended by guessing right. The counter starts over after a quiet\n * period without failures, or when the caller resets it on success.\n *\n * A blocked attempt is refused immediately rather than delayed, so that the\n * caller never has to wait while holding a lock or an open request.\n */\n\nconst BASE_BLOCK_MS = 1_000;\nconst MAX_BLOCK_MS = 60_000;\n/** A counter with no failures for this long starts over. */\nconst RESET_AFTER_MS = 15 * 60_000;\n\nexport class AuthAttemptCounter {\n readonly #freeAttempts: number;\n readonly #now: () => number;\n #failures = 0;\n #lastFailure = 0;\n #blockedUntil = 0;\n\n /**\n * @param freeAttempts Failures allowed before the counter blocks.\n */\n constructor(freeAttempts: number, now: () => number = () => Date.now()) {\n this.#freeAttempts = freeAttempts;\n this.#now = now;\n }\n\n /**\n * Milliseconds until attempts are allowed again, or 0 when they are\n * allowed now.\n */\n blockedFor(): number {\n return Math.max(0, this.#blockedUntil - this.#now());\n }\n\n /**\n * Records a failed (or blocked) attempt. Returns the number of failures\n * since the counter last started over.\n */\n recordFailure(): number {\n const now = this.#now();\n if (now - this.#lastFailure > RESET_AFTER_MS) {\n this.#failures = 0;\n }\n this.#failures++;\n this.#lastFailure = now;\n if (this.#failures >= this.#freeAttempts) {\n this.#blockedUntil =\n now +\n Math.min(\n BASE_BLOCK_MS * 2 ** (this.#failures - this.#freeAttempts),\n MAX_BLOCK_MS,\n );\n }\n return this.#failures;\n }\n\n /** Forgets all failures, as after a successful attempt. */\n reset(): void {\n this.#failures = 0;\n this.#lastFailure = 0;\n this.#blockedUntil = 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,IAAM,gBAAgB;AACtB,IAAM,eAAe;;AAErB,IAAM,iBAAiB;AAEvB,IAAa,qBAAb,MAAgC;CAC9B;CACA;CACA,YAAY;CACZ,eAAe;CACf,gBAAgB;;;;CAKhB,YAAY,cAAsB,YAA0B,KAAK,IAAI,GAAG;EACtE,KAAK,gBAAgB;EACrB,KAAK,OAAO;CACd;;;;;CAMA,aAAqB;EACnB,OAAO,KAAK,IAAI,GAAG,KAAK,gBAAgB,KAAK,KAAK,CAAC;CACrD;;;;;CAMA,gBAAwB;EACtB,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,MAAM,KAAK,eAAe,gBAC5B,KAAK,YAAY;EAEnB,KAAK;EACL,KAAK,eAAe;EACpB,IAAI,KAAK,aAAa,KAAK,eACzB,KAAK,gBACH,MACA,KAAK,IACH,gBAAgB,MAAM,KAAK,YAAY,KAAK,gBAC5C,YACF;EAEJ,OAAO,KAAK;CACd;;CAGA,QAAc;EACZ,KAAK,YAAY;EACjB,KAAK,eAAe;EACpB,KAAK,gBAAgB;CACvB;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { assert } from "../../../../../shared/src/asserts.js";
|
|
2
|
+
import { randomCharacters } from "../../../../../shared/src/random-values.js";
|
|
2
3
|
import { parse } from "../../../../../shared/src/valita.js";
|
|
3
4
|
import { must } from "../../../../../shared/src/must.js";
|
|
4
|
-
import { randomCharacters } from "../../../../../shared/src/random-values.js";
|
|
5
5
|
import { equals } from "../../../../../shared/src/set-utils.js";
|
|
6
6
|
import { id } from "../../../types/sql.js";
|
|
7
7
|
import { connectPgClient, pgClient } from "../../../types/pg.js";
|
|
@@ -28,6 +28,7 @@ import "./initial-sync.js";
|
|
|
28
28
|
import { streamBackfill } from "./backfill-stream.js";
|
|
29
29
|
import { ReplicationSlotCleanupMonitor } from "./replication-slot-cleanup-monitor.js";
|
|
30
30
|
import { registerReplicationSlotHealthMetrics } from "./replication-slot-health.js";
|
|
31
|
+
import { timingSafeEqual } from "node:crypto";
|
|
31
32
|
import { nanoid } from "nanoid";
|
|
32
33
|
import postgres from "postgres";
|
|
33
34
|
import { PG_ADMIN_SHUTDOWN, PG_INSUFFICIENT_PRIVILEGE } from "@drdgvhbh/postgres-error-codes";
|
|
@@ -123,7 +124,8 @@ var PostgresChangeSource = class {
|
|
|
123
124
|
*/
|
|
124
125
|
const isTransactionalMessage = (lsn, msg) => {
|
|
125
126
|
if (msg.tag === "message" && msg.prefix === this.#lagReporter?.messagePrefix) {
|
|
126
|
-
|
|
127
|
+
const status = this.#lagReporter.processLagReportMessage(msg, lsn);
|
|
128
|
+
if (status) changes.pushStatus(status);
|
|
127
129
|
return false;
|
|
128
130
|
}
|
|
129
131
|
const status = this.#lagReporter?.checkCurrentLSN(lsn);
|
|
@@ -363,6 +365,10 @@ var LagReporter = class LagReporter {
|
|
|
363
365
|
description: "Number of replication lag reports retried because the expected report was not received before the next report interval.",
|
|
364
366
|
unit: "{report}"
|
|
365
367
|
});
|
|
368
|
+
#lagReportsIgnored = getOrCreateCounter("replication", "lag_reports_ignored", {
|
|
369
|
+
description: "Number of messages with the lag report prefix ignored because their content could not be parsed as a lag report.",
|
|
370
|
+
unit: "{message}"
|
|
371
|
+
});
|
|
366
372
|
#pgVersion;
|
|
367
373
|
#expectingLagReport = null;
|
|
368
374
|
#lastAcceptedSendTimeMs = 0;
|
|
@@ -532,10 +538,22 @@ var LagReporter = class LagReporter {
|
|
|
532
538
|
* XLogData header), rather than the `messageLsn` of the message itself,
|
|
533
539
|
* which is not guaranteed to be comparable to other stream LSNs (e.g. on
|
|
534
540
|
* Aurora). See {@link initiateLagReport}.
|
|
541
|
+
* @returns the status to send downstream, or `undefined` if the message is
|
|
542
|
+
* not a lag report. Any session that can call
|
|
543
|
+
* `pg_logical_emit_message()` can emit a message with the lag report
|
|
544
|
+
* prefix; one that cannot be parsed carries no report and is ignored,
|
|
545
|
+
* as a message of an unknown prefix would be, rather than failing the
|
|
546
|
+
* stream (which would replay it from the same position).
|
|
535
547
|
*/
|
|
536
548
|
processLagReportMessage(msg, lsn) {
|
|
537
549
|
assert(msg.prefix === this.messagePrefix, `unexpected message prefix: ${msg.prefix}`);
|
|
538
|
-
|
|
550
|
+
let report;
|
|
551
|
+
try {
|
|
552
|
+
report = parseLogicalMessageContent(this.#lc, msg, lagReportSchema);
|
|
553
|
+
} catch {
|
|
554
|
+
this.#lagReportsIgnored.add(1);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
539
557
|
return this.#processLagReport(report, majorVersionToString(lsn));
|
|
540
558
|
}
|
|
541
559
|
#processLagReport(report, watermark) {
|
|
@@ -700,9 +718,50 @@ var ChangeMaker = class {
|
|
|
700
718
|
default: throw new Error(`Unexpected message type ${stringify(msg)}`);
|
|
701
719
|
}
|
|
702
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Any session that can call `pg_logical_emit_message()` can emit a message
|
|
723
|
+
* with the shard's ddl prefix. Only events that carry the shard's secret,
|
|
724
|
+
* which the upstream functions read from a table in the shard's schema
|
|
725
|
+
* (see `createEventFunctionStatements()`), are applied. Events emitted
|
|
726
|
+
* before the secret was installed (by functions that predate it) are
|
|
727
|
+
* accepted without one.
|
|
728
|
+
*
|
|
729
|
+
* @returns why the event cannot be verified, or `undefined` if it can.
|
|
730
|
+
*/
|
|
731
|
+
#ddlEventOriginProblem(lsn, json, ddlSecret, ddlSecretLsn) {
|
|
732
|
+
const secret = field(json, "secret");
|
|
733
|
+
if (typeof secret === "string") {
|
|
734
|
+
if (equalSecrets(secret, ddlSecret)) return;
|
|
735
|
+
} else if ((secret === void 0 || secret === null) && ddlSecretLsn && lsn <= toBigInt(ddlSecretLsn)) return;
|
|
736
|
+
return `the event does not carry the secret of the ${upstreamSchema(this.#shard)} schema's event functions (secret ${typeof secret === "string" ? "mismatch" : "absent"}, installed at ${ddlSecretLsn ?? "unknown"})`;
|
|
737
|
+
}
|
|
703
738
|
#lastReplicationEvent;
|
|
739
|
+
#ddlEventsIgnored = getOrCreateCounter("replication", "ddl_events_ignored", {
|
|
740
|
+
description: "Number of ddl messages ignored because they were not emitted by the shard's upstream functions (missing or wrong secret, or not valid JSON).",
|
|
741
|
+
unit: "{message}"
|
|
742
|
+
});
|
|
704
743
|
#handleDdlMessage(lc, lsn, msg) {
|
|
705
|
-
const
|
|
744
|
+
const { ddlSecret, ddlSecretLsn } = this.#shardConfig;
|
|
745
|
+
assert(ddlSecret, "shard config has no ddl event secret");
|
|
746
|
+
const content = decodeLogicalMessage(msg);
|
|
747
|
+
let json;
|
|
748
|
+
let problem;
|
|
749
|
+
try {
|
|
750
|
+
json = JSON.parse(content);
|
|
751
|
+
} catch (e) {
|
|
752
|
+
problem = `not valid JSON: ${String(e)}`;
|
|
753
|
+
}
|
|
754
|
+
problem ??= this.#ddlEventOriginProblem(lsn, json, ddlSecret, ddlSecretLsn);
|
|
755
|
+
if (problem !== void 0) {
|
|
756
|
+
this.#ddlEventsIgnored.add(1);
|
|
757
|
+
lc.warn?.(`ignoring ${msg.prefix} message at ${fromBigInt(lsn)} (not emitted by the shard's upstream functions; replication continues): ${problem}`, describeUnverifiedMessage(json));
|
|
758
|
+
lc.debug?.(`ignored ${msg.prefix} message content`, { content: content.substring(0, 4096) });
|
|
759
|
+
return [];
|
|
760
|
+
}
|
|
761
|
+
const event = validateLogicalMessage(lc, msg, json, replicationEventSchema, () => stringify(field(json, "secret") === void 0 ? json : {
|
|
762
|
+
...json,
|
|
763
|
+
secret: "<redacted>"
|
|
764
|
+
}));
|
|
706
765
|
lc = lc.withContext("lsn", fromBigInt(lsn)).withContext("tag", event.event.tag).withContext("query", event.context.query);
|
|
707
766
|
if (event.previousSchema) warnForSkippedIndexes(lc, event.previousSchema, this.#skippedIndexWarnings);
|
|
708
767
|
if (event.schema) warnForSkippedIndexes(lc, event.schema, this.#skippedIndexWarnings);
|
|
@@ -1056,8 +1115,7 @@ function summarizeSchemaForLog(schema) {
|
|
|
1056
1115
|
};
|
|
1057
1116
|
}
|
|
1058
1117
|
function summarizeReplicationEventForLog(event) {
|
|
1059
|
-
const { schema, ...
|
|
1060
|
-
const { previousSchema, ...eventWithoutSchemas } = rest;
|
|
1118
|
+
const { schema, previousSchema, secret: _, ...eventWithoutSchemas } = event;
|
|
1061
1119
|
return {
|
|
1062
1120
|
...eventWithoutSchemas,
|
|
1063
1121
|
...schema !== void 0 && { schema: summarizeSchemaForLog(schema) },
|
|
@@ -1086,20 +1144,59 @@ var ShutdownSignal = class extends AbortError {
|
|
|
1086
1144
|
super("shutdown signal received (e.g. another zero-cache taking over the replication stream)", { cause });
|
|
1087
1145
|
}
|
|
1088
1146
|
};
|
|
1147
|
+
function decodeLogicalMessage({ content }) {
|
|
1148
|
+
return content instanceof Buffer ? content.toString("utf-8") : new TextDecoder().decode(content);
|
|
1149
|
+
}
|
|
1089
1150
|
function parseLogicalMessageContent(lc, msg, schema) {
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1151
|
+
const str = decodeLogicalMessage(msg);
|
|
1152
|
+
let json;
|
|
1153
|
+
try {
|
|
1154
|
+
json = JSON.parse(str);
|
|
1155
|
+
} catch (e) {
|
|
1156
|
+
return failParse(lc, msg, e, str);
|
|
1157
|
+
}
|
|
1158
|
+
return validateLogicalMessage(lc, msg, json, schema, () => str);
|
|
1159
|
+
}
|
|
1160
|
+
function validateLogicalMessage(lc, msg, json, schema, contentForLog) {
|
|
1092
1161
|
try {
|
|
1093
|
-
const json = JSON.parse(str);
|
|
1094
1162
|
return parse(json, schema, "passthrough");
|
|
1095
1163
|
} catch (e) {
|
|
1096
|
-
lc
|
|
1097
|
-
...msg,
|
|
1098
|
-
content: str
|
|
1099
|
-
} });
|
|
1100
|
-
throw e;
|
|
1164
|
+
return failParse(lc, msg, e, contentForLog());
|
|
1101
1165
|
}
|
|
1102
1166
|
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Logs and rethrows a parse failure. Any session that can call
|
|
1169
|
+
* `pg_logical_emit_message()` can emit a message with a prefix that the
|
|
1170
|
+
* zero-cache parses, so the content is untrusted: it is logged at `warn`
|
|
1171
|
+
* (callers skip the message, so there is nothing for an operator to fix in
|
|
1172
|
+
* zero-cache) without the content, which goes to `debug`, bounded.
|
|
1173
|
+
*/
|
|
1174
|
+
function failParse(lc, msg, e, content) {
|
|
1175
|
+
const { content: _, ...header } = msg;
|
|
1176
|
+
lc.warn?.(`unable to parse logical message content: ${String(e)}`, {
|
|
1177
|
+
message: header,
|
|
1178
|
+
contentLength: content.length
|
|
1179
|
+
});
|
|
1180
|
+
lc.debug?.(`unparseable logical message content`, { content: content.substring(0, 4096) });
|
|
1181
|
+
throw e;
|
|
1182
|
+
}
|
|
1183
|
+
/** A property of untrusted JSON, or `undefined` if there is no such property. */
|
|
1184
|
+
function field(json, name) {
|
|
1185
|
+
return typeof json === "object" && json !== null && name in json ? json[name] : void 0;
|
|
1186
|
+
}
|
|
1187
|
+
function describeUnverifiedMessage(json) {
|
|
1188
|
+
const str = (value) => typeof value === "string" ? value.substring(0, 200) : null;
|
|
1189
|
+
return {
|
|
1190
|
+
type: str(field(json, "type")),
|
|
1191
|
+
tag: str(field(field(json, "event"), "tag")),
|
|
1192
|
+
query: str(field(field(json, "context"), "query"))
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
function equalSecrets(a, b) {
|
|
1196
|
+
const bufA = Buffer.from(a);
|
|
1197
|
+
const bufB = Buffer.from(b);
|
|
1198
|
+
return bufA.length === bufB.length && timingSafeEqual(bufA, bufB);
|
|
1199
|
+
}
|
|
1103
1200
|
//#endregion
|
|
1104
1201
|
export { Acker, LagReporter, PostgresChangeSource, isIndexStructurallyChanged };
|
|
1105
1202
|
|