@rebasepro/server-postgres 0.24.0 → 0.24.1-canary.gcb918fc
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-BRt78gfa.js → BranchService-Bsn2wfaj.js} +3 -2
- package/dist/{BranchService-BRt78gfa.js.map → BranchService-Bsn2wfaj.js.map} +1 -1
- package/dist/PostgresBackendDriver.d.ts +91 -2
- package/dist/PostgresBootstrapper.d.ts +101 -2
- package/dist/auth/ensure-tables.d.ts +12 -0
- package/dist/auth/services.d.ts +6 -0
- package/dist/backup/backup-cli.d.ts +14 -0
- package/dist/backup/backup-service.d.ts +2 -2
- package/dist/backup/pg-binary.d.ts +5 -0
- package/dist/{backup-cli-DW5p9_zv.js → backup-cli-DnpJi6I7.js} +37 -29
- package/dist/backup-cli-DnpJi6I7.js.map +1 -0
- package/dist/{backup-service-EwcDVG-8.js → backup-service-BTBB7RXn.js} +55 -21
- package/dist/backup-service-BTBB7RXn.js.map +1 -0
- package/dist/boot-ddl-DVpsFN6r.js +105 -0
- package/dist/boot-ddl-DVpsFN6r.js.map +1 -0
- package/dist/cli-console-Ca6ZlAVU.js +154 -0
- package/dist/cli-console-Ca6ZlAVU.js.map +1 -0
- package/dist/cli-console.d.ts +39 -0
- package/dist/cli-errors-CaI4ntyD.js +1919 -0
- package/dist/cli-errors-CaI4ntyD.js.map +1 -0
- package/dist/cli-errors.d.ts +51 -5
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-JNI-pwSf.js} +6 -3
- package/dist/cli-flags-JNI-pwSf.js.map +1 -0
- package/dist/cli-helpers.d.ts +40 -0
- package/dist/cli-output.d.ts +16 -28
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli.js +566 -182
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-1-LQD0yI.js → column-plan-helpers-drZIsSNf.js} +10 -4
- package/dist/{column-plan-helpers-1-LQD0yI.js.map → column-plan-helpers-drZIsSNf.js.map} +1 -1
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-C9mT8bgf.js} +24 -112
- package/dist/connection-C9mT8bgf.js.map +1 -0
- package/dist/connection.d.ts +13 -2
- package/dist/databasePoolManager-BIuewidb.js +119 -0
- package/dist/databasePoolManager-BIuewidb.js.map +1 -0
- package/dist/{ensure-collection-tables-kkkHk8oo.js → deferred-index-build-DP1_SNFo.js} +484 -346
- package/dist/deferred-index-build-DP1_SNFo.js.map +1 -0
- package/dist/{doctor-C-sYWbmt.js → doctor-C4WiftHm.js} +63 -52
- package/dist/doctor-C4WiftHm.js.map +1 -0
- package/dist/ensure-collection-policies-CXpbeNag.js +541 -0
- package/dist/ensure-collection-policies-CXpbeNag.js.map +1 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js +532 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js.map +1 -0
- package/dist/{ensure-tables-BmI_tRxc.js → ensure-tables-CVm_yvzb.js} +173 -77
- package/dist/ensure-tables-CVm_yvzb.js.map +1 -0
- package/dist/enum-labels-kept-D8fI1MoX.js +56 -0
- package/dist/enum-labels-kept-D8fI1MoX.js.map +1 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js +57 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-sSFDp6LR.js → generate-drizzle-schema-logic-B74WN37S.js} +5 -5
- package/dist/generate-drizzle-schema-logic-B74WN37S.js.map +1 -0
- package/dist/generate-files-CdEckX6c.js +199 -0
- package/dist/generate-files-CdEckX6c.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js → generate-postgres-ddl-logic-B64O2oZF.js} +3 -3
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js.map → generate-postgres-ddl-logic-B64O2oZF.js.map} +1 -1
- package/dist/generated-sql.d.ts +8 -2
- 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 -19236
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-kCETE8TY.js → introspect-db-logic-C-Di8dA9.js} +44 -6
- package/dist/introspect-db-logic-C-Di8dA9.js.map +1 -0
- package/dist/{introspect-db-queries-C_Q5VgQw.js → introspect-db-queries-D6kAqReI.js} +23 -1
- package/dist/introspect-db-queries-D6kAqReI.js.map +1 -0
- package/dist/open-rebase-CbOur0TU.js +20858 -0
- package/dist/open-rebase-CbOur0TU.js.map +1 -0
- package/dist/open-rebase.d.ts +86 -0
- package/dist/pg-error-utils-vJezlXdI.js +388 -0
- package/dist/pg-error-utils-vJezlXdI.js.map +1 -0
- package/dist/{plan-schema-DU9exq6C.js → plan-schema-B1YF_tYq.js} +151 -41
- package/dist/plan-schema-B1YF_tYq.js.map +1 -0
- package/dist/{policy-drift-B-J2hhm0.js → policy-drift-Dm8i-s1J.js} +23 -6
- package/dist/policy-drift-Dm8i-s1J.js.map +1 -0
- package/dist/push-index-prebuild-Db4AQF4i.js +45 -0
- package/dist/push-index-prebuild-Db4AQF4i.js.map +1 -0
- package/dist/push-legacy-renames-BK6ilQq2.js +34 -0
- package/dist/push-legacy-renames-BK6ilQq2.js.map +1 -0
- package/dist/{render-ddl-Ds2t_d9V.js → render-ddl-CtC3y9Ol.js} +8 -4
- package/dist/render-ddl-CtC3y9Ol.js.map +1 -0
- package/dist/{rls-bootstrap-sql-_KNnjanK.js → rls-bootstrap-sql-RQUljnJ1.js} +125 -1270
- package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +1 -0
- package/dist/{rls-enforcement-CfXOJJaW.js → rls-enforcement-DvzqlaL1.js} +129 -17
- package/dist/rls-enforcement-DvzqlaL1.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +2 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/collection-index.d.ts +45 -0
- package/dist/schema/column-plan-helpers.d.ts +5 -0
- package/dist/schema/deferred-index-build.d.ts +131 -0
- package/dist/schema/destructive-sql.d.ts +7 -0
- 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 +20 -1
- package/dist/schema/ensure-collection-policies.d.ts +73 -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.js +10 -81
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/introspect-db-logic.d.ts +12 -0
- package/dist/schema/introspect-db-queries.d.ts +11 -1
- package/dist/schema/introspect-db-structure.d.ts +10 -0
- package/dist/schema/introspect-db.js +4 -5
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migration-post-process.d.ts +34 -0
- package/dist/schema/plan/diff-plan.d.ts +126 -0
- package/dist/schema/plan/types.d.ts +8 -0
- 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/security/rls-enforcement.d.ts +32 -4
- package/dist/services/FetchService.d.ts +58 -8
- package/dist/services/PersistService.d.ts +15 -0
- package/dist/services/RelationService.d.ts +88 -3
- package/dist/services/cdc/CdcListener.d.ts +17 -6
- package/dist/services/cdc/trigger-cdc.d.ts +151 -27
- package/dist/services/channel-bus/index.d.ts +1 -1
- package/dist/services/channel-history.d.ts +15 -4
- package/dist/services/collection-helpers.d.ts +15 -0
- package/dist/services/field-viewer.d.ts +15 -1
- package/dist/services/pg-notify-listener.d.ts +7 -0
- package/dist/services/realtimeService.d.ts +314 -14
- package/dist/services/row-pipeline.d.ts +16 -9
- package/dist/services/socket-liveness.d.ts +42 -2
- package/dist/services/sql-script.d.ts +207 -3
- package/dist/services/vector-iterative-scan.d.ts +40 -0
- package/dist/services/write-depth.d.ts +6 -0
- package/dist/services/write-transaction-scope.d.ts +9 -0
- package/dist/socket-opt-ins.d.ts +64 -0
- package/dist/source-DBDbBTL4.js +811 -0
- package/dist/source-DBDbBTL4.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/drizzle-conditions.d.ts +21 -1
- 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/websocket-BLGc5yhs.js +1604 -0
- package/dist/websocket-BLGc5yhs.js.map +1 -0
- package/dist/websocket.d.ts +13 -0
- package/package.json +7 -7
- package/dist/backup-cli-DW5p9_zv.js.map +0 -1
- package/dist/backup-service-EwcDVG-8.js.map +0 -1
- package/dist/cli-errors-DsA-K9uP.js +0 -893
- package/dist/cli-errors-DsA-K9uP.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/databasePoolManager-Bj5FbeAs.js.map +0 -1
- package/dist/doctor-C-sYWbmt.js.map +0 -1
- package/dist/ensure-collection-policies-B1ureSIV.js +0 -160
- package/dist/ensure-collection-policies-B1ureSIV.js.map +0 -1
- package/dist/ensure-collection-tables-kkkHk8oo.js.map +0 -1
- package/dist/ensure-tables-BmI_tRxc.js.map +0 -1
- package/dist/generate-drizzle-schema-93M0lUxK.js +0 -123
- package/dist/generate-drizzle-schema-93M0lUxK.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +0 -1
- package/dist/introspect-db-logic-kCETE8TY.js.map +0 -1
- package/dist/introspect-db-queries-C_Q5VgQw.js.map +0 -1
- package/dist/plan-schema-DU9exq6C.js.map +0 -1
- package/dist/policy-drift-B-J2hhm0.js.map +0 -1
- package/dist/render-ddl-Ds2t_d9V.js.map +0 -1
- package/dist/rls-bootstrap-sql-_KNnjanK.js.map +0 -1
- package/dist/rls-enforcement-CfXOJJaW.js.map +0 -1
- package/dist/source-Br7L7GOI.js +0 -410
- package/dist/source-Br7L7GOI.js.map +0 -1
- package/dist/utils/sql-redaction.d.ts +0 -21
|
@@ -95,6 +95,13 @@ type StoredCollectionRequest = {
|
|
|
95
95
|
fields?: string[];
|
|
96
96
|
/** `SELECT DISTINCT` over the projection. */
|
|
97
97
|
distinct?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* A subscription to the count alone (`listenCount`): no rows are read,
|
|
100
|
+
* and each frame carries `rows: []` and the `meta` with the total. A
|
|
101
|
+
* server that predates it reads the rows it was asked for (the SDK asks
|
|
102
|
+
* for one) and counts them as usual, so the same frame answers it.
|
|
103
|
+
*/
|
|
104
|
+
countOnly?: boolean;
|
|
98
105
|
};
|
|
99
106
|
/**
|
|
100
107
|
* What an in-process listener asks for: the read, and who it reads as.
|
|
@@ -111,6 +118,7 @@ export type DataDriverSubscriptionRequest = {
|
|
|
111
118
|
} | {
|
|
112
119
|
type: "single";
|
|
113
120
|
id: string | number;
|
|
121
|
+
include?: IncludeSpec;
|
|
114
122
|
});
|
|
115
123
|
/**
|
|
116
124
|
* A registered subscription, plus the two counters that order its deliveries.
|
|
@@ -142,6 +150,13 @@ type Subscription = {
|
|
|
142
150
|
path: string;
|
|
143
151
|
id?: string | number;
|
|
144
152
|
collectionRequest?: StoredCollectionRequest;
|
|
153
|
+
/**
|
|
154
|
+
* The relations a single-row subscription loads into every frame — the
|
|
155
|
+
* `include` a `findById` takes. A list keeps its own in `collectionRequest`.
|
|
156
|
+
*/
|
|
157
|
+
include?: IncludeSpec;
|
|
158
|
+
/** A single-row subscriber that asked for the row's `ETag` on every frame. */
|
|
159
|
+
etag?: boolean;
|
|
145
160
|
authContext?: SubscriptionAuthContext;
|
|
146
161
|
/**
|
|
147
162
|
* An in-process listener's error callback, called when a refetch fails.
|
|
@@ -153,7 +168,28 @@ type Subscription = {
|
|
|
153
168
|
started: number;
|
|
154
169
|
/** The highest started-sequence that has already reached the subscriber. */
|
|
155
170
|
delivered: number;
|
|
171
|
+
/**
|
|
172
|
+
* A socket subscriber's last data frame, as {@link frameDigest} of what
|
|
173
|
+
* follows its subscription id; unset before the first and after an error
|
|
174
|
+
* frame. A refetch that reads the same rows sends it nothing — see
|
|
175
|
+
* {@link sendDataFrame}.
|
|
176
|
+
*/
|
|
177
|
+
lastFrame?: string;
|
|
156
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* How many group refetches may run at once: a quarter of the connection pool
|
|
181
|
+
* the service shares with every REST request, and never fewer than two.
|
|
182
|
+
*
|
|
183
|
+
* One write to a list watched by many different principals arms a refetch
|
|
184
|
+
* per principal, and they all came due in the same tick. Started together,
|
|
185
|
+
* 200 of them were 200 transactions queued on a 20-connection pool, and an
|
|
186
|
+
* unrelated request arriving meanwhile waited behind every one (measured: a
|
|
187
|
+
* `SELECT 1` took 521 ms). Capped, the refetches queue here instead, and
|
|
188
|
+
* three quarters of the pool stays free for the requests. A pool this cannot
|
|
189
|
+
* read (a test double, a client that is not a `pg` pool) is taken to be the
|
|
190
|
+
* default 20-connection one.
|
|
191
|
+
*/
|
|
192
|
+
export declare function refetchConcurrencyFor(db: object): number;
|
|
157
193
|
/**
|
|
158
194
|
* The default ceiling on subscriptions one socket may hold.
|
|
159
195
|
*
|
|
@@ -172,6 +208,25 @@ export declare const DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET = 1000;
|
|
|
172
208
|
* meaning anything.
|
|
173
209
|
*/
|
|
174
210
|
export declare function parseMaxSubscriptionsPerSocket(value: unknown, source: string): number;
|
|
211
|
+
/**
|
|
212
|
+
* The smallest last total at which a group's count is reused under
|
|
213
|
+
* `REALTIME_RECOUNT_INTERVAL_MS`, unless `REALTIME_RECOUNT_MIN_ROWS` says
|
|
214
|
+
* otherwise. Below it a count is cheap, and stays exact.
|
|
215
|
+
*/
|
|
216
|
+
export declare const DEFAULT_RECOUNT_MIN_ROWS = 100000;
|
|
217
|
+
/**
|
|
218
|
+
* Read one of the realtime tuning numbers (`REALTIME_REFETCH_MAX_WAIT_MS`,
|
|
219
|
+
* `REALTIME_RECOUNT_INTERVAL_MS`, `REALTIME_RECOUNT_MIN_ROWS` and their
|
|
220
|
+
* `realtime.*` options). A value someone set and mistyped stops the boot
|
|
221
|
+
* rather than quietly becoming the default.
|
|
222
|
+
*/
|
|
223
|
+
export declare function parseRealtimeTuning(value: unknown, source: string, unit: string): number;
|
|
224
|
+
/**
|
|
225
|
+
* Read `REALTIME_MAX_SOCKETS_PER_USER` / `realtime.maxSocketsPerUser`. Like the
|
|
226
|
+
* subscription ceiling, a value someone set and mistyped stops the boot rather
|
|
227
|
+
* than quietly becoming no ceiling.
|
|
228
|
+
*/
|
|
229
|
+
export declare function parseMaxSocketsPerUser(value: unknown, source: string): number;
|
|
175
230
|
/**
|
|
176
231
|
* PostgreSQL-specific realtime service.
|
|
177
232
|
* Handles WebSocket connections and subscriptions for real-time row updates.
|
|
@@ -188,8 +243,19 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
188
243
|
*/
|
|
189
244
|
readonly supportsChannels = true;
|
|
190
245
|
private clients;
|
|
246
|
+
/** What each socket opted into when it opened — see `socket-opt-ins.ts`. */
|
|
247
|
+
private clientOptIns;
|
|
191
248
|
private channels;
|
|
192
249
|
private presence;
|
|
250
|
+
/**
|
|
251
|
+
* The reverse of {@link channels} and {@link presence}: the channels each
|
|
252
|
+
* client is a member of, and those it has a presence entry in. Kept in
|
|
253
|
+
* step wherever either map gains or loses a client, so a disconnect visits
|
|
254
|
+
* what the client was in. It used to walk every channel on the instance:
|
|
255
|
+
* with a channel per document, 5.4 ms per disconnect at 50k channels.
|
|
256
|
+
*/
|
|
257
|
+
private channelsByClient;
|
|
258
|
+
private presenceByClient;
|
|
193
259
|
/**
|
|
194
260
|
* Ordered, replayable history for channels that opt into it.
|
|
195
261
|
*
|
|
@@ -230,9 +296,17 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
230
296
|
private presenceStore?;
|
|
231
297
|
/** Sweeps roster rows left behind by instances that stopped heartbeating. */
|
|
232
298
|
private presenceSweepInterval?;
|
|
299
|
+
/**
|
|
300
|
+
* The clients that have tracked presence through this instance — the only
|
|
301
|
+
* ones with roster rows to delete when their socket closes. Most sockets
|
|
302
|
+
* never track presence, and each of their closes was a `DELETE` too.
|
|
303
|
+
*/
|
|
304
|
+
private presenceTrackers;
|
|
233
305
|
/**
|
|
234
306
|
* Channels whose oversized ephemeral broadcasts have already been reported,
|
|
235
307
|
* so a hot channel logs the problem once rather than once per message.
|
|
308
|
+
* Forgotten with the channel, so it holds live channels only — not every
|
|
309
|
+
* name a client ever broadcast on.
|
|
236
310
|
*/
|
|
237
311
|
private oversizedBroadcastWarned;
|
|
238
312
|
/**
|
|
@@ -252,6 +326,11 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
252
326
|
private foreignInstanceSeen;
|
|
253
327
|
/** So the multi-pod memory-bus warning is emitted once, not once per join. */
|
|
254
328
|
private memoryBusWarned;
|
|
329
|
+
/** When the database was last asked whether another instance listens — see {@link warnIfMemoryBusOnMultiplePods}. */
|
|
330
|
+
private otherInstancesCheckedAt?;
|
|
331
|
+
/** The check in flight, so a burst of joins asks once. Awaited by tests. */
|
|
332
|
+
private otherInstancesCheck?;
|
|
333
|
+
private static readonly OTHER_INSTANCES_CHECK_INTERVAL_MS;
|
|
255
334
|
private presenceInterval?;
|
|
256
335
|
private static readonly PRESENCE_TIMEOUT_MS;
|
|
257
336
|
/** How often stale roster rows from other instances are reaped. */
|
|
@@ -271,8 +350,25 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
271
350
|
private subscriptionCallbacks;
|
|
272
351
|
/** Each client's subscriptions, by key — for its ceiling and its disconnect. */
|
|
273
352
|
private subscriptionsByClient;
|
|
353
|
+
/**
|
|
354
|
+
* The in-process subscriptions — started by `startDataDriverSubscription`,
|
|
355
|
+
* `subscribeToCollection` or `subscribeToOne` — by the id their caller
|
|
356
|
+
* gave them, for {@link unsubscribe}, which has nothing else to go on. It
|
|
357
|
+
* used to walk every subscription on the server, sockets' included.
|
|
358
|
+
*/
|
|
359
|
+
private inProcessKeysById;
|
|
274
360
|
/** Subscriptions one refetch answers — see {@link SubscriptionGroup}. */
|
|
275
361
|
private groups;
|
|
362
|
+
/**
|
|
363
|
+
* Groups whose refetch is due and waiting for a slot, in the order they
|
|
364
|
+
* came due. A group is in it at most once; changes that arrive while it
|
|
365
|
+
* waits join the read it is waiting for, which starts after them.
|
|
366
|
+
*/
|
|
367
|
+
private refetchQueue;
|
|
368
|
+
/** Group refetches running now — at most {@link maxConcurrentRefetches}. */
|
|
369
|
+
private refetchesRunning;
|
|
370
|
+
/** See {@link refetchConcurrencyFor}. */
|
|
371
|
+
maxConcurrentRefetches: number;
|
|
276
372
|
/**
|
|
277
373
|
* The collection groups on each path, and the single-row groups at each
|
|
278
374
|
* `path` + id: what a change looks up, instead of walking every
|
|
@@ -280,6 +376,21 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
280
376
|
*/
|
|
281
377
|
private collectionGroupsByPath;
|
|
282
378
|
private singleGroupsByAddress;
|
|
379
|
+
/** The single-row groups on each path, for a change that names no row ({@link ANY_ROW}). */
|
|
380
|
+
private singleGroupsByPath;
|
|
381
|
+
/**
|
|
382
|
+
* When something on each path a list group reads last changed: the
|
|
383
|
+
* debounce clock those groups share, moved once per changed row instead
|
|
384
|
+
* of once per row per group. See {@link notifyPathUpdate}.
|
|
385
|
+
*/
|
|
386
|
+
private listPathChangedAt;
|
|
387
|
+
/**
|
|
388
|
+
* The list groups on each path that a change still has to schedule: new
|
|
389
|
+
* ones, and those whose last refetch has started. The rest already have a
|
|
390
|
+
* refetch for every member pending, which the next change only has to
|
|
391
|
+
* keep waiting — the clock above does that.
|
|
392
|
+
*/
|
|
393
|
+
private unscheduledListGroups;
|
|
283
394
|
/**
|
|
284
395
|
* Every path a subscription holds, with the collection it lands on and how
|
|
285
396
|
* many hold it — the candidates `aliasPaths` considers, resolved once.
|
|
@@ -290,21 +401,60 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
290
401
|
* {@link DEFAULT_MAX_SUBSCRIPTIONS_PER_SOCKET}.
|
|
291
402
|
*/
|
|
292
403
|
maxSubscriptionsPerSocket: number;
|
|
404
|
+
/**
|
|
405
|
+
* The longest a change waits for its refetch, in ms; set from config at
|
|
406
|
+
* boot (`REALTIME_REFETCH_MAX_WAIT_MS`). See {@link REFETCH_MAX_WAIT_MS},
|
|
407
|
+
* the default.
|
|
408
|
+
*/
|
|
409
|
+
refetchMaxWaitMs: number;
|
|
410
|
+
/**
|
|
411
|
+
* How long a list's total may be reused before it is counted again, in ms
|
|
412
|
+
* (`REALTIME_RECOUNT_INTERVAL_MS`). Unset, the default, counts with every
|
|
413
|
+
* frame. Only for a group whose last total was at least
|
|
414
|
+
* {@link recountMinRows}: a count is cheap on a small set, and exact there.
|
|
415
|
+
*/
|
|
416
|
+
recountIntervalMs: number | undefined;
|
|
417
|
+
/** See {@link recountIntervalMs} (`REALTIME_RECOUNT_MIN_ROWS`). */
|
|
418
|
+
recountMinRows: number;
|
|
293
419
|
private driver?;
|
|
294
420
|
/** Unique identifier for this process instance, used to skip own notifications. */
|
|
295
421
|
private readonly instanceId;
|
|
422
|
+
/** What this instance's LISTEN connections report in `pg_stat_activity`. */
|
|
423
|
+
private readonly listenerApplicationName;
|
|
296
424
|
/** The LISTEN connection on {@link PG_NOTIFY_CHANNEL}, outside the Drizzle pool. */
|
|
297
425
|
private crossInstanceListener?;
|
|
298
426
|
/** Whether cross-instance broadcasting is active. */
|
|
299
427
|
private broadcasting;
|
|
428
|
+
/**
|
|
429
|
+
* Changes waiting to be announced on {@link PG_NOTIFY_CHANNEL}, and the
|
|
430
|
+
* send that will announce them — see {@link broadcastChange}.
|
|
431
|
+
*/
|
|
432
|
+
private pendingBroadcasts;
|
|
433
|
+
private broadcastFlush?;
|
|
300
434
|
/** Debounce window (ms) for coalescing rapid row updates into a single correctness refetch. */
|
|
301
435
|
private static readonly REFETCH_DEBOUNCE_MS;
|
|
436
|
+
/**
|
|
437
|
+
* The longest a change waits for its refetch, quiet window or not.
|
|
438
|
+
*
|
|
439
|
+
* A pure debounce waits for 300 ms without a change, and a collection
|
|
440
|
+
* written more often than that — an orders, events or chat table at a few
|
|
441
|
+
* writes a second — never went quiet: its subscribers received nothing for
|
|
442
|
+
* as long as the writes continued. Under a steady stream a group now
|
|
443
|
+
* refetches once per this interval, which is the whole cost.
|
|
444
|
+
*/
|
|
445
|
+
private static readonly REFETCH_MAX_WAIT_MS;
|
|
302
446
|
/** Dedicated LISTEN client for DB-level change events (undefined unless CDC is enabled). */
|
|
303
447
|
private cdcListener?;
|
|
304
448
|
/** Whether database-level CDC is the active cross-instance change source. */
|
|
305
449
|
private cdcActive;
|
|
306
450
|
/** Junction table → the child lists its rows belong to, built when CDC starts. */
|
|
307
451
|
private junctionLinkMap?;
|
|
452
|
+
/** Changes waiting for a turn of the event loop — see {@link handleCdcEvents}. */
|
|
453
|
+
private cdcBacklog;
|
|
454
|
+
/** Changes handled in the current turn. */
|
|
455
|
+
private cdcTurnRows;
|
|
456
|
+
/** How many changed rows one turn of the event loop routes: ~5–10 ms of work. */
|
|
457
|
+
private static readonly CDC_ROWS_PER_TURN;
|
|
308
458
|
/** Reverse lookup: `schema.table` (and bare `table`) → collection, built when CDC starts. */
|
|
309
459
|
private cdcTableMap?;
|
|
310
460
|
/**
|
|
@@ -349,6 +499,18 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
349
499
|
* redaction lives.
|
|
350
500
|
*/
|
|
351
501
|
private debugLog;
|
|
502
|
+
/**
|
|
503
|
+
* {@link rawQueryLoggingEnabled}, read at most once a second.
|
|
504
|
+
*
|
|
505
|
+
* It reads two environment variables, and `process.env` is a getenv per
|
|
506
|
+
* read: called three or four times per changed row, it was the largest
|
|
507
|
+
* cost of a change notification here — 1.4 µs of every ~3.5 µs per row,
|
|
508
|
+
* so ~1.4 s of event loop for a 1M-row statement. A switch flipped at run
|
|
509
|
+
* time is honoured within a second.
|
|
510
|
+
*/
|
|
511
|
+
private debugEnabled;
|
|
512
|
+
private debugFlag;
|
|
513
|
+
private debugCheckedAt;
|
|
352
514
|
setDataDriver(driver: DataDriver): void;
|
|
353
515
|
/**
|
|
354
516
|
* The live subscriptions by the id their client gave them — for tests and
|
|
@@ -368,13 +530,18 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
368
530
|
* every index that finds it.
|
|
369
531
|
*/
|
|
370
532
|
private registerSubscription;
|
|
533
|
+
/** Remember an in-process subscription by its caller's id — see {@link inProcessKeysById}. */
|
|
534
|
+
private indexInProcess;
|
|
371
535
|
/** Remove the subscription under `key` from everything that holds it. */
|
|
372
536
|
private dropSubscription;
|
|
373
537
|
/** Take `subscription` out of its group and path count; a group left empty goes, timer and all. */
|
|
374
538
|
private leaveGroup;
|
|
539
|
+
/** See {@link unscheduledListGroups}. */
|
|
540
|
+
private markListGroupUnscheduled;
|
|
375
541
|
/**
|
|
376
542
|
* Ask for a refetch of `group` once changes have been quiet for the
|
|
377
|
-
* debounce window — for every member, or only for `member
|
|
543
|
+
* debounce window — for every member, or only for `member` — and in any
|
|
544
|
+
* case no later than {@link REFETCH_MAX_WAIT_MS} after the first of them.
|
|
378
545
|
*
|
|
379
546
|
* The window is measured from the last change, as a debounce is, but the
|
|
380
547
|
* timer is not re-armed per change: a 10k-row statement is 10k changes,
|
|
@@ -384,6 +551,13 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
384
551
|
*/
|
|
385
552
|
private scheduleGroupRefetch;
|
|
386
553
|
private armGroupTimer;
|
|
554
|
+
/**
|
|
555
|
+
* Start queued group refetches while fewer than
|
|
556
|
+
* {@link maxConcurrentRefetches} are running. Each one that settles starts
|
|
557
|
+
* the next, so the fan-out of one write waits its turn here rather than
|
|
558
|
+
* in the pool, ahead of every REST request.
|
|
559
|
+
*/
|
|
560
|
+
private drainRefetchQueue;
|
|
387
561
|
/**
|
|
388
562
|
* One read for the group, delivered to each member it is for.
|
|
389
563
|
*
|
|
@@ -456,7 +630,12 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
456
630
|
unsubscribe(subscriptionId: string): void;
|
|
457
631
|
addClient(clientId: string, ws: WebSocket): void;
|
|
458
632
|
handleClientMessage(clientId: string, message: WebSocketMessage, authContext?: SubscriptionAuthContext): Promise<void>;
|
|
633
|
+
/** Record what a socket opted into when it opened. See `socket-opt-ins.ts`. */
|
|
634
|
+
setClientOptIns(clientId: string, optIns: ReadonlySet<string>): void;
|
|
459
635
|
removeClient(clientId: string): Promise<void>;
|
|
636
|
+
/** Record that `clientId` is in `channel`, in a reverse index — see {@link channelsByClient}. */
|
|
637
|
+
private indexClientChannel;
|
|
638
|
+
private unindexClientChannel;
|
|
460
639
|
private handleMessage;
|
|
461
640
|
/**
|
|
462
641
|
* Refuse a new subscription past the socket's ceiling, and say so.
|
|
@@ -569,13 +748,20 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
569
748
|
*/
|
|
570
749
|
private reportDriverFetchFailure;
|
|
571
750
|
/**
|
|
572
|
-
* Fetch a collection with optional RLS auth context
|
|
751
|
+
* Fetch a collection with optional RLS auth context, and with `meta: true`
|
|
752
|
+
* the `meta` a socket frame carries beside the rows.
|
|
753
|
+
*
|
|
573
754
|
* When authContext is provided, the fetch runs inside a transaction
|
|
574
|
-
* with set_config calls so PostgreSQL RLS policies are enforced.
|
|
755
|
+
* with set_config calls so PostgreSQL RLS policies are enforced. The count
|
|
756
|
+
* behind `meta` runs in that same transaction — one pool acquisition and
|
|
757
|
+
* one `applyAuthContext` per refetch, where it used to take a second
|
|
758
|
+
* transaction of its own — see {@link collectionMeta}.
|
|
575
759
|
*/
|
|
576
760
|
private fetchCollectionWithAuth;
|
|
577
761
|
/**
|
|
578
|
-
* The `meta` a `collection_update` frame carries beside its rows
|
|
762
|
+
* The `meta` a `collection_update` frame carries beside its rows, counted
|
|
763
|
+
* through `service` — the subscriber's own transaction, read only, with
|
|
764
|
+
* its auth context and `beforeQuery` narrowing already bound.
|
|
579
765
|
*
|
|
580
766
|
* Frames used to carry rows and primary keys and nothing else, so the
|
|
581
767
|
* client issued a `GET /count` **per push** to fill in a total it needed to
|
|
@@ -583,20 +769,61 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
583
769
|
* write, per subscriber, forever. The refetch already knows the query; it
|
|
584
770
|
* counts once, here, under the same RLS transaction that read the rows, so
|
|
585
771
|
* the total describes the same set they came from.
|
|
772
|
+
*
|
|
773
|
+
* A first page that came back short of its limit *is* the whole set — the
|
|
774
|
+
* same filter, the same narrowing, read as the same principal — so its
|
|
775
|
+
* length is the total and nothing is counted. Not for a later page, a
|
|
776
|
+
* cursor page, a search (whose page and count are not the same query) or
|
|
777
|
+
* a `distinct` read (whose count counts rows, not values): those count.
|
|
586
778
|
*/
|
|
587
|
-
private
|
|
779
|
+
private collectionMeta;
|
|
780
|
+
/**
|
|
781
|
+
* A `single_update` frame's row, and — when the subscriber asked — the
|
|
782
|
+
* `ETag` a `GET /:id` of the same row answers.
|
|
783
|
+
*
|
|
784
|
+
* The tag names the row, not the read: `GET` hashes the row as
|
|
785
|
+
* `restFetchService.fetchOneForRest` serves it with no `include`, and a
|
|
786
|
+
* write's `If-Match` is compared against that same rendering. A frame
|
|
787
|
+
* whose relations are loaded therefore takes its tag from a second read
|
|
788
|
+
* without them, read first — as `GET /:id?include=` does — so a write
|
|
789
|
+
* landing between the two leaves the tag older than the body (a 412 the
|
|
790
|
+
* client recovers from) and never newer, which would let an edit made
|
|
791
|
+
* against the older body pass over a change it never saw.
|
|
792
|
+
*/
|
|
793
|
+
private fetchEntityFrame;
|
|
588
794
|
/**
|
|
589
795
|
* Fetch a single row with optional RLS auth context.
|
|
590
796
|
*/
|
|
591
797
|
private fetchEntityWithAuth;
|
|
592
|
-
private sendCollectionUpdate;
|
|
593
798
|
/**
|
|
594
799
|
* Everything in a `collection_update` frame after its subscription id,
|
|
595
|
-
* serialised once for every member of a group: the
|
|
596
|
-
* meta
|
|
800
|
+
* serialised once for every member of a group: the rows, the collection's
|
|
801
|
+
* key columns, and the `meta` beside them (see {@link CollectionUpdateMeta}
|
|
802
|
+
* for the round trip that removes). The order is the one
|
|
803
|
+
* {@link CollectionUpdateMessage} declares.
|
|
597
804
|
*/
|
|
598
805
|
private collectionFrameTail;
|
|
599
|
-
|
|
806
|
+
/** Everything in a `single_update` frame after its subscription id. */
|
|
807
|
+
private singleFrameTail;
|
|
808
|
+
/** A frame tail with its {@link frameDigest}, computed once for every member it goes to. */
|
|
809
|
+
private dataFrame;
|
|
810
|
+
/**
|
|
811
|
+
* What a subscriber was last sent, in 44 bytes: holding the frames
|
|
812
|
+
* themselves would keep a page of rows per subscriber in memory.
|
|
813
|
+
*/
|
|
814
|
+
private frameDigest;
|
|
815
|
+
/**
|
|
816
|
+
* Send a data frame to a socket subscriber — unless it is exactly the one
|
|
817
|
+
* the subscriber was last sent.
|
|
818
|
+
*
|
|
819
|
+
* A write anywhere in a collection refetches every list of it, and the
|
|
820
|
+
* first 50 rows of a 10k-row list come back the same for 99.5% of writes.
|
|
821
|
+
* Each of those frames was sent anyway: ~30 KB per viewer per write,
|
|
822
|
+
* parsed, merged and re-rendered by every client to change nothing. The
|
|
823
|
+
* comparison is per subscriber, because the members of one group need not
|
|
824
|
+
* have been sent the same frame last — one may have joined since.
|
|
825
|
+
*/
|
|
826
|
+
private sendDataFrame;
|
|
600
827
|
/**
|
|
601
828
|
* Send a lightweight row-level patch to a collection subscriber.
|
|
602
829
|
* The client can merge this into its cached data for instant feedback.
|
|
@@ -709,6 +936,21 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
709
936
|
* fraction of the room. The evidence is already in the process, so use it.
|
|
710
937
|
*/
|
|
711
938
|
private warnIfMemoryBusOnMultiplePods;
|
|
939
|
+
/**
|
|
940
|
+
* Ask the database whether another process holds a realtime LISTEN
|
|
941
|
+
* connection on it, at most once per {@link OTHER_INSTANCES_CHECK_INTERVAL_MS}.
|
|
942
|
+
*
|
|
943
|
+
* The legacy entity channel proves a second instance with every foreign
|
|
944
|
+
* `sid`, but under change capture — the default — nothing crosses that
|
|
945
|
+
* channel, so the warning above could never fire on exactly the
|
|
946
|
+
* deployments it was written for. Every LISTEN connection names itself
|
|
947
|
+
* (`application_name`, which `pg_stat_activity` shows to any role), so
|
|
948
|
+
* the count is one cheap query. Only a process that listens is asked: a
|
|
949
|
+
* single process with no LISTEN connection has nothing to compare with.
|
|
950
|
+
*/
|
|
951
|
+
private checkForOtherInstances;
|
|
952
|
+
/** Drop a channel its last local member left, and what was kept per channel for it. */
|
|
953
|
+
private forgetChannel;
|
|
712
954
|
/** Leave a broadcast channel */
|
|
713
955
|
leaveChannel(clientId: string, channel: string): void;
|
|
714
956
|
/**
|
|
@@ -926,6 +1168,19 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
926
1168
|
* therefore only ever receives rows its RLS policies permit — filtering is per
|
|
927
1169
|
* subscriber, never per publisher.
|
|
928
1170
|
*/
|
|
1171
|
+
/**
|
|
1172
|
+
* Route the rows one notification names — a statement's batch, or one row.
|
|
1173
|
+
*
|
|
1174
|
+
* Each is handled as a change on its own (the app-emit de-duplication and
|
|
1175
|
+
* single-row addressing are per row), at most {@link CDC_ROWS_PER_TURN} in
|
|
1176
|
+
* one turn of the event loop; the rest wait in order for the next turns. A
|
|
1177
|
+
* socket read hands the listener up to 2 MB of notifications at once, and
|
|
1178
|
+
* batched that is ~250k rows: handled in one go, the event loop stalled
|
|
1179
|
+
* for ~400 ms. Sliced, a 1M-row statement is ~200 short turns that timers,
|
|
1180
|
+
* sockets and requests run between.
|
|
1181
|
+
*/
|
|
1182
|
+
private handleCdcEvents;
|
|
1183
|
+
private drainCdcBacklog;
|
|
929
1184
|
private handleCdcEvent;
|
|
930
1185
|
/**
|
|
931
1186
|
* Deliver a change on a many-to-many junction table as a change to the child
|
|
@@ -944,7 +1199,11 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
944
1199
|
* Returns whether the table was recognised as a junction.
|
|
945
1200
|
*/
|
|
946
1201
|
private handleJunctionCdcEvent;
|
|
947
|
-
/**
|
|
1202
|
+
/**
|
|
1203
|
+
* Compute the canonical (possibly composite) id string from a captured row,
|
|
1204
|
+
* or {@link ANY_ROW} when it names none — a key too large to notify, a
|
|
1205
|
+
* table with no key.
|
|
1206
|
+
*/
|
|
948
1207
|
private extractIdFromCdcRow;
|
|
949
1208
|
/**
|
|
950
1209
|
* A captured row with its key under the key's *field* names.
|
|
@@ -957,8 +1216,19 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
957
1216
|
*/
|
|
958
1217
|
private cdcRowByKeyFields;
|
|
959
1218
|
private dedupKey;
|
|
960
|
-
/**
|
|
1219
|
+
/**
|
|
1220
|
+
* Record that this instance just delivered `key` via the app path.
|
|
1221
|
+
*
|
|
1222
|
+
* Expired entries are purged so the map cannot grow unbounded under write
|
|
1223
|
+
* load — once it holds over 1,000, and at most once per dedup window. The
|
|
1224
|
+
* purge walks the whole map, and it used to run on every write past 1,000:
|
|
1225
|
+
* inside one window nothing has expired yet, so a burst of writes walked
|
|
1226
|
+
* all of it each time (703 ms of event loop for 20,000 marks). Once a
|
|
1227
|
+
* window, the map holds at most two windows' writes.
|
|
1228
|
+
*/
|
|
961
1229
|
private markAppEmit;
|
|
1230
|
+
/** When {@link markAppEmit} may next purge expired entries. */
|
|
1231
|
+
private nextAppEmitPurgeAt;
|
|
962
1232
|
/** Consume a matching app-emit record if present and unexpired; true ⇒ suppress the CDC echo. */
|
|
963
1233
|
private consumeAppEmit;
|
|
964
1234
|
/**
|
|
@@ -979,18 +1249,48 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
|
|
|
979
1249
|
* @param connectionString Raw Postgres connection string for the LISTEN client.
|
|
980
1250
|
*/
|
|
981
1251
|
startListening(connectionString: string): Promise<void>;
|
|
1252
|
+
/**
|
|
1253
|
+
* Announce this process's writes on {@link PG_NOTIFY_CHANNEL} without
|
|
1254
|
+
* listening for anyone else's.
|
|
1255
|
+
*
|
|
1256
|
+
* For a process that serves no websockets (`worker`, `functions`) while
|
|
1257
|
+
* change capture is not in effect. It has no subscriber of its own, so it
|
|
1258
|
+
* holds no LISTEN connection — but without capture the announcement is
|
|
1259
|
+
* the only way the `api` hears that a cron or a function wrote a row.
|
|
1260
|
+
* Sending needs only the pool.
|
|
1261
|
+
*/
|
|
1262
|
+
startPublishing(): void;
|
|
982
1263
|
/**
|
|
983
1264
|
* Stop listening and clean up the dedicated LISTEN connection.
|
|
984
1265
|
*/
|
|
985
1266
|
stopListening(): Promise<void>;
|
|
986
1267
|
/**
|
|
987
|
-
*
|
|
988
|
-
*
|
|
1268
|
+
* Announce a change to the other instances on {@link PG_NOTIFY_CHANNEL},
|
|
1269
|
+
* with the rest of its burst. Resolves once the notification carrying it
|
|
1270
|
+
* has been sent. Uses the main Drizzle connection (pooled), not the LISTEN
|
|
1271
|
+
* client.
|
|
1272
|
+
*
|
|
1273
|
+
* Changes queued in the same turn of the event loop go out together, as
|
|
1274
|
+
* many to a notification as fit under the NOTIFY ceiling: a 1,000-row save
|
|
1275
|
+
* used to be 1,000 round trips. A burst of one keeps the single-change
|
|
1276
|
+
* shape `{ sid, p, eid, db }`, which an older instance in a rolling deploy
|
|
1277
|
+
* still reads; a batch is `{ sid, c: [[p, eid, db], …] }`. Both carry
|
|
1278
|
+
* addresses and nothing else.
|
|
989
1279
|
*/
|
|
990
1280
|
private broadcastChange;
|
|
991
|
-
|
|
1281
|
+
private flushBroadcasts;
|
|
1282
|
+
/** One notification another instance published on {@link PG_NOTIFY_CHANNEL}: one change, or a batch. */
|
|
992
1283
|
private handleCrossInstanceNotification;
|
|
993
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Pack `changes` into as few payloads as fit under
|
|
1287
|
+
* {@link ENTITY_NOTIFY_MAX_BYTES}. One change keeps the single-change shape.
|
|
1288
|
+
*/
|
|
1289
|
+
export declare function packEntityChanges(sid: string, changes: Array<{
|
|
1290
|
+
p: string;
|
|
1291
|
+
eid: string;
|
|
1292
|
+
db: string | null;
|
|
1293
|
+
}>): string[];
|
|
994
1294
|
/**
|
|
995
1295
|
* Alias for RealtimeService for consistent naming with other database implementations.
|
|
996
1296
|
* This allows code to use PostgresRealtimeProvider alongside future MongoRealtimeProvider, etc.
|
|
@@ -29,15 +29,6 @@ export type RelationStyle = "ref" | "inline";
|
|
|
29
29
|
* to unwrap that same level back out.
|
|
30
30
|
*/
|
|
31
31
|
export declare function isJunctionRelation(relation: ResolvedRelation): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Apply {@link toRestScalar} across a row, leaving undeclared columns alone.
|
|
34
|
-
*
|
|
35
|
-
* Exported because the include loader attaches related rows itself, and a
|
|
36
|
-
* target rendered differently from its parent is the shape bug this whole file
|
|
37
|
-
* exists to prevent — a date was a string at the top level and a
|
|
38
|
-
* `{ __type: "date" }` envelope one level down, in the same response.
|
|
39
|
-
*/
|
|
40
|
-
export declare function toRestValues(row: Record<string, unknown>, collection: CollectionConfig): Record<string, unknown>;
|
|
41
32
|
/** Render one target row in the requested style. */
|
|
42
33
|
/**
|
|
43
34
|
* Drop every column this caller may not read.
|
|
@@ -69,6 +60,22 @@ export declare function toRestValues(row: Record<string, unknown>, collection: C
|
|
|
69
60
|
* though the array itself is theirs to see.
|
|
70
61
|
*/
|
|
71
62
|
export declare function stripUnreadable(row: Record<string, unknown>, collection: CollectionConfig): Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* May the current caller be handed the rows of this relation of `collection`?
|
|
65
|
+
*
|
|
66
|
+
* A relation property's value *is* its related rows, so a property the caller
|
|
67
|
+
* may not read withholds the relation whole: an `include` neither loads it nor
|
|
68
|
+
* counts it. {@link stripUnreadable} cannot be the only guard here, because the
|
|
69
|
+
* REST rows are stripped before the include loader runs (`toRestRow`, then
|
|
70
|
+
* `loadIncludes`) — the loader wrote the rows back onto the key the strip had
|
|
71
|
+
* just deleted, and `_count` beside them.
|
|
72
|
+
*
|
|
73
|
+
* Judged by every `type: "relation"` property that names this relation: by its
|
|
74
|
+
* own key (the include key of an inline relation), or by the `relationName` it
|
|
75
|
+
* addresses in the collection's `relations` array. A relation declared only in
|
|
76
|
+
* `relations` has no property, so no field rule, and stays readable.
|
|
77
|
+
*/
|
|
78
|
+
export declare function canReadRelation(collection: CollectionConfig, key: string, relation: ResolvedRelation): boolean;
|
|
72
79
|
/**
|
|
73
80
|
* The address a relation ref points at.
|
|
74
81
|
*
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
* Two checks catch it. The socket must answer a ping: one that has not answered
|
|
12
12
|
* by the next is terminated. And it must read what it is sent: one whose unsent
|
|
13
13
|
* backlog passes {@link MAX_SOCKET_BUFFERED_BYTES} is terminated at the next
|
|
14
|
-
* frame for it
|
|
15
|
-
*
|
|
14
|
+
* frame for it, and when all of the process's sockets together hold more than
|
|
15
|
+
* {@link MAX_PROCESS_BUFFERED_BYTES}, the ones with the largest backlogs go
|
|
16
|
+
* first. A terminated socket closes like any other, so the realtime service
|
|
17
|
+
* drops its subscriptions, channels and presence on its `close`.
|
|
16
18
|
*/
|
|
17
19
|
import { WebSocket, type WebSocketServer } from "ws";
|
|
18
20
|
/**
|
|
@@ -33,6 +35,40 @@ export declare const SOCKET_PING_INTERVAL_MS = 30000;
|
|
|
33
35
|
* subscription instead of the backlog.
|
|
34
36
|
*/
|
|
35
37
|
export declare const MAX_SOCKET_BUFFERED_BYTES: number;
|
|
38
|
+
/**
|
|
39
|
+
* The unsent bytes all of this process's sockets may hold between them before
|
|
40
|
+
* the ones with the largest backlogs are terminated.
|
|
41
|
+
*
|
|
42
|
+
* The per-socket ceiling alone bounds nothing for the process: a hundred slow
|
|
43
|
+
* phones during a bulk-update storm could each sit just under it, 1.6 GB of
|
|
44
|
+
* frames between them, before any one was let go. A healthy socket's backlog
|
|
45
|
+
* is close to nothing — the kernel takes a frame as it is written — so a
|
|
46
|
+
* process holding this much unread is already in a storm. 256 MiB is sixteen
|
|
47
|
+
* sockets at their own ceiling, and an eighth of a 2 GiB container; under a
|
|
48
|
+
* smaller memory limit it is a quarter of that limit (see
|
|
49
|
+
* {@link processSendBudget}).
|
|
50
|
+
*/
|
|
51
|
+
export declare const MAX_PROCESS_BUFFERED_BYTES: number;
|
|
52
|
+
/**
|
|
53
|
+
* The process-wide budget: {@link MAX_PROCESS_BUFFERED_BYTES}, or a quarter of
|
|
54
|
+
* the memory limit the process runs under when that is smaller — never less
|
|
55
|
+
* than one socket's own ceiling. `constrained` is the limit in bytes, 0 for
|
|
56
|
+
* none, as `process.constrainedMemory()` reports it.
|
|
57
|
+
*/
|
|
58
|
+
export declare function processSendBudget(constrained?: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Count the sockets of `wss` in the process-wide send budget. Returns the
|
|
61
|
+
* function that stops counting them, for when the server closes.
|
|
62
|
+
*/
|
|
63
|
+
export declare function budgetSendBuffers(wss: WebSocketServer): () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Terminate sockets, largest backlog first, until what this process's sockets
|
|
66
|
+
* hold unread between them is back within `budget`. Returns the sockets it
|
|
67
|
+
* terminated. A terminated socket closes like any other, and its client
|
|
68
|
+
* reconnects and re-subscribes: one fresh frame per subscription, instead of
|
|
69
|
+
* the backlog.
|
|
70
|
+
*/
|
|
71
|
+
export declare function enforceProcessSendBudget(budget?: number): WebSocket[];
|
|
36
72
|
/**
|
|
37
73
|
* Ping every socket of `wss` each `intervalMs`, and terminate the ones that did
|
|
38
74
|
* not answer the previous ping. Returns the function that stops it.
|
|
@@ -41,5 +77,9 @@ export declare function reapSilentSockets(wss: WebSocketServer, intervalMs?: num
|
|
|
41
77
|
/**
|
|
42
78
|
* Terminate `ws` if its unsent backlog has passed {@link MAX_SOCKET_BUFFERED_BYTES}.
|
|
43
79
|
* Returns whether it did; the caller then sends nothing.
|
|
80
|
+
*
|
|
81
|
+
* Every frame in and out of a socket comes through here, so this is also where
|
|
82
|
+
* the process-wide budget is kept, summed at most every
|
|
83
|
+
* {@link PROCESS_BUDGET_CHECK_MS} — see {@link enforceProcessSendBudget}.
|
|
44
84
|
*/
|
|
45
85
|
export declare function terminateIfBacklogged(ws: WebSocket, clientId: string): boolean;
|