@supalive/core 1.8.1 → 1.10.0
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 +1 -2
- package/dist/index-BQRlJY_1.d.ts +2192 -0
- package/dist/index-BQRlJY_1.d.ts.map +1 -0
- package/dist/index-CDG1kbFO.d.ts +2211 -0
- package/dist/index-CDG1kbFO.d.ts.map +1 -0
- package/dist/index-CkYZi3ai.d.ts +2186 -0
- package/dist/index-CkYZi3ai.d.ts.map +1 -0
- package/dist/index-D8O39FoZ.d.ts +2192 -0
- package/dist/index-D8O39FoZ.d.ts.map +1 -0
- package/dist/index-DKlWIC3k.d.ts +2200 -0
- package/dist/index-DKlWIC3k.d.ts.map +1 -0
- package/dist/index-Dy9UlONo.d.ts +2192 -0
- package/dist/index-Dy9UlONo.d.ts.map +1 -0
- package/dist/index-WMhtVzJA.d.ts +2192 -0
- package/dist/index-WMhtVzJA.d.ts.map +1 -0
- package/dist/index-tDcHLwJu.d.ts +2186 -0
- package/dist/index-tDcHLwJu.d.ts.map +1 -0
- package/dist/mysql-BBNCFz8q.d.ts +109 -0
- package/dist/mysql-BBNCFz8q.d.ts.map +1 -0
- package/dist/mysql-BVz7201u.d.ts +109 -0
- package/dist/mysql-BVz7201u.d.ts.map +1 -0
- package/dist/mysql-CBSuONm5.d.ts +109 -0
- package/dist/mysql-CBSuONm5.d.ts.map +1 -0
- package/dist/mysql-CBlZ6FVc.d.ts +109 -0
- package/dist/mysql-CBlZ6FVc.d.ts.map +1 -0
- package/dist/mysql-CDenA4XE.d.ts +109 -0
- package/dist/mysql-CDenA4XE.d.ts.map +1 -0
- package/dist/mysql-CfYZLs5V.d.ts +109 -0
- package/dist/mysql-CfYZLs5V.d.ts.map +1 -0
- package/dist/mysql-DAfFK5v1.d.ts +109 -0
- package/dist/mysql-DAfFK5v1.d.ts.map +1 -0
- package/dist/mysql-DjjyCiO4.d.ts +109 -0
- package/dist/mysql-DjjyCiO4.d.ts.map +1 -0
- package/dist/postgres-BKGLPrKt.d.ts +113 -0
- package/dist/postgres-BKGLPrKt.d.ts.map +1 -0
- package/dist/postgres-CKBWXyY8.d.ts +113 -0
- package/dist/postgres-CKBWXyY8.d.ts.map +1 -0
- package/dist/postgres-CUHirAJN.d.ts +113 -0
- package/dist/postgres-CUHirAJN.d.ts.map +1 -0
- package/dist/postgres-Cj9zuZ8p.d.ts +113 -0
- package/dist/postgres-Cj9zuZ8p.d.ts.map +1 -0
- package/dist/postgres-DGK6Y1aL.d.ts +113 -0
- package/dist/postgres-DGK6Y1aL.d.ts.map +1 -0
- package/dist/postgres-DQxKs5Nu.d.ts +113 -0
- package/dist/postgres-DQxKs5Nu.d.ts.map +1 -0
- package/dist/postgres-hlrVkOXa.d.ts +113 -0
- package/dist/postgres-hlrVkOXa.d.ts.map +1 -0
- package/dist/postgres-ziXP2hco.d.ts +113 -0
- package/dist/postgres-ziXP2hco.d.ts.map +1 -0
- package/dist/procedure-B4qKrlBL.js +158 -0
- package/dist/procedure-B4qKrlBL.js.map +1 -0
- package/dist/procedure-BDcrK98I.js +173 -0
- package/dist/procedure-BDcrK98I.js.map +1 -0
- package/dist/procedure-BxGOWNWg.js.map +1 -0
- package/dist/procedure-CRkrxHvn.js +187 -0
- package/dist/procedure-CRkrxHvn.js.map +1 -0
- package/dist/procedure-Ck4v3__H.js +212 -0
- package/dist/procedure-Ck4v3__H.js.map +1 -0
- package/dist/procedure-DPgXC2Cm.js +221 -0
- package/dist/procedure-DPgXC2Cm.js.map +1 -0
- package/dist/procedure-xUVIUm5W.js +213 -0
- package/dist/procedure-xUVIUm5W.js.map +1 -0
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.js +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/procedure.d.ts +17 -2
- package/dist/src/exports/procedure.d.ts.map +1 -0
- package/dist/src/exports/procedure.js +8 -2
- package/dist/src/exports/procedure.js.map +1 -0
- package/dist/src/exports/schema-sql.d.ts +1 -1
- package/dist/src/exports/server.d.ts +24 -124
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +128 -30
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/sub-manager-worker-entry.d.ts +1 -0
- package/dist/src/exports/sub-manager-worker-entry.js +71 -0
- package/dist/src/exports/sub-manager-worker-entry.js.map +1 -0
- package/dist/src/exports/subscription-manager-worker-entry.js +3 -49
- package/dist/src/exports/subscription-manager-worker-entry.js.map +1 -1
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/sub-worker-dispatch-G8V8asfR.js +810 -0
- package/dist/sub-worker-dispatch-G8V8asfR.js.map +1 -0
- package/dist/subscription-worker-dispatch-CSQV-PO3.js +810 -0
- package/dist/subscription-worker-dispatch-CSQV-PO3.js.map +1 -0
- package/dist/types_server-AQ8Jl2_R.d.ts +521 -0
- package/dist/types_server-AQ8Jl2_R.d.ts.map +1 -0
- package/dist/types_server-BB2WvXEA.d.ts +521 -0
- package/dist/types_server-BB2WvXEA.d.ts.map +1 -0
- package/dist/types_server-BI7uQd6x.d.ts +328 -0
- package/dist/types_server-BI7uQd6x.d.ts.map +1 -0
- package/dist/types_server-BcT4A8OM.d.ts +328 -0
- package/dist/types_server-BcT4A8OM.d.ts.map +1 -0
- package/dist/types_server-CO3IYNS9.d.ts +328 -0
- package/dist/types_server-CO3IYNS9.d.ts.map +1 -0
- package/dist/types_server-Clf74qZP.d.ts +328 -0
- package/dist/types_server-Clf74qZP.d.ts.map +1 -0
- package/dist/types_server-DEAwXI4p.d.ts +328 -0
- package/dist/types_server-DEAwXI4p.d.ts.map +1 -0
- package/dist/types_server-DU9JMSXd.d.ts +521 -0
- package/dist/types_server-DU9JMSXd.d.ts.map +1 -0
- package/dist/zod_ext-B5cyjWqZ.d.ts +11 -0
- package/dist/zod_ext-B5cyjWqZ.d.ts.map +1 -0
- package/dist/zod_ext-CZlJ4wtJ.d.ts +11 -0
- package/dist/zod_ext-CZlJ4wtJ.d.ts.map +1 -0
- package/package.json +6 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $n as
|
|
2
|
-
import { t as MySqlDatabase } from "../../mysql-
|
|
3
|
-
import { t as PgDatabase } from "../../postgres-
|
|
4
|
-
import { b as SupaliveServerConfig, p as DatabaseConfig, u as SubId, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-
|
|
1
|
+
import { $n as AffectedSubscription, Bt as ScheduleHandle, E as Context, Ft as IncomingJobRequest, Ht as SupaliveDb, It as JobClient, Lt as JobScheduler, Mt as DevScheduler, Nt as DevSchedulerOptions, Pt as EnqueueOptions, Qn as CacheLayer, Rt as QStashScheduler, Un as Database, Vt as ScheduledJobDef, ar as RegisterSubscriptionResult, cr as UnregisterSubscriptionResult, dr as UpdateSubscriptionReadSetParams, er as InvalidateWritesetParams, fr as UpdateSubscriptionReadSetParamsSchema, ir as RegisterSubscriptionParamsSchema, nr as InvalidateWritesetResult, or as UnregisterSubscriptionParams, pr as UpdateSubscriptionReadSetResult, rr as RegisterSubscriptionParams, rt as Router, sr as UnregisterSubscriptionParamsSchema, tr as InvalidateWritesetParamsSchema, zt as QStashSchedulerOptions } from "../../index-BQRlJY_1.js";
|
|
2
|
+
import { t as MySqlDatabase } from "../../mysql-CBSuONm5.js";
|
|
3
|
+
import { t as PgDatabase } from "../../postgres-BKGLPrKt.js";
|
|
4
|
+
import { C as SubManagerLink, S as SubManagerClient, b as SupaliveServerConfig, p as DatabaseConfig, u as SubId, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-DU9JMSXd.js";
|
|
5
5
|
import pino, { Level } from "pino";
|
|
6
6
|
import Redis$1 from "ioredis";
|
|
7
7
|
|
|
@@ -190,7 +190,7 @@ declare const logger: pino.Logger<never, boolean>;
|
|
|
190
190
|
declare function logLevelOf(name: string): Level;
|
|
191
191
|
declare function serializeMapValue<K extends string | number | symbol, V>(map: Map<K, V>): Record<K, V>;
|
|
192
192
|
//#endregion
|
|
193
|
-
//#region src/server/
|
|
193
|
+
//#region src/server/sub-manager.d.ts
|
|
194
194
|
/**
|
|
195
195
|
* One process per deployment. Routes each subscription operation to one of N
|
|
196
196
|
* worker threads by hashing subId. Worker threads run in parallel on separate
|
|
@@ -199,14 +199,30 @@ declare function serializeMapValue<K extends string | number | symbol, V>(map: M
|
|
|
199
199
|
* raw payload. The worker validates and parses with Zod before executing.
|
|
200
200
|
*/
|
|
201
201
|
declare class SubscriptionManager {
|
|
202
|
-
private wss
|
|
203
|
-
private port
|
|
202
|
+
private wss?;
|
|
203
|
+
private port?;
|
|
204
|
+
private listen;
|
|
204
205
|
private workers;
|
|
205
206
|
private pruneInterval;
|
|
206
207
|
private static readonly PRUNE_INTERVAL_MS;
|
|
207
208
|
constructor(config: SubscriptionManagerConfig);
|
|
209
|
+
/**
|
|
210
|
+
* Build the single in-process worker for {@link SubscriptionManagerConfig.inline}
|
|
211
|
+
* mode. Reuses the host server's {@link SubscriptionManagerConfig.db} +
|
|
212
|
+
* {@link SubscriptionManagerConfig.cache} when provided (borrowed — not closed
|
|
213
|
+
* on stop); otherwise opens its own pool/cache from `database`/`upstash`.
|
|
214
|
+
*/
|
|
215
|
+
private buildInlineWorker;
|
|
208
216
|
start(): Promise<void>;
|
|
209
217
|
stop(): Promise<void>;
|
|
218
|
+
/**
|
|
219
|
+
* A direct, in-process {@link SubManagerLink} to this manager, for embedding
|
|
220
|
+
* it in the app server (pass as `SupaliveServerConfig.subManager`). Calls the
|
|
221
|
+
* dispatch handlers directly — no socket, no serialization. The recovery
|
|
222
|
+
* callbacks are no-ops: an in-process link never disconnects, so the app
|
|
223
|
+
* server's reconnect/buffer machinery is inert.
|
|
224
|
+
*/
|
|
225
|
+
localLink(): SubManagerLink;
|
|
210
226
|
private pruneCommitLogs;
|
|
211
227
|
/** Fan a watermark value out to every worker's in-memory copy. Worker 0 (the
|
|
212
228
|
* pruner/seeder) already has it, but re-sending is harmless — {@link
|
|
@@ -225,122 +241,6 @@ declare class SubscriptionManager {
|
|
|
225
241
|
private invalidateWriteset;
|
|
226
242
|
}
|
|
227
243
|
//#endregion
|
|
228
|
-
//#region src/server/subscription-manager-client.d.ts
|
|
229
|
-
/**
|
|
230
|
-
* Callback the app server registers to process invalidations that were
|
|
231
|
-
* deferred during a sub-manager outage. When the sub-manager reconnects and
|
|
232
|
-
* the buffered writeSets are flushed in one batched RPC, the resulting
|
|
233
|
-
* `affected[]` is handed to this callback — which is expected to do the
|
|
234
|
-
* same work as the normal per-mutation `reexecuteAffectedQuery` path.
|
|
235
|
-
*/
|
|
236
|
-
type AffectedHandler = (affected: AffectedSubscription[]) => Promise<void>;
|
|
237
|
-
/**
|
|
238
|
-
* Callback the app server registers to drive recovery on sub-manager
|
|
239
|
-
* reconnect. Iterates `serverSubscriptions`, reads `sl:reg:<subId>` from
|
|
240
|
-
* Redis, and re-registers each known sub. Resolves when complete; only then
|
|
241
|
-
* does the client's buffer flush and gated calls release.
|
|
242
|
-
*/
|
|
243
|
-
type RecoveryDriver = () => Promise<void>;
|
|
244
|
-
interface SubManagerClientOptions {
|
|
245
|
-
url: string;
|
|
246
|
-
/**
|
|
247
|
-
* Maximum number of buffered WriteEntry records held during an outage.
|
|
248
|
-
* If exceeded, the buffer is dropped — the commit-log replay path during
|
|
249
|
-
* the next register call will catch up any missed invalidations, at the
|
|
250
|
-
* cost of a brief recompute spike. Default 50_000 entries.
|
|
251
|
-
*/
|
|
252
|
-
invalidateBufferLimit?: number;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* RPC client for the (single) sub-manager process.
|
|
256
|
-
*
|
|
257
|
-
* Design (Convex-inspired):
|
|
258
|
-
* • The sub-manager is in-memory and ephemeral. There is no version
|
|
259
|
-
* protocol, no client-side registration registry, no OUT_OF_SYNC
|
|
260
|
-
* replay logic. All recovery is driven from the app server using
|
|
261
|
-
* state it already has (`serverSubscriptions`) plus registration
|
|
262
|
-
* records persisted to Redis at `sl:reg:<subId>`.
|
|
263
|
-
*
|
|
264
|
-
* • Out-of-order `updateSubscriptionReadSet` calls are filtered server
|
|
265
|
-
* side using `lastSnapshotTs` (monotonic DB commit timestamp).
|
|
266
|
-
*
|
|
267
|
-
* • On rpc-websockets reconnect, the client:
|
|
268
|
-
* 1. Transitions to `busy` (any in-flight `invalidateWriteset`
|
|
269
|
-
* buffers its writeSet).
|
|
270
|
-
* 2. Invokes the app server's `recoveryDriver` to re-register all
|
|
271
|
-
* known subs.
|
|
272
|
-
* 3. Flushes the buffer as one batched `invalidateWriteset` RPC and
|
|
273
|
-
* hands the resulting `affected[]` to `affectedHandler`.
|
|
274
|
-
* 4. Transitions to `ready`.
|
|
275
|
-
*
|
|
276
|
-
* • During an extended outage, the buffer is bounded; if exceeded, it
|
|
277
|
-
* is dropped and we rely on the commit-log replay built into
|
|
278
|
-
* `SubscriptionWorker.register()` to catch each sub up on the next
|
|
279
|
-
* re-register.
|
|
280
|
-
*/
|
|
281
|
-
declare class SubManagerClient {
|
|
282
|
-
private client;
|
|
283
|
-
private state;
|
|
284
|
-
private hasConnectedOnce;
|
|
285
|
-
/** Resolves whenever state transitions to 'ready'. Replaced on each busy→ready cycle. */
|
|
286
|
-
private readyPromise;
|
|
287
|
-
private resolveReady;
|
|
288
|
-
/** Writeset entries accumulated while state === 'busy'. Flushed as one RPC after recovery. */
|
|
289
|
-
private bufferedWriteSets;
|
|
290
|
-
private readonly bufferLimit;
|
|
291
|
-
private bufferDroppedDuringOutage;
|
|
292
|
-
private recoveryDriver?;
|
|
293
|
-
private affectedHandler?;
|
|
294
|
-
constructor(urlOrOptions: string | SubManagerClientOptions);
|
|
295
|
-
/**
|
|
296
|
-
* Wire up the app-server-driven recovery. Must be called before any
|
|
297
|
-
* disconnect/reconnect cycle for buffered writeSets to be flushed and
|
|
298
|
-
* subscriptions re-registered.
|
|
299
|
-
*/
|
|
300
|
-
setRecoveryDriver(driver: RecoveryDriver): void;
|
|
301
|
-
/**
|
|
302
|
-
* Wire up the handler invoked when batched invalidate results land
|
|
303
|
-
* after recovery. The handler is expected to drive
|
|
304
|
-
* `reexecuteAffectedQuery` for each entry.
|
|
305
|
-
*/
|
|
306
|
-
setAffectedHandler(handler: AffectedHandler): void;
|
|
307
|
-
registerSubscription(params: RegisterSubscriptionParams): Promise<RegisterSubscriptionResult>;
|
|
308
|
-
registerSubscriptionBatch(params: RegisterSubscriptionParams[]): Promise<RegisterSubscriptionResult[]>;
|
|
309
|
-
updateSubscriptionReadSet(params: UpdateSubscriptionReadSetParams): Promise<UpdateSubscriptionReadSetResult>;
|
|
310
|
-
unregisterSubscription(params: UnregisterSubscriptionParams): Promise<UnregisterSubscriptionResult>;
|
|
311
|
-
unregisterSubscriptions(params: UnregisterSubscriptionsParams): Promise<UnregisterSubscriptionsResult>;
|
|
312
|
-
/**
|
|
313
|
-
* Sends the writeSet to the sub-manager for invalidation, OR buffers it
|
|
314
|
-
* if the client is currently disconnected / recovering. Returns
|
|
315
|
-
* `{ affected: [] }` while buffering — the actual `affected` from the
|
|
316
|
-
* buffered batch is delivered via the registered `affectedHandler`
|
|
317
|
-
* after recovery completes.
|
|
318
|
-
*/
|
|
319
|
-
invalidateWriteset(params: InvalidateWritesetParams): Promise<InvalidateWritesetResult>;
|
|
320
|
-
/**
|
|
321
|
-
* Single point through which RPCs go. Throws on transport / server error.
|
|
322
|
-
* `registerSubscription`, `updateSubscriptionReadSet`, and
|
|
323
|
-
* `unregisterSubscription` are NOT gated on the ready state: during a
|
|
324
|
-
* disconnect they will fail at the socket layer, and during recovery
|
|
325
|
-
* (which itself uses these methods) gating would deadlock.
|
|
326
|
-
*/
|
|
327
|
-
private call;
|
|
328
|
-
private appendToBuffer;
|
|
329
|
-
private flushBuffer;
|
|
330
|
-
private markBusy;
|
|
331
|
-
private markReady;
|
|
332
|
-
private handleClose;
|
|
333
|
-
private handleOpen;
|
|
334
|
-
private runRecovery;
|
|
335
|
-
/**
|
|
336
|
-
* Test/inspection helpers.
|
|
337
|
-
*/
|
|
338
|
-
/** @internal */
|
|
339
|
-
isReady(): boolean;
|
|
340
|
-
/** @internal */
|
|
341
|
-
bufferSize(): number;
|
|
342
|
-
}
|
|
343
|
-
//#endregion
|
|
344
244
|
//#region src/db/init_db.d.ts
|
|
345
245
|
/**
|
|
346
246
|
* Current core runtime-bootstrap version. Bump this whenever core needs a new
|
|
@@ -426,5 +326,5 @@ declare function generateSubscriptionId(procedure: string, input: unknown | stri
|
|
|
426
326
|
*/
|
|
427
327
|
declare function generateCacheKey(queryName: string, input: unknown | string, queryIdentity: string): Promise<string>;
|
|
428
328
|
//#endregion
|
|
429
|
-
export { ANONYMOUS_IDENTITY, type AffectedSubscription, CORE_VERSION, CacheLayer, DB_QUERY_TIMEOUT_MS, DevScheduler, type DevSchedulerOptions, type EnqueueOptions, type IncomingJobRequest, InitCacheLayerOptions, InitDbOptions, type InvalidateWritesetParams, InvalidateWritesetParamsSchema, type InvalidateWritesetResult, JobClient, type JobScheduler, QStashScheduler, type QStashSchedulerOptions, type RegisterSubscriptionParams, RegisterSubscriptionParamsSchema, type RegisterSubscriptionResult, type ScheduleHandle, type ScheduledJobDef, type SubId, SubManagerClient, SubscriptionManager, SupaliveWebSocketServer, type UnregisterSubscriptionParams, UnregisterSubscriptionParamsSchema, type UnregisterSubscriptionResult, type UpdateSubscriptionReadSetParams, UpdateSubscriptionReadSetParamsSchema, type UpdateSubscriptionReadSetResult, appInstanceId, bootstrapDbTypes, createDatabase, generateCacheKey, generateSubscriptionId, getHashOf, initCacheLayer, initCore, initDatabase, isProduction, logLevelOf, logger, serializeMapValue };
|
|
329
|
+
export { ANONYMOUS_IDENTITY, type AffectedSubscription, CORE_VERSION, CacheLayer, DB_QUERY_TIMEOUT_MS, DevScheduler, type DevSchedulerOptions, type EnqueueOptions, type IncomingJobRequest, InitCacheLayerOptions, InitDbOptions, type InvalidateWritesetParams, InvalidateWritesetParamsSchema, type InvalidateWritesetResult, JobClient, type JobScheduler, QStashScheduler, type QStashSchedulerOptions, type RegisterSubscriptionParams, RegisterSubscriptionParamsSchema, type RegisterSubscriptionResult, type ScheduleHandle, type ScheduledJobDef, type SubId, SubManagerClient, type SubManagerLink, SubscriptionManager, SupaliveWebSocketServer, type UnregisterSubscriptionParams, UnregisterSubscriptionParamsSchema, type UnregisterSubscriptionResult, type UpdateSubscriptionReadSetParams, UpdateSubscriptionReadSetParamsSchema, type UpdateSubscriptionReadSetResult, appInstanceId, bootstrapDbTypes, createDatabase, generateCacheKey, generateSubscriptionId, getHashOf, initCacheLayer, initCore, initDatabase, isProduction, logLevelOf, logger, serializeMapValue };
|
|
430
330
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","names":[],"sources":["../../../src/server/supalive-server.ts","../../../src/logger.ts","../../../src/server/
|
|
1
|
+
{"version":3,"file":"server.d.ts","names":[],"sources":["../../../src/server/supalive-server.ts","../../../src/logger.ts","../../../src/server/sub-manager.ts","../../../src/db/init_db.ts","../../../src/server/sub_hash.ts"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { r as evaluateCacheFreshness } from "../../overlap-checker-BqR9qCfy.js";
|
|
|
4
4
|
import { i as AuthenticationError, o as ClientMessageSchema } from "../../types_client_rpc-CIsjQQGG.js";
|
|
5
5
|
import { n as RemoteSubscriptionMessageSchema } from "../../types_server-Dk6o_B7N.js";
|
|
6
6
|
import { n as sleep } from "../../realtime_db-DiYQbfeA.js";
|
|
7
|
-
import { C as
|
|
7
|
+
import { C as appInstanceId, D as serializeMapValue, E as logger, S as RetentionWatermark, T as logLevelOf, _ as UnregisterSubscriptionResultSchema, a as bootstrapDbTypes, b as UpdateSubscriptionReadSetResultSchema, c as initCore, d as InvalidateWritesetParamsSchema, f as InvalidateWritesetResultSchema, g as UnregisterSubscriptionParamsSchema, h as RegisterSubscriptionResultSchema, i as DB_QUERY_TIMEOUT_MS, l as initDatabase, m as RegisterSubscriptionParamsSchema, n as SubscriptionWorker, o as createDatabase, p as RegisterSubscriptionBatchResultSchema, r as CORE_VERSION, s as initCacheLayer, t as dispatchWorkerMessage, u as CacheLayer, v as UnregisterSubscriptionsResultSchema, w as isProduction, x as RETENTION_WATERMARK_CHANNEL, y as UpdateSubscriptionReadSetParamsSchema } from "../../sub-worker-dispatch-G8V8asfR.js";
|
|
8
8
|
import { createServer } from "http";
|
|
9
9
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10
10
|
import { v4 } from "uuid";
|
|
@@ -118,7 +118,7 @@ async function runCachedQuery(deps, procedure, procedureName, input, serverCtx,
|
|
|
118
118
|
return result.data;
|
|
119
119
|
}
|
|
120
120
|
//#endregion
|
|
121
|
-
//#region src/server/
|
|
121
|
+
//#region src/server/sub-manager-client.ts
|
|
122
122
|
const logger$5 = logger.child({ name: "SUBS_MGR_CLIENT" }, { level: logLevelOf("SUB_MANAGER_CLIENT_LOG_LEVEL") });
|
|
123
123
|
const subscriptionManagerDataPack = {
|
|
124
124
|
encode: (data) => supaliveStringify(data),
|
|
@@ -828,8 +828,9 @@ var SupaliveWebSocketServer = class {
|
|
|
828
828
|
...config
|
|
829
829
|
};
|
|
830
830
|
this.instanceId = appInstanceId;
|
|
831
|
-
if (
|
|
832
|
-
this.subManagerClient = new SubManagerClient(config.subManagerUrl);
|
|
831
|
+
if (config.subManager) this.subManagerClient = config.subManager;
|
|
832
|
+
else if (config.subManagerUrl) this.subManagerClient = new SubManagerClient(config.subManagerUrl);
|
|
833
|
+
else throw new Error("either `subManager` (in-process link) or `subManagerUrl` (WebSocket URL) is required");
|
|
833
834
|
this.cache = config.cacheLayer;
|
|
834
835
|
this.redisSub = config.redisSubClient;
|
|
835
836
|
this.appPubSubChannels = new Set(config.appPubSub?.channels ?? []);
|
|
@@ -1812,12 +1813,12 @@ function fnv1a32(s) {
|
|
|
1812
1813
|
return h >>> 0;
|
|
1813
1814
|
}
|
|
1814
1815
|
//#endregion
|
|
1815
|
-
//#region src/server/
|
|
1816
|
+
//#region src/server/sub-manager.ts
|
|
1816
1817
|
const logger$1 = logger.child({ name: "SUBS_MGR" }, { level: logLevelOf("SUBS_MANAGER_SERVER_LOG_LEVEL") });
|
|
1817
1818
|
function resolveWorkerEntry() {
|
|
1818
1819
|
const isTs = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
1819
1820
|
return {
|
|
1820
|
-
url: new URL(`./
|
|
1821
|
+
url: new URL(`./sub-manager-worker-entry${isTs ? ".ts" : ".js"}`, import.meta.url),
|
|
1821
1822
|
isTs
|
|
1822
1823
|
};
|
|
1823
1824
|
}
|
|
@@ -1826,7 +1827,7 @@ function workerExecArgv(isTs) {
|
|
|
1826
1827
|
if (typeof globalThis.Bun !== "undefined") return void 0;
|
|
1827
1828
|
return ["--import", "tsx"];
|
|
1828
1829
|
}
|
|
1829
|
-
var
|
|
1830
|
+
var ThreadWorkerHandle = class {
|
|
1830
1831
|
id;
|
|
1831
1832
|
worker;
|
|
1832
1833
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -1881,6 +1882,38 @@ var SubscriptionWorkerHandle = class {
|
|
|
1881
1882
|
}
|
|
1882
1883
|
};
|
|
1883
1884
|
/**
|
|
1885
|
+
* In-process worker handle: holds a live {@link SubscriptionWorker} and drives
|
|
1886
|
+
* it via {@link dispatchWorkerMessage} with `trusted: true`. Every `send` is a
|
|
1887
|
+
* direct `await worker.method(payload)` on the main event loop — no
|
|
1888
|
+
* `postMessage`, no structured-clone. Used when the manager is embedded in the
|
|
1889
|
+
* app server. When `ownsDb` is false the db is borrowed from the host server
|
|
1890
|
+
* and is NOT closed on shutdown.
|
|
1891
|
+
*/
|
|
1892
|
+
var InlineWorkerHandle = class {
|
|
1893
|
+
id;
|
|
1894
|
+
worker;
|
|
1895
|
+
db;
|
|
1896
|
+
ownsDb;
|
|
1897
|
+
constructor(id, worker, db, ownsDb) {
|
|
1898
|
+
this.id = id;
|
|
1899
|
+
this.worker = worker;
|
|
1900
|
+
this.db = db;
|
|
1901
|
+
this.ownsDb = ownsDb;
|
|
1902
|
+
}
|
|
1903
|
+
ready() {
|
|
1904
|
+
return Promise.resolve();
|
|
1905
|
+
}
|
|
1906
|
+
send(type, payload) {
|
|
1907
|
+
return dispatchWorkerMessage(this.worker, this.db, type, payload, { trusted: true });
|
|
1908
|
+
}
|
|
1909
|
+
async shutdown() {
|
|
1910
|
+
if (!this.ownsDb) return;
|
|
1911
|
+
try {
|
|
1912
|
+
await this.db.close();
|
|
1913
|
+
} catch (_) {}
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
/**
|
|
1884
1917
|
* One process per deployment. Routes each subscription operation to one of N
|
|
1885
1918
|
* worker threads by hashing subId. Worker threads run in parallel on separate
|
|
1886
1919
|
* CPU cores; this dispatcher does no Zod parsing — it reads `subId` directly
|
|
@@ -1890,28 +1923,72 @@ var SubscriptionWorkerHandle = class {
|
|
|
1890
1923
|
var SubscriptionManager = class SubscriptionManager {
|
|
1891
1924
|
wss;
|
|
1892
1925
|
port;
|
|
1926
|
+
listen;
|
|
1893
1927
|
workers;
|
|
1894
1928
|
pruneInterval = null;
|
|
1895
1929
|
static PRUNE_INTERVAL_MS = 10080 * 60 * 1e3;
|
|
1896
1930
|
constructor(config) {
|
|
1897
|
-
this.
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
this.
|
|
1931
|
+
this.listen = config.listen !== false;
|
|
1932
|
+
if (this.listen) {
|
|
1933
|
+
if (config.port == null) throw new Error("SubscriptionManager: `port` is required unless `listen: false`");
|
|
1934
|
+
this.port = config.port;
|
|
1935
|
+
this.wss = new Server({ port: this.port }, subscriptionManagerDataPack);
|
|
1936
|
+
this.wss.dataPack = subscriptionManagerDataPack;
|
|
1937
|
+
}
|
|
1938
|
+
const inline = config.inline === true;
|
|
1939
|
+
let numWorkers = resolveWorkerCount(config.workers);
|
|
1940
|
+
if (inline && numWorkers !== 1) {
|
|
1941
|
+
logger$1.warn(`inline mode runs on the main event loop; forcing workers=1 (was ${numWorkers})`);
|
|
1942
|
+
numWorkers = 1;
|
|
1943
|
+
}
|
|
1944
|
+
if (inline) this.workers = [this.buildInlineWorker(config)];
|
|
1945
|
+
else {
|
|
1946
|
+
if (!config.database || !config.upstash) throw new Error("SubscriptionManager: `database` and `upstash` are required in threaded (non-inline) mode");
|
|
1947
|
+
const maxConnectionsPerWorker = resolveMaxConnectionsPerWorker(config, numWorkers);
|
|
1948
|
+
logger$1.info(`Spawning ${numWorkers} subscription worker${numWorkers === 1 ? "" : "s"}`);
|
|
1949
|
+
this.workers = Array.from({ length: numWorkers }, (_, i) => new ThreadWorkerHandle(i + 1, {
|
|
1950
|
+
workerId: i + 1,
|
|
1951
|
+
database: config.database,
|
|
1952
|
+
upstash: config.upstash,
|
|
1953
|
+
cacheTTLSeconds: config.cacheTTLSeconds,
|
|
1954
|
+
maxConnectionsPerWorker
|
|
1955
|
+
}));
|
|
1956
|
+
}
|
|
1957
|
+
if (this.listen) this.registerMethods();
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Build the single in-process worker for {@link SubscriptionManagerConfig.inline}
|
|
1961
|
+
* mode. Reuses the host server's {@link SubscriptionManagerConfig.db} +
|
|
1962
|
+
* {@link SubscriptionManagerConfig.cache} when provided (borrowed — not closed
|
|
1963
|
+
* on stop); otherwise opens its own pool/cache from `database`/`upstash`.
|
|
1964
|
+
*/
|
|
1965
|
+
buildInlineWorker(config) {
|
|
1966
|
+
let db;
|
|
1967
|
+
let cache;
|
|
1968
|
+
let ownsDb;
|
|
1969
|
+
if (config.db && config.cache) {
|
|
1970
|
+
db = config.db;
|
|
1971
|
+
cache = config.cache;
|
|
1972
|
+
ownsDb = false;
|
|
1973
|
+
logger$1.info("Inline subscription worker: reusing host db + cache");
|
|
1974
|
+
} else {
|
|
1975
|
+
if (!config.database || !config.upstash) throw new Error("SubscriptionManager inline mode needs either { db, cache } to reuse, or { database, upstash } to build its own");
|
|
1976
|
+
const { cacheLayer } = initCacheLayer({
|
|
1977
|
+
upstash: config.upstash,
|
|
1978
|
+
cacheTtlSeconds: config.cacheTTLSeconds,
|
|
1979
|
+
commandMaxRetries: 5,
|
|
1980
|
+
createSubClient: false
|
|
1981
|
+
});
|
|
1982
|
+
cache = cacheLayer;
|
|
1983
|
+
db = createDatabase(config.database, 1);
|
|
1984
|
+
ownsDb = true;
|
|
1985
|
+
logger$1.info("Inline subscription worker: built own db + cache");
|
|
1986
|
+
}
|
|
1987
|
+
return new InlineWorkerHandle(1, new SubscriptionWorker(1, cache, db), db, ownsDb);
|
|
1911
1988
|
}
|
|
1912
1989
|
async start() {
|
|
1913
1990
|
await Promise.all(this.workers.map((w) => w.ready()));
|
|
1914
|
-
logger$1.info(`Listening on port ${this.port}`);
|
|
1991
|
+
logger$1.info(this.listen ? `Listening on port ${this.port}` : "Started (embedded, in-process)");
|
|
1915
1992
|
try {
|
|
1916
1993
|
const minRetainedTs = await this.workers[0].send(12, null);
|
|
1917
1994
|
await this.broadcastRetentionWatermark(minRetainedTs);
|
|
@@ -1928,10 +2005,29 @@ var SubscriptionManager = class SubscriptionManager {
|
|
|
1928
2005
|
clearInterval(this.pruneInterval);
|
|
1929
2006
|
this.pruneInterval = null;
|
|
1930
2007
|
}
|
|
1931
|
-
this.wss
|
|
2008
|
+
this.wss?.close();
|
|
1932
2009
|
await Promise.all(this.workers.map((w) => w.shutdown()));
|
|
1933
2010
|
logger$1.info("Stopped");
|
|
1934
2011
|
}
|
|
2012
|
+
/**
|
|
2013
|
+
* A direct, in-process {@link SubManagerLink} to this manager, for embedding
|
|
2014
|
+
* it in the app server (pass as `SupaliveServerConfig.subManager`). Calls the
|
|
2015
|
+
* dispatch handlers directly — no socket, no serialization. The recovery
|
|
2016
|
+
* callbacks are no-ops: an in-process link never disconnects, so the app
|
|
2017
|
+
* server's reconnect/buffer machinery is inert.
|
|
2018
|
+
*/
|
|
2019
|
+
localLink() {
|
|
2020
|
+
return {
|
|
2021
|
+
registerSubscription: (p) => this.registerSubscription(p),
|
|
2022
|
+
registerSubscriptionBatch: (p) => this.registerSubscriptionBatch(p),
|
|
2023
|
+
updateSubscriptionReadSet: (p) => this.updateSubscriptionReadSet(p),
|
|
2024
|
+
unregisterSubscription: (p) => this.unregisterSubscription(p),
|
|
2025
|
+
unregisterSubscriptions: (p) => this.unregisterSubscriptions(p),
|
|
2026
|
+
invalidateWriteset: (p) => this.invalidateWriteset(p),
|
|
2027
|
+
setRecoveryDriver: () => {},
|
|
2028
|
+
setAffectedHandler: () => {}
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
1935
2031
|
async pruneCommitLogs() {
|
|
1936
2032
|
const minPerWorker = await Promise.all(this.workers.map((w) => w.send(8, null)));
|
|
1937
2033
|
let minRetainedTs = null;
|
|
@@ -1964,12 +2060,14 @@ var SubscriptionManager = class SubscriptionManager {
|
|
|
1964
2060
|
return this.workers[this.workerIndexFor(subId)];
|
|
1965
2061
|
}
|
|
1966
2062
|
registerMethods() {
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2063
|
+
const wss = this.wss;
|
|
2064
|
+
if (!wss) return;
|
|
2065
|
+
wss.register("registerSubscription", this.registerSubscription.bind(this));
|
|
2066
|
+
wss.register("registerSubscriptionBatch", this.registerSubscriptionBatch.bind(this));
|
|
2067
|
+
wss.register("updateSubscriptionReadSet", this.updateSubscriptionReadSet.bind(this));
|
|
2068
|
+
wss.register("invalidateWriteset", this.invalidateWriteset.bind(this));
|
|
2069
|
+
wss.register("unregisterSubscription", this.unregisterSubscription.bind(this));
|
|
2070
|
+
wss.register("unregisterSubscriptions", this.unregisterSubscriptions.bind(this));
|
|
1973
2071
|
}
|
|
1974
2072
|
readSubId(params) {
|
|
1975
2073
|
return params.subId;
|
|
@@ -2037,7 +2135,7 @@ function resolveWorkerCount(configured) {
|
|
|
2037
2135
|
}
|
|
2038
2136
|
function resolveMaxConnectionsPerWorker(config, workers) {
|
|
2039
2137
|
if (config.maxConnectionsPerWorker && config.maxConnectionsPerWorker > 0) return Math.floor(config.maxConnectionsPerWorker);
|
|
2040
|
-
const total = config.database
|
|
2138
|
+
const total = config.database?.maxConnections ?? (config.database?.type === "mysql" ? 20 : 10);
|
|
2041
2139
|
return Math.max(1, Math.floor(total / workers));
|
|
2042
2140
|
}
|
|
2043
2141
|
//#endregion
|