@rebasepro/server-postgres 0.23.0 → 0.24.1-canary.g19596ef
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/README.md +16 -0
- package/dist/{BranchService-DGPL6G_C.js → BranchService-UgKa416x.js} +47 -19
- package/dist/BranchService-UgKa416x.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +187 -13
- package/dist/PostgresBootstrapper.d.ts +101 -2
- package/dist/auth/ensure-tables.d.ts +12 -0
- package/dist/auth/services.d.ts +78 -14
- package/dist/{auth-users-columns-D2LBFrMH.js → auth-users-columns-C72EMoDJ.js} +17 -1
- package/dist/{auth-users-columns-D2LBFrMH.js.map → auth-users-columns-C72EMoDJ.js.map} +1 -1
- package/dist/backup/backup-cli.d.ts +75 -2
- package/dist/backup/backup-service.d.ts +2 -2
- package/dist/backup/pg-binary.d.ts +5 -0
- package/dist/{backup-cli-24v4OlSp.js → backup-cli-yiKd1GsC.js} +168 -75
- package/dist/backup-cli-yiKd1GsC.js.map +1 -0
- package/dist/{backup-service-HQ9GC4tN.js → backup-service-AZWc2wvI.js} +104 -35
- package/dist/backup-service-AZWc2wvI.js.map +1 -0
- package/dist/boot-ddl-B9ZmBJ75.js +105 -0
- package/dist/boot-ddl-B9ZmBJ75.js.map +1 -0
- package/dist/cli-console-DGee3Zl7.js +225 -0
- package/dist/cli-console-DGee3Zl7.js.map +1 -0
- package/dist/cli-console.d.ts +39 -0
- package/dist/cli-errors-oVUuD8k0.js +2180 -0
- package/dist/cli-errors-oVUuD8k0.js.map +1 -0
- package/dist/cli-errors.d.ts +133 -9
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-DnPHXjRH.js} +56 -6
- package/dist/cli-flags-DnPHXjRH.js.map +1 -0
- package/dist/cli-flags.d.ts +15 -0
- package/dist/cli-helpers.d.ts +88 -34
- package/dist/cli-output.d.ts +16 -28
- package/dist/{arg-Dni7MzLB.js → cli-prompt-egtNKkZl.js} +49 -3
- package/dist/cli-prompt-egtNKkZl.js.map +1 -0
- package/dist/cli-prompt.d.ts +19 -0
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli-scratch-database.d.ts +33 -8
- package/dist/cli.js +826 -230
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-DF-8dTVa.js → column-plan-helpers-D_V4oaZh.js} +43 -27
- package/dist/column-plan-helpers-D_V4oaZh.js.map +1 -0
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-C938nR3E.js} +25 -113
- package/dist/connection-C938nR3E.js.map +1 -0
- package/dist/connection.d.ts +13 -2
- package/dist/data-transformer.d.ts +0 -8
- package/dist/databasePoolManager-DZgnBOgj.js +119 -0
- package/dist/databasePoolManager-DZgnBOgj.js.map +1 -0
- package/dist/deferred-index-build-BqmEHGaD.js +1051 -0
- package/dist/deferred-index-build-BqmEHGaD.js.map +1 -0
- package/dist/{doctor-Cb2thZ8s.js → doctor-CUAAOFZm.js} +281 -89
- package/dist/doctor-CUAAOFZm.js.map +1 -0
- package/dist/ensure-collection-policies-Bb51V2OV.js +541 -0
- package/dist/ensure-collection-policies-Bb51V2OV.js.map +1 -0
- package/dist/ensure-collection-tables-egjGBjhv.js +532 -0
- package/dist/ensure-collection-tables-egjGBjhv.js.map +1 -0
- package/dist/{ensure-tables-Dhn9KM3B.js → ensure-tables-DMPWhJ6R.js} +180 -77
- package/dist/ensure-tables-DMPWhJ6R.js.map +1 -0
- package/dist/enum-labels-kept-Msq5gMcb.js +56 -0
- package/dist/enum-labels-kept-Msq5gMcb.js.map +1 -0
- package/dist/generate-drizzle-schema-DrA-YgXv.js +57 -0
- package/dist/generate-drizzle-schema-DrA-YgXv.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-BfzK7UQd.js → generate-drizzle-schema-logic-BaT0H7Gq.js} +29 -11
- package/dist/generate-drizzle-schema-logic-BaT0H7Gq.js.map +1 -0
- package/dist/generate-files-OoRJLEdk.js +199 -0
- package/dist/generate-files-OoRJLEdk.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-B4k9djAV.js +152 -0
- package/dist/generate-postgres-ddl-logic-B4k9djAV.js.map +1 -0
- package/dist/{generated-schema-staleness-Di9c4ZxN.js → generated-schema-staleness-BWN_PsMi.js} +2 -2
- package/dist/{generated-schema-staleness-Di9c4ZxN.js.map → generated-schema-staleness-BWN_PsMi.js.map} +1 -1
- package/dist/generated-sql.d.ts +34 -0
- package/dist/history/HistoryService.d.ts +64 -5
- package/dist/history/ensure-history-table.d.ts +63 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +139 -16738
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-WMuAfxvw.js → introspect-db-logic-BCc1Jym2.js} +654 -353
- package/dist/introspect-db-logic-BCc1Jym2.js.map +1 -0
- package/dist/introspect-db-queries-BhhJg0b8.js +364 -0
- package/dist/introspect-db-queries-BhhJg0b8.js.map +1 -0
- package/dist/open-rebase-CAjWpPjs.js +20895 -0
- package/dist/open-rebase-CAjWpPjs.js.map +1 -0
- package/dist/open-rebase.d.ts +94 -0
- package/dist/pg-error-utils-Bs_Ue4wn.js +395 -0
- package/dist/pg-error-utils-Bs_Ue4wn.js.map +1 -0
- package/dist/{plan-schema-C0fxM8dY.js → plan-schema-CsNgK0qi.js} +279 -534
- package/dist/plan-schema-CsNgK0qi.js.map +1 -0
- package/dist/{policy-drift-DljYdrpW.js → policy-drift-BKC50y2p.js} +33 -9
- package/dist/policy-drift-BKC50y2p.js.map +1 -0
- package/dist/push-index-prebuild-C_qhyoJa.js +45 -0
- package/dist/push-index-prebuild-C_qhyoJa.js.map +1 -0
- package/dist/push-legacy-renames-5g9h3HOa.js +34 -0
- package/dist/push-legacy-renames-5g9h3HOa.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-Bt2d2mRH.js → render-ddl-CrfEe__B.js} +24 -157
- package/dist/render-ddl-CrfEe__B.js.map +1 -0
- package/dist/{rls-bootstrap-sql-B8EclDyM.js → rls-bootstrap-sql-Bd-hJk08.js} +125 -1101
- package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +1 -0
- package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-D7P4-ZYX.js} +138 -18
- package/dist/rls-enforcement-D7P4-ZYX.js.map +1 -0
- package/dist/{__vite-browser-external-BnuHet1e.js → rolldown-runtime-V7rFbMRe.js} +1 -8
- package/dist/schema/atlas-exclude.d.ts +42 -0
- package/dist/schema/auth-schema.d.ts +172 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/carved-out-migration.d.ts +5 -4
- package/dist/schema/classify-change.d.ts +29 -1
- package/dist/schema/collection-index.d.ts +45 -0
- package/dist/schema/column-plan-helpers.d.ts +38 -21
- package/dist/schema/deferred-index-build.d.ts +131 -0
- package/dist/schema/destructive-sql.d.ts +50 -2
- package/dist/schema/doctor-cli.js +110 -6
- package/dist/schema/doctor-cli.js.map +1 -1
- package/dist/schema/doctor-search-scale.d.ts +45 -0
- package/dist/schema/doctor.d.ts +53 -1
- package/dist/schema/ensure-collection-policies.d.ts +95 -35
- package/dist/schema/ensure-collection-tables.d.ts +56 -3
- package/dist/schema/enum-labels-kept.d.ts +26 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-files.d.ts +44 -0
- package/dist/schema/generate-next-step.d.ts +0 -13
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
- package/dist/schema/generate-postgres-ddl.js +10 -81
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/generate-schema-commit.d.ts +12 -0
- package/dist/schema/generated-schema-diff.d.ts +7 -0
- package/dist/schema/introspect-db-logic.d.ts +63 -0
- package/dist/schema/introspect-db-queries.d.ts +21 -2
- package/dist/schema/introspect-db-search.d.ts +22 -0
- package/dist/schema/introspect-db-storage.d.ts +83 -0
- package/dist/schema/introspect-db-structure.d.ts +10 -0
- package/dist/schema/introspect-db.js +29 -21
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migrate-baseline.d.ts +33 -0
- package/dist/schema/migration-post-process.d.ts +34 -0
- package/dist/schema/plan/diff-plan.d.ts +127 -1
- package/dist/schema/plan/plan-schema.d.ts +16 -8
- package/dist/schema/plan/render-ddl.d.ts +8 -1
- package/dist/schema/plan/types.d.ts +45 -11
- package/dist/schema/push-index-prebuild.d.ts +25 -0
- package/dist/schema/push-legacy-renames.d.ts +20 -0
- package/dist/schema/rls-bootstrap-sql.d.ts +15 -0
- package/dist/schema/undeclarable-constraints.d.ts +59 -0
- package/dist/search-column-BM-GV6vH.js +442 -0
- package/dist/search-column-BM-GV6vH.js.map +1 -0
- package/dist/security/policy-drift.d.ts +1 -1
- package/dist/security/rls-enforcement.d.ts +39 -4
- package/dist/services/BranchService.d.ts +29 -1
- package/dist/services/FetchService.d.ts +63 -12
- package/dist/services/PersistService.d.ts +15 -0
- package/dist/services/RelationService.d.ts +106 -3
- package/dist/services/cdc/CdcListener.d.ts +26 -10
- package/dist/services/cdc/identity-columns.d.ts +19 -0
- package/dist/services/cdc/trigger-cdc.d.ts +180 -18
- package/dist/services/channel-bus/PostgresChannelBus.d.ts +12 -7
- package/dist/services/channel-bus/index.d.ts +1 -1
- package/dist/services/channel-history.d.ts +32 -5
- package/dist/services/collection-helpers.d.ts +26 -1
- package/dist/services/field-viewer.d.ts +15 -1
- package/dist/services/pg-notify-listener.d.ts +95 -6
- package/dist/services/realtimeService.d.ts +467 -53
- package/dist/services/row-pipeline.d.ts +16 -9
- package/dist/services/socket-liveness.d.ts +85 -0
- package/dist/services/soft-delete.d.ts +12 -0
- package/dist/services/sql-script.d.ts +275 -0
- package/dist/services/vector-iterative-scan.d.ts +40 -0
- package/dist/services/write-depth.d.ts +22 -0
- package/dist/services/write-transaction-scope.d.ts +15 -3
- package/dist/socket-opt-ins.d.ts +64 -0
- package/dist/source-DB6xJv3p.js +873 -0
- package/dist/source-DB6xJv3p.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/connection-string.d.ts +31 -0
- package/dist/utils/drizzle-conditions.d.ts +63 -3
- package/dist/utils/error-cause.d.ts +7 -0
- package/dist/utils/pg-error-utils.d.ts +2 -4
- package/dist/utils/result-rows.d.ts +10 -0
- package/dist/{vector-index-CMJKb3C9.js → vector-index-BFhyEaiz.js} +2 -2
- package/dist/{vector-index-CMJKb3C9.js.map → vector-index-BFhyEaiz.js.map} +1 -1
- package/dist/websocket-DUiiwQd2.js +1632 -0
- package/dist/websocket-DUiiwQd2.js.map +1 -0
- package/dist/websocket.d.ts +63 -18
- package/package.json +7 -7
- package/dist/BranchService-DGPL6G_C.js.map +0 -1
- package/dist/__vite-browser-external-BnuHet1e.js.map +0 -1
- package/dist/arg-Dni7MzLB.js.map +0 -1
- package/dist/backup-cli-24v4OlSp.js.map +0 -1
- package/dist/backup-service-HQ9GC4tN.js.map +0 -1
- package/dist/cli-errors-B8qHg02P.js +0 -798
- package/dist/cli-errors-B8qHg02P.js.map +0 -1
- package/dist/cli-flags-BglvpjHv.js.map +0 -1
- package/dist/cli-output-CNdMql-L.js +0 -47
- package/dist/cli-output-CNdMql-L.js.map +0 -1
- package/dist/collection-index-DdnsxiJ_.js +0 -286
- package/dist/collection-index-DdnsxiJ_.js.map +0 -1
- package/dist/column-plan-helpers-DF-8dTVa.js.map +0 -1
- package/dist/databasePoolManager-Bj5FbeAs.js.map +0 -1
- package/dist/doctor-Cb2thZ8s.js.map +0 -1
- package/dist/ensure-collection-policies-RHUcEp4v.js +0 -105
- package/dist/ensure-collection-policies-RHUcEp4v.js.map +0 -1
- package/dist/ensure-collection-tables-BEEjn5cn.js +0 -857
- package/dist/ensure-collection-tables-BEEjn5cn.js.map +0 -1
- package/dist/ensure-tables-Dhn9KM3B.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-BfzK7UQd.js.map +0 -1
- package/dist/generate-drizzle-schema-yzY_BLhr.js +0 -123
- package/dist/generate-drizzle-schema-yzY_BLhr.js.map +0 -1
- package/dist/generate-postgres-ddl-logic-Bt2d2mRH.js.map +0 -1
- package/dist/introspect-db-logic-WMuAfxvw.js.map +0 -1
- package/dist/plan-schema-C0fxM8dY.js.map +0 -1
- package/dist/policy-drift-DljYdrpW.js.map +0 -1
- package/dist/rls-bootstrap-sql-B8EclDyM.js.map +0 -1
- package/dist/rls-enforcement-C6Xk0lA6.js.map +0 -1
- package/dist/source-Br7L7GOI.js +0 -410
- package/dist/source-Br7L7GOI.js.map +0 -1
|
@@ -6,9 +6,13 @@ import type { RawSqlRunner } from "../../security/rls-enforcement.js";
|
|
|
6
6
|
* — like Supabase Realtime — sees *every* commit regardless of how it was made.
|
|
7
7
|
* When logical replication is unavailable (managed Postgres without
|
|
8
8
|
* `wal_level=logical`, no replication privilege, no `REPLICA IDENTITY`), this
|
|
9
|
-
* trigger-based fallback provides the same guarantee
|
|
9
|
+
* trigger-based fallback provides the same guarantee:
|
|
10
10
|
*
|
|
11
|
-
* AFTER INSERT/UPDATE/DELETE trigger → pg_notify('rebase_cdc', payload)
|
|
11
|
+
* AFTER INSERT/UPDATE/DELETE/TRUNCATE statement trigger → pg_notify('rebase_cdc', payload)
|
|
12
|
+
*
|
|
13
|
+
* once per statement, naming the changed rows by key in batches of about a
|
|
14
|
+
* thousand — a 1M-row UPDATE is ~1,000 notifications, where a row trigger sent
|
|
15
|
+
* a million into Postgres's shared notification queue, to every instance.
|
|
12
16
|
*
|
|
13
17
|
* A single dedicated LISTEN client per backend instance consumes the channel
|
|
14
18
|
* (see {@link CdcListener}) and feeds the change into the existing
|
|
@@ -21,29 +25,127 @@ import type { RawSqlRunner } from "../../security/rls-enforcement.js";
|
|
|
21
25
|
*/
|
|
22
26
|
/** Postgres NOTIFY channel carrying database-level change events. */
|
|
23
27
|
export declare const CDC_CHANNEL = "rebase_cdc";
|
|
24
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Schema-qualified name of the row-level trigger function: one notification
|
|
30
|
+
* per changed row. What every version before statement-level capture attached
|
|
31
|
+
* everywhere, and still what a partitioned or foreign table gets — see
|
|
32
|
+
* {@link levelForRelkind}.
|
|
33
|
+
*/
|
|
25
34
|
export declare const CDC_TRIGGER_FUNCTION = "rebase.rebase_cdc_notify";
|
|
26
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Schema-qualified name of the statement-level trigger function: one
|
|
37
|
+
* notification per ~1,000 changed rows. See {@link buildCdcFunctionSql}.
|
|
38
|
+
*
|
|
39
|
+
* A function of its own rather than a new body for the row function, because
|
|
40
|
+
* an instance of an older version replaces the row function's body every time
|
|
41
|
+
* it boots: a statement trigger calling it would then run a row trigger's body,
|
|
42
|
+
* for every write, until the next boot of this version.
|
|
43
|
+
*/
|
|
44
|
+
export declare const CDC_STATEMENT_FUNCTION = "rebase.rebase_cdc_notify_statement";
|
|
45
|
+
/** Name of the per-table row-level trigger (unqualified — triggers are namespaced by table). */
|
|
27
46
|
export declare const CDC_TRIGGER_NAME = "rebase_cdc_trigger";
|
|
47
|
+
/** How a table is instrumented: one notification per statement's ~1,000 rows, or one per row. */
|
|
48
|
+
export type CdcTriggerLevel = "statement" | "row";
|
|
49
|
+
/** One trigger this module creates, as `pg_trigger` describes it. */
|
|
50
|
+
interface CdcTriggerSpec {
|
|
51
|
+
name: string;
|
|
52
|
+
/** The function it calls. */
|
|
53
|
+
fn: "row" | "statement";
|
|
54
|
+
/** `CREATE TRIGGER` between the name and `ON`. */
|
|
55
|
+
timing: string;
|
|
56
|
+
/** `CREATE TRIGGER` between the table and `EXECUTE`. */
|
|
57
|
+
forEach: string;
|
|
58
|
+
/** `tgtype`: ROW (1), BEFORE (2), INSERT (4), DELETE (8), UPDATE (16), TRUNCATE (32), INSTEAD (64). */
|
|
59
|
+
type: number;
|
|
60
|
+
/** `tgnewtable` / `tgoldtable`: the transition tables it captures. */
|
|
61
|
+
newTable: string | null;
|
|
62
|
+
oldTable: string | null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The statement-level triggers a plain table gets. One per event, because
|
|
66
|
+
* Postgres refuses transition tables on a trigger with more than one event.
|
|
67
|
+
* The TRUNCATE one is new with them: a row trigger never fires on `TRUNCATE`,
|
|
68
|
+
* so a truncated table used to be announced to nobody.
|
|
69
|
+
*/
|
|
70
|
+
export declare const CDC_STATEMENT_TRIGGERS: readonly CdcTriggerSpec[];
|
|
71
|
+
/**
|
|
72
|
+
* The level a table is instrumented at, by `pg_class.relkind`.
|
|
73
|
+
*
|
|
74
|
+
* A plain table gets statement triggers. A partitioned table keeps the row
|
|
75
|
+
* trigger: Postgres clones a row trigger onto every partition, so a write made
|
|
76
|
+
* straight to a partition is still captured, while a statement trigger on the
|
|
77
|
+
* parent fires only for statements against the parent. A foreign table
|
|
78
|
+
* cannot have transition tables at all.
|
|
79
|
+
*/
|
|
80
|
+
export declare const levelForRelkind: (relkind: string | null | undefined) => CdcTriggerLevel;
|
|
28
81
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* The
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
82
|
+
* How long after a boot first converts a table from the row trigger, the
|
|
83
|
+
* statement triggers still announce every change one row per notification.
|
|
84
|
+
*
|
|
85
|
+
* The triggers are the database's, and an instance of the previous version
|
|
86
|
+
* still running beside this one — a rolling deploy, a rollback — reads the
|
|
87
|
+
* same channel with a parser that knows one row per notification: given a
|
|
88
|
+
* batch it sees no key, so its lists refetch but its single-row and
|
|
89
|
+
* many-to-many subscribers hear nothing. While any such instance may be up,
|
|
90
|
+
* the statement triggers speak its protocol; after this window they batch.
|
|
91
|
+
* Every version that batches reads both. A boot of the older version re-attaches its row trigger (it
|
|
92
|
+
* creates a missing one), and the next boot of this one converts it back and
|
|
93
|
+
* re-opens the window, so a rollback is covered the same way.
|
|
37
94
|
*/
|
|
38
|
-
export declare
|
|
95
|
+
export declare const CDC_ROW_COMPAT_MS: number;
|
|
39
96
|
/**
|
|
40
|
-
* SQL that (re)
|
|
41
|
-
*
|
|
97
|
+
* SQL that (re)creates both CDC trigger functions. Safe to run repeatedly:
|
|
98
|
+
* `CREATE OR REPLACE` updates in place without dropping dependent triggers —
|
|
99
|
+
* which is also how a database instrumented by an older version gets these
|
|
100
|
+
* bodies on its next boot, for every table at once, re-attached or not.
|
|
101
|
+
*
|
|
102
|
+
* Both functions emit `{ schema, table, op, ... }`, and the rows are named by
|
|
103
|
+
* **identity, never the tuple**. Postgres puts no privilege on `LISTEN`: any
|
|
104
|
+
* role that can connect can listen on this channel, whatever it may `SELECT`.
|
|
105
|
+
* When `row` was `to_jsonb(NEW)`, a login with no grant on anything received
|
|
106
|
+
* every changed row of every instrumented table — `password_hash` and
|
|
107
|
+
* verification tokens from the auth table included — past RLS and column
|
|
108
|
+
* grants. The consumer never read anything but the key: every subscriber
|
|
109
|
+
* re-reads the row under its own scope.
|
|
110
|
+
*
|
|
111
|
+
* The identity is, in order:
|
|
112
|
+
* - the columns the trigger was attached with (`TG_ARGV`) — the key the
|
|
113
|
+
* consumer addresses the row by, and for a junction table the two ids that
|
|
114
|
+
* name the child list it changed; see {@link buildCdcTriggerSql};
|
|
115
|
+
* - otherwise the table's primary key, read from the catalogue — a trigger
|
|
116
|
+
* attached with no columns, by an older boot or on a table no collection
|
|
117
|
+
* maps any more;
|
|
118
|
+
* - otherwise `id`, if the table has one, and else nothing: a change the
|
|
119
|
+
* consumer can only treat as collection-wide.
|
|
120
|
+
*
|
|
121
|
+
* `compatUntil` is the end of the window in which the statement function
|
|
122
|
+
* still sends one row per notification — see {@link CDC_ROW_COMPAT_MS}. A
|
|
123
|
+
* timestamp, or `-infinity` for none.
|
|
42
124
|
*/
|
|
43
|
-
export declare function
|
|
125
|
+
export declare function buildCdcFunctionSql(options?: {
|
|
126
|
+
compatUntil?: string;
|
|
127
|
+
}): string;
|
|
128
|
+
/**
|
|
129
|
+
* SQL that (re)attaches the CDC triggers to a single table, at `level`.
|
|
130
|
+
* Every trigger this module owns is dropped first, whichever level it was
|
|
131
|
+
* created at, so a table moves between levels — a row trigger left by an
|
|
132
|
+
* older version becomes the statement triggers — in one transaction, with no
|
|
133
|
+
* moment at which a write is announced by neither.
|
|
134
|
+
*
|
|
135
|
+
* `identityColumns` are what the payload names the changed rows by — see
|
|
136
|
+
* {@link buildCdcFunctionSql}. Passed as trigger arguments, so one shared
|
|
137
|
+
* function serves every table. Empty means "the primary key".
|
|
138
|
+
*/
|
|
139
|
+
export declare function buildCdcTriggerSql(schema: string, table: string, identityColumns?: readonly string[], level?: CdcTriggerLevel): string;
|
|
44
140
|
export interface CdcTableRef {
|
|
45
141
|
schema: string;
|
|
46
142
|
table: string;
|
|
143
|
+
/**
|
|
144
|
+
* The columns a change to this table is announced by — the collection's
|
|
145
|
+
* key columns, or a junction's two id columns. Absent means the primary
|
|
146
|
+
* key. Nothing else ever leaves the trigger; see {@link buildCdcFunctionSql}.
|
|
147
|
+
*/
|
|
148
|
+
identityColumns?: string[];
|
|
47
149
|
}
|
|
48
150
|
export interface ProvisionResult {
|
|
49
151
|
/** Tables the trigger was successfully attached to. */
|
|
@@ -54,11 +156,71 @@ export interface ProvisionResult {
|
|
|
54
156
|
}>;
|
|
55
157
|
}
|
|
56
158
|
/**
|
|
57
|
-
*
|
|
159
|
+
* Replace the already-installed trigger functions with the current bodies, and
|
|
160
|
+
* install nothing where there is none.
|
|
161
|
+
*
|
|
162
|
+
* For a process that owns the schema but is not provisioning capture on this
|
|
163
|
+
* boot — `REALTIME_CDC=off`, or no direct URL to listen on. The triggers an
|
|
164
|
+
* earlier boot attached keep firing whether anything consumes them or not, so
|
|
165
|
+
* a database instrumented by a version whose function put whole rows on the
|
|
166
|
+
* channel kept doing so, for every login that cares to `LISTEN`, until capture
|
|
167
|
+
* was switched back on. The statement function keeps its compatibility
|
|
168
|
+
* window. Returns whether a function was there to replace.
|
|
169
|
+
*/
|
|
170
|
+
export declare function refreshInstalledCdcFunction(run: RawSqlRunner): Promise<boolean>;
|
|
171
|
+
/**
|
|
172
|
+
* Take change capture off the database: detach every capture trigger and drop
|
|
173
|
+
* both functions. Returns how many triggers were attached.
|
|
174
|
+
*
|
|
175
|
+
* For `REALTIME_CDC=off`. Capture switched off used to leave the triggers in
|
|
176
|
+
* place, so every write to every instrumented table still ran one and sent a
|
|
177
|
+
* notification on a channel nothing listened to. Nothing depends on them once
|
|
178
|
+
* capture is off: a process without capture announces its own writes on the
|
|
179
|
+
* cross-instance channel, and the instances that serve realtime listen there.
|
|
180
|
+
*
|
|
181
|
+
* `DROP FUNCTION … CASCADE` takes the triggers with it in one statement. It
|
|
182
|
+
* waits for each table's lock like any trigger DDL, so it runs with the boot's
|
|
183
|
+
* lock budget and is retried on a lock timeout; a database still contended
|
|
184
|
+
* after that keeps its triggers and the caller reports it.
|
|
185
|
+
*/
|
|
186
|
+
export declare function removeTriggerCdc(run: RawSqlRunner): Promise<number>;
|
|
187
|
+
/**
|
|
188
|
+
* What a boot that does not provision capture does with capture an earlier
|
|
189
|
+
* boot installed.
|
|
190
|
+
*
|
|
191
|
+
* - `capture: "off"` — `REALTIME_CDC=off` — removes it ({@link removeTriggerCdc}).
|
|
192
|
+
* - `capture: "not-provisioned"` — capture is wanted but this boot could not
|
|
193
|
+
* install it (no direct URL, `auto` fell back) — leaves the triggers and
|
|
194
|
+
* brings the functions up to date ({@link refreshInstalledCdcFunction}),
|
|
195
|
+
* since they keep firing.
|
|
196
|
+
*/
|
|
197
|
+
export declare function settleInstalledCapture(run: RawSqlRunner, options: {
|
|
198
|
+
capture: "off" | "not-provisioned";
|
|
199
|
+
}): Promise<{
|
|
200
|
+
removed: number;
|
|
201
|
+
} | {
|
|
202
|
+
refreshed: boolean;
|
|
203
|
+
}>;
|
|
204
|
+
/**
|
|
205
|
+
* Idempotently install the CDC trigger functions and per-table triggers.
|
|
206
|
+
*
|
|
207
|
+
* A plain table gets the statement-level triggers, a partitioned or foreign
|
|
208
|
+
* one the row trigger ({@link levelForRelkind}). A table whose triggers are
|
|
209
|
+
* already exactly the ones this would create — same function, same events,
|
|
210
|
+
* enabled, same identity arguments — is left alone; one with a trigger
|
|
211
|
+
* missing, disabled, attached with different arguments or at the other level
|
|
212
|
+
* is (re-)attached, all of it in one transaction.
|
|
213
|
+
*
|
|
214
|
+
* Converting a table from a row trigger an older version attached opens the
|
|
215
|
+
* statement function's compatibility window ({@link CDC_ROW_COMPAT_MS}): the
|
|
216
|
+
* instances that attached it may still be listening.
|
|
58
217
|
*
|
|
59
218
|
* Runs as the owner (server) connection at bootstrap. A table that does not yet
|
|
60
219
|
* exist in the database (schema drift) is skipped with a warning rather than
|
|
61
220
|
* aborting the whole install, so one un-migrated collection cannot disable CDC
|
|
62
221
|
* for the rest.
|
|
63
222
|
*/
|
|
64
|
-
export declare function provisionTriggerCdc(run: RawSqlRunner, tables: CdcTableRef[]
|
|
223
|
+
export declare function provisionTriggerCdc(run: RawSqlRunner, tables: CdcTableRef[], options?: {
|
|
224
|
+
now?: number;
|
|
225
|
+
}): Promise<ProvisionResult>;
|
|
226
|
+
export {};
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* properties of `NOTIFY` shape everything below:
|
|
7
7
|
*
|
|
8
8
|
* - **8000 bytes per payload.** Presence and cursors fit with room to spare; a
|
|
9
|
-
* scene snapshot does not.
|
|
10
|
-
*
|
|
11
|
-
* `rebase.channel_messages` with a sequence
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* scene snapshot does not. A message on a *retained* channel never travels
|
|
10
|
+
* here at all, whatever its size: the statement that numbers it notifies a
|
|
11
|
+
* pointer — the body is already in `rebase.channel_messages` with a sequence
|
|
12
|
+
* number, so the receiver reads it back (`ChannelHistoryStore.append`). That
|
|
13
|
+
* is the same trick the entity path uses (notify an address, refetch the
|
|
14
|
+
* row), applied to a different table, and for the same reason: any database
|
|
15
|
+
* login can `LISTEN`. On an ephemeral channel there is nothing to point at,
|
|
16
|
+
* so the frame carries the message, and an oversized one is refused loudly
|
|
17
|
+
* instead of reaching some instances and not others.
|
|
16
18
|
*
|
|
17
19
|
* - **A notify is a query on the primary database.** Not a slow one, but it
|
|
18
20
|
* competes with the application's real queries, and that — not throughput —
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
* than correctness. That is what makes coalescing safe.
|
|
27
29
|
*/
|
|
28
30
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
31
|
+
import { type PgNotifyListenerStatus } from "../pg-notify-listener.js";
|
|
29
32
|
import { ChannelBus, ChannelBusFrame, ChannelBusHandler } from "./ChannelBus.js";
|
|
30
33
|
/** NOTIFY channel carrying channel-bus frames. */
|
|
31
34
|
export declare const CHANNEL_BUS_NOTIFY_CHANNEL = "rebase_channel_bus";
|
|
@@ -79,6 +82,8 @@ export declare class PostgresChannelBus implements ChannelBus {
|
|
|
79
82
|
* still holds.
|
|
80
83
|
*/
|
|
81
84
|
publish(frame: ChannelBusFrame): Promise<void>;
|
|
85
|
+
/** The LISTEN connection's state, for `/health` — see {@link PgNotifyListener}. */
|
|
86
|
+
status(): PgNotifyListenerStatus | undefined;
|
|
82
87
|
stop(): Promise<void>;
|
|
83
88
|
private openWindow;
|
|
84
89
|
/** Send everything queued and settle the promises waiting on it. */
|
|
@@ -36,7 +36,7 @@ export interface ChannelBusDeps {
|
|
|
36
36
|
* transports this package knows how to construct, so honouring it there would
|
|
37
37
|
* mean silently discarding the object the application handed us.
|
|
38
38
|
*/
|
|
39
|
-
export declare function resolveChannelBusSetting(configured?: ChannelBusSetting): ChannelBusSetting;
|
|
39
|
+
export declare function resolveChannelBusSetting(configured?: ChannelBusSetting, env?: NodeJS.ProcessEnv): ChannelBusSetting;
|
|
40
40
|
/**
|
|
41
41
|
* Produce the bus a setting asks for.
|
|
42
42
|
*
|
|
@@ -53,6 +53,22 @@ export interface ResolvedRetention {
|
|
|
53
53
|
limit?: number;
|
|
54
54
|
ttlMs?: number;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* A bus frame to send in the statement that numbers a retained message — see
|
|
58
|
+
* {@link ChannelHistoryStore.append}.
|
|
59
|
+
*
|
|
60
|
+
* It is always a pointer, `{ kind: "broadcast_ref", sid, channel, seq }`, and
|
|
61
|
+
* never the message. Postgres puts no privilege on `LISTEN`, so a NOTIFY payload
|
|
62
|
+
* reaches every role that can connect to the database; the body stays in
|
|
63
|
+
* `rebase.channel_messages`, behind the table grants, and each receiving
|
|
64
|
+
* instance reads it back by `(channel, seq)` — the sender's id with it.
|
|
65
|
+
*/
|
|
66
|
+
export interface RetainedAnnouncement {
|
|
67
|
+
/** The NOTIFY channel the bus listens on. */
|
|
68
|
+
notifyChannel: string;
|
|
69
|
+
/** The sending instance. */
|
|
70
|
+
sid: string;
|
|
71
|
+
}
|
|
56
72
|
/**
|
|
57
73
|
* Persistence and replay for retained channels.
|
|
58
74
|
*
|
|
@@ -63,10 +79,21 @@ export interface ResolvedRetention {
|
|
|
63
79
|
export declare class ChannelHistoryStore {
|
|
64
80
|
private db;
|
|
65
81
|
private rules;
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Each rule with its retention resolved, once. Matched per call rather
|
|
84
|
+
* than cached per channel name: a cache keyed by the names clients invent
|
|
85
|
+
* grew by every one of them for the life of the process, and matching a
|
|
86
|
+
* handful of prefixes costs less than the hashing a lookup does.
|
|
87
|
+
*/
|
|
88
|
+
private resolvedRules;
|
|
89
|
+
/**
|
|
90
|
+
* Channel → timestamp of its last prune, for {@link PRUNE_THROTTLE_MS}.
|
|
91
|
+
* An entry older than the window throttles nothing, so they are swept once
|
|
92
|
+
* a window: the map holds the channels pruned recently, not every channel
|
|
93
|
+
* ever pruned.
|
|
94
|
+
*/
|
|
69
95
|
private lastPruned;
|
|
96
|
+
private nextPruneSweepAt;
|
|
70
97
|
private tablesReady;
|
|
71
98
|
constructor(db: NodePgDatabase<Record<string, unknown>>, rules?: ChannelRetentionRule[]);
|
|
72
99
|
/** Whether any channel retains anything at all. */
|
|
@@ -91,7 +118,7 @@ export declare class ChannelHistoryStore {
|
|
|
91
118
|
* broadcasts to one channel line up in a single order — and what keeps
|
|
92
119
|
* different channels from contending with each other at all.
|
|
93
120
|
*/
|
|
94
|
-
append(channel: string, event: string, payload: unknown, senderId?: string): Promise<{
|
|
121
|
+
append(channel: string, event: string, payload: unknown, senderId?: string, announce?: RetainedAnnouncement): Promise<{
|
|
95
122
|
seq: number;
|
|
96
123
|
at: string;
|
|
97
124
|
}>;
|
|
@@ -124,6 +151,6 @@ export declare class ChannelHistoryStore {
|
|
|
124
151
|
* volume, which is what makes retention affordable on a hot channel.
|
|
125
152
|
*/
|
|
126
153
|
prune(channel: string, retention: ResolvedRetention): Promise<number>;
|
|
127
|
-
/** Forget
|
|
154
|
+
/** Forget the prune throttle. Called on shutdown. */
|
|
128
155
|
clear(): void;
|
|
129
156
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
|
|
2
|
-
import { SQL } from "drizzle-orm";
|
|
2
|
+
import { type SQL } from "drizzle-orm";
|
|
3
3
|
import { CollectionConfig, ResolvedHasMany, ResolvedHasOne } from "@rebasepro/types";
|
|
4
4
|
import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry.js";
|
|
5
5
|
import { ApiError } from "@rebasepro/server";
|
|
@@ -133,6 +133,31 @@ export declare function getPrimaryKeys(collection: CollectionConfig, registry: P
|
|
|
133
133
|
* wrong and which collection it is wrong about.
|
|
134
134
|
*/
|
|
135
135
|
export declare function requirePrimaryKeys(collection: CollectionConfig, registry: PostgresCollectionRegistry): PrimaryKeyInfo[];
|
|
136
|
+
/**
|
|
137
|
+
* The table's key columns, in key order.
|
|
138
|
+
*
|
|
139
|
+
* Throws when there are none, or when the table has no column for one of them:
|
|
140
|
+
* a WHERE built from fewer columns than the key matches every row that shares
|
|
141
|
+
* the columns it did find, so there is no partial answer worth returning.
|
|
142
|
+
*
|
|
143
|
+
* @param collectionPath named in the error.
|
|
144
|
+
*/
|
|
145
|
+
export declare function primaryKeyColumns(table: PgTable, primaryKeys: PrimaryKeyInfo[], collectionPath: string): AnyPgColumn[];
|
|
146
|
+
/**
|
|
147
|
+
* The column a `joinPath` step names, on a table that may key it either way.
|
|
148
|
+
*
|
|
149
|
+
* A `JoinStep` is written in database terms — `on: { from: "author_id" }` —
|
|
150
|
+
* because there is no collection to translate through: the step names a raw
|
|
151
|
+
* table. A table the boot reads back out of the catalogue is keyed by the
|
|
152
|
+
* *wire* name, `authorId`, while a hand-written Drizzle schema may key it by
|
|
153
|
+
* the column name. Looking the step's name up as a key therefore found
|
|
154
|
+
* nothing on every booted table, and a `via` relation loaded nothing at all.
|
|
155
|
+
*
|
|
156
|
+
* The key first, as the step may already name one; then the column whose SQL
|
|
157
|
+
* name it is. `undefined` when the table has neither, which the caller turns
|
|
158
|
+
* into an error naming the step.
|
|
159
|
+
*/
|
|
160
|
+
export declare function columnNamed(table: PgTable, name: string): AnyPgColumn | undefined;
|
|
136
161
|
/**
|
|
137
162
|
* The column on the *source* table that a `hasOne`/`hasMany` link points at.
|
|
138
163
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FieldViewer } from "@rebasepro/common";
|
|
2
|
+
import type { CollectionConfig } from "@rebasepro/types";
|
|
2
3
|
/**
|
|
3
4
|
* Run `fn` with `viewer` as the caller every row strip judges against.
|
|
4
5
|
*
|
|
@@ -17,3 +18,16 @@ export declare function withFieldViewer<T>(viewer: FieldViewer | undefined, fn:
|
|
|
17
18
|
* that is what establishes the viewer.
|
|
18
19
|
*/
|
|
19
20
|
export declare function currentFieldViewer(): FieldViewer | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The names an API caller's query may not see on `collection`: its
|
|
23
|
+
* `excludeFromApi` fields, in every spelling. Empty on the trusted server plane
|
|
24
|
+
* (no viewer), which may filter and sort on those columns — the auth adapter
|
|
25
|
+
* looks rows up by them — and empty with no collection in hand.
|
|
26
|
+
*
|
|
27
|
+
* A query naming one of these is answered exactly as a query naming a column
|
|
28
|
+
* the table does not have. The REST check passes such a name through to this
|
|
29
|
+
* driver rather than refusing it itself, so the two answers come from one throw
|
|
30
|
+
* and cannot drift: a "not readable" there would confirm a column OpenAPI and
|
|
31
|
+
* `list_collections` hide.
|
|
32
|
+
*/
|
|
33
|
+
export declare function namesHiddenFromCaller(collection: CollectionConfig | undefined): ReadonlySet<string>;
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every cross-instance feature in the backend needs the same thing: one
|
|
5
5
|
* connection *outside* the Drizzle pool that stays open, holds a `LISTEN`, and
|
|
6
|
-
* comes back on its own after the database or the network drops it. CDC
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* comes back on its own after the database or the network drops it. CDC, the
|
|
7
|
+
* channel bus and the cross-instance broadcast all use this one. By default the
|
|
8
|
+
* **first** connect is validated and rethrown, so a caller can fall back to a
|
|
9
|
+
* different strategy, while every later drop is repaired quietly in the
|
|
10
|
+
* background; `retryInitialConnect` repairs the first one the same way.
|
|
11
11
|
*
|
|
12
12
|
* `LISTEN` is session state, so this connection must not go through a
|
|
13
13
|
* transaction-mode pooler (PgBouncer): give it the direct database URL.
|
|
@@ -30,25 +30,114 @@ export interface PgNotifyListenerOptions {
|
|
|
30
30
|
* gone for good; a caller that mirrors state from the channel resyncs here.
|
|
31
31
|
*/
|
|
32
32
|
onReconnect?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* How often the connection proves it is alive with a round trip. `0`
|
|
35
|
+
* turns the heartbeat off. See {@link PgNotifyListener} for why it exists.
|
|
36
|
+
*/
|
|
37
|
+
heartbeatIntervalMs?: number;
|
|
38
|
+
/** How long a heartbeat may take before the connection is declared dead. */
|
|
39
|
+
heartbeatTimeoutMs?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Treat a failed first connect like any later drop: `start()` resolves,
|
|
42
|
+
* the listener reports itself down, and it keeps dialling in the
|
|
43
|
+
* background. For a caller with nothing to fall back to, for whom a
|
|
44
|
+
* database that is unreachable at boot is an outage to wait out rather
|
|
45
|
+
* than a reason to run without the channel. Off by default: `start()`
|
|
46
|
+
* rejects, so a caller with a fallback can take it.
|
|
47
|
+
*/
|
|
48
|
+
retryInitialConnect?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The `application_name` the connection reports in `pg_stat_activity`.
|
|
51
|
+
* Visible to every role, which is what lets one instance count the others
|
|
52
|
+
* (see `RealtimeService.warnIfMemoryBusOnMultiplePods`). A connection
|
|
53
|
+
* string that sets its own `application_name` wins.
|
|
54
|
+
*/
|
|
55
|
+
applicationName?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* What a listener can say about itself — read by `/health`.
|
|
59
|
+
*
|
|
60
|
+
* `connected` is "listening, and the last heartbeat answered", not "a socket
|
|
61
|
+
* object exists": a half-open connection has a perfectly good socket object.
|
|
62
|
+
*/
|
|
63
|
+
export interface PgNotifyListenerStatus {
|
|
64
|
+
channel: string;
|
|
65
|
+
connected: boolean;
|
|
66
|
+
/** Since when (epoch ms) it has been down, while it is down. */
|
|
67
|
+
downSince?: number;
|
|
68
|
+
/** When (epoch ms) it last answered a heartbeat. */
|
|
69
|
+
lastHeartbeatAt?: number;
|
|
33
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Below every common idle-flow timeout: Azure's load balancer drops an idle
|
|
73
|
+
* flow at 4 minutes, AWS's NLB at 350 s, and NAT and conntrack tables and
|
|
74
|
+
* pooler-side reapers are often shorter. A LISTEN connection is idle by
|
|
75
|
+
* nature, so it is exactly the connection those timeouts kill.
|
|
76
|
+
*/
|
|
77
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 30000;
|
|
78
|
+
export declare const DEFAULT_HEARTBEAT_TIMEOUT_MS = 10000;
|
|
79
|
+
/**
|
|
80
|
+
* A half-open connection — one whose packets stop arriving, with no FIN and no
|
|
81
|
+
* RST, which is what an idle-flow timeout on a load balancer, a NAT, a dead
|
|
82
|
+
* middlebox or a failed-over network looks like — fires no `error` and no
|
|
83
|
+
* `end`. The connection object stays, `LISTEN` stays registered on a backend
|
|
84
|
+
* that may no longer exist, and every notification from then on is lost: CDC,
|
|
85
|
+
* the cross-instance broadcast and the channel bus all went quiet with no
|
|
86
|
+
* error, no reconnect and a green `/health`, while writes made through this
|
|
87
|
+
* instance still looked live because they are delivered locally.
|
|
88
|
+
*
|
|
89
|
+
* So the connection proves itself: TCP keepalive is on, and every
|
|
90
|
+
* {@link DEFAULT_HEARTBEAT_INTERVAL_MS} a `SELECT 1` must come back within
|
|
91
|
+
* {@link DEFAULT_HEARTBEAT_TIMEOUT_MS}. One that does not is torn down and
|
|
92
|
+
* replaced, and `onReconnect` resyncs whatever was missed.
|
|
93
|
+
*/
|
|
34
94
|
export declare class PgNotifyListener {
|
|
35
95
|
private readonly options;
|
|
36
96
|
private client?;
|
|
37
97
|
private running;
|
|
38
98
|
private reconnectTimer?;
|
|
99
|
+
private heartbeatTimer?;
|
|
100
|
+
private heartbeatInFlight;
|
|
101
|
+
private downSince?;
|
|
102
|
+
private lastHeartbeatAt?;
|
|
39
103
|
constructor(options: PgNotifyListenerOptions);
|
|
40
104
|
/** Whether the listener is meant to be connected right now. */
|
|
41
105
|
get active(): boolean;
|
|
106
|
+
/** Whether it is listening on a connection that answered its last heartbeat. */
|
|
107
|
+
get connected(): boolean;
|
|
108
|
+
status(): PgNotifyListenerStatus;
|
|
42
109
|
/**
|
|
43
110
|
* Connect and begin listening. Idempotent.
|
|
44
111
|
*
|
|
45
112
|
* Rejects if the *initial* connection or `LISTEN` fails, leaving the
|
|
46
113
|
* listener stopped — callers use that to degrade deliberately instead of
|
|
47
|
-
* running blind against a channel nothing is delivering.
|
|
114
|
+
* running blind against a channel nothing is delivering. With
|
|
115
|
+
* `retryInitialConnect` it resolves instead, reports itself down, and
|
|
116
|
+
* keeps dialling.
|
|
48
117
|
*/
|
|
49
118
|
start(): Promise<void>;
|
|
50
119
|
/** Stop listening and release the connection. Idempotent. */
|
|
51
120
|
stop(): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* @param first The connect `start()` makes: never a reconnect, so it owes
|
|
123
|
+
* no resync, and — unless `retryInitialConnect` — a failure is
|
|
124
|
+
* thrown to the caller rather than retried.
|
|
125
|
+
*/
|
|
52
126
|
private connect;
|
|
127
|
+
/**
|
|
128
|
+
* Give up on the current connection: it is no longer the listener's, so
|
|
129
|
+
* its late events are ignored and `connected` says so at once; it is
|
|
130
|
+
* ended without waiting on it; and a replacement is scheduled.
|
|
131
|
+
*/
|
|
132
|
+
private abandon;
|
|
133
|
+
private startHeartbeat;
|
|
134
|
+
private stopHeartbeat;
|
|
135
|
+
/**
|
|
136
|
+
* One round trip on the LISTEN connection. Not answered in time, it is
|
|
137
|
+
* treated as dead: the client is ended — which, with the query still
|
|
138
|
+
* outstanding, destroys the socket rather than waiting on a goodbye that
|
|
139
|
+
* cannot arrive — and replaced.
|
|
140
|
+
*/
|
|
141
|
+
private heartbeat;
|
|
53
142
|
private scheduleReconnect;
|
|
54
143
|
}
|