@supalive/core 1.18.0 → 1.20.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/dist/index-BuWa503L.d.ts +2445 -0
- package/dist/index-BuWa503L.d.ts.map +1 -0
- package/dist/index-CBIjmPUS.d.ts +2444 -0
- package/dist/index-CBIjmPUS.d.ts.map +1 -0
- package/dist/index-CKo6HvJc.d.ts +2455 -0
- package/dist/index-CKo6HvJc.d.ts.map +1 -0
- package/dist/index-DShKjpzF.d.ts +2453 -0
- package/dist/index-DShKjpzF.d.ts.map +1 -0
- package/dist/index-Dcxf_Gm-.d.ts +2454 -0
- package/dist/index-Dcxf_Gm-.d.ts.map +1 -0
- package/dist/index-DsBAE32T.d.ts +2459 -0
- package/dist/index-DsBAE32T.d.ts.map +1 -0
- package/dist/logger-DrXccWFZ.js +346 -0
- package/dist/logger-DrXccWFZ.js.map +1 -0
- package/dist/mysql-B2u0Ye4x.d.ts +114 -0
- package/dist/mysql-B2u0Ye4x.d.ts.map +1 -0
- package/dist/mysql-BHW-tAqt.d.ts +114 -0
- package/dist/mysql-BHW-tAqt.d.ts.map +1 -0
- package/dist/mysql-C79_qNQO.d.ts +114 -0
- package/dist/mysql-C79_qNQO.d.ts.map +1 -0
- package/dist/mysql-C_TTCZ8j.d.ts +114 -0
- package/dist/mysql-C_TTCZ8j.d.ts.map +1 -0
- package/dist/mysql-CyzJqrvO.d.ts +114 -0
- package/dist/mysql-CyzJqrvO.d.ts.map +1 -0
- package/dist/mysql-ygH2EMGn.js +622 -0
- package/dist/mysql-ygH2EMGn.js.map +1 -0
- package/dist/mysql-z9Jefhd6.d.ts +114 -0
- package/dist/mysql-z9Jefhd6.d.ts.map +1 -0
- package/dist/object-storage-DTVmZq3l.d.ts +110 -0
- package/dist/object-storage-DTVmZq3l.d.ts.map +1 -0
- package/dist/one-shot-query-9mCboWaB.js +565 -0
- package/dist/one-shot-query-9mCboWaB.js.map +1 -0
- package/dist/one-shot-query-B9SjfcCD.js +550 -0
- package/dist/one-shot-query-B9SjfcCD.js.map +1 -0
- package/dist/one-shot-query-BBiv9LGP.js +565 -0
- package/dist/one-shot-query-BBiv9LGP.js.map +1 -0
- package/dist/one-shot-query-Bxz_N-NE.js +563 -0
- package/dist/one-shot-query-Bxz_N-NE.js.map +1 -0
- package/dist/one-shot-query-DE9oQU40.js +550 -0
- package/dist/one-shot-query-DE9oQU40.js.map +1 -0
- package/dist/one-shot-query-adoWqGLs.js +565 -0
- package/dist/one-shot-query-adoWqGLs.js.map +1 -0
- package/dist/overlap-checker-CCgq_Tpa.js +284 -0
- package/dist/overlap-checker-CCgq_Tpa.js.map +1 -0
- package/dist/postgres-B2FLN1Kw.d.ts +118 -0
- package/dist/postgres-B2FLN1Kw.d.ts.map +1 -0
- package/dist/postgres-C7xHWbij.d.ts +118 -0
- package/dist/postgres-C7xHWbij.d.ts.map +1 -0
- package/dist/postgres-DBMAv_dq.d.ts +118 -0
- package/dist/postgres-DBMAv_dq.d.ts.map +1 -0
- package/dist/postgres-DBuP9ON_.d.ts +118 -0
- package/dist/postgres-DBuP9ON_.d.ts.map +1 -0
- package/dist/postgres-DwiUT_A6.js +867 -0
- package/dist/postgres-DwiUT_A6.js.map +1 -0
- package/dist/postgres-I4sgdBnG.d.ts +118 -0
- package/dist/postgres-I4sgdBnG.d.ts.map +1 -0
- package/dist/postgres-SM8muZqN.d.ts +118 -0
- package/dist/postgres-SM8muZqN.d.ts.map +1 -0
- package/dist/router-DP2ThAwh.js.map +1 -1
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.js +47 -50
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +1 -1
- package/dist/src/exports/schema-sql.d.ts +1 -1
- package/dist/src/exports/server.d.ts +11 -4
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +31 -410
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/storage.d.ts +4 -2
- package/dist/src/exports/storage.d.ts.map +1 -1
- package/dist/src/exports/storage.js +23 -1
- package/dist/src/exports/storage.js.map +1 -1
- package/dist/src/exports/sub-manager-worker-entry.js +1 -1
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/sub-worker-dispatch-BLujn13f.js +792 -0
- package/dist/sub-worker-dispatch-BLujn13f.js.map +1 -0
- package/dist/types_server-BCst4TfZ.d.ts +703 -0
- package/dist/types_server-BCst4TfZ.d.ts.map +1 -0
- package/dist/types_server-BYwLkwbC.d.ts +691 -0
- package/dist/types_server-BYwLkwbC.d.ts.map +1 -0
- package/dist/types_server-Bvrohyc8.d.ts +691 -0
- package/dist/types_server-Bvrohyc8.d.ts.map +1 -0
- package/dist/types_server-C6oCm9oC.d.ts +691 -0
- package/dist/types_server-C6oCm9oC.d.ts.map +1 -0
- package/dist/types_server-CHJLfdxb.d.ts +691 -0
- package/dist/types_server-CHJLfdxb.d.ts.map +1 -0
- package/dist/types_server-DnO_fp-F.d.ts +691 -0
- package/dist/types_server-DnO_fp-F.d.ts.map +1 -0
- package/dist/types_server-gSgzZHuD.d.ts +703 -0
- package/dist/types_server-gSgzZHuD.d.ts.map +1 -0
- package/package.json +3 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as PgDatabase } from "../../postgres-
|
|
1
|
+
import { t as PgDatabase } from "../../postgres-SM8muZqN.js";
|
|
2
2
|
export { PgDatabase };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as PgDatabase } from "../../postgres-
|
|
1
|
+
import { t as PgDatabase } from "../../postgres-DwiUT_A6.js";
|
|
2
2
|
export { PgDatabase };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as ActionProcedures, An as MutationArgs, At as QueryFn, Bn as StoredSchemaColumnsOptions, Cn as ComputedFieldConfig, Ct as MutationConfig, Dn as InferSchema, Dt as OutputOf, En as IndexDefinition, Et as MutationProcedure, Fn as ReturnQuery, Ft as createMutationBuilder, Gn as tableNameToId, Hn as defineSchema, In as SchemaCodecs, It as createQueryBuilder, Ln as SchemaColumnMapping, Mn as Prettify, Mt as TypeOf, Nn as QueryArgs, Nt as createActionBuilder, On as InsertData, Ot as QueryConfig, Pn as ReturnLiveQuery, Pt as createJobBuilder, Rn as SchemaColumnsOptions, Sn as ComputedField, St as JobProcedure, Tn as DefineSchemaConfig, Tt as MutationFn, Un as schemaRegistry, Vn as defineComputedField, Wn as shouldTrackSchema, _n as matchesPredicate, _t as HandlerLogger, an as IdAndCommitTs, at as PublicProcedures, bn as ComputedCodec, bt as JobCtx, cn as JsonOpOptions, ct as Router, dn as PaginationClause, dt as ActionConfig, et as AppRouter, fn as QueryBuilder, ft as ActionCtx, gn as jsonPathExtract, gt as ContextOf, hn as jsonPathExists, ht as AnyProcedure, it as ProcedureNames, jn as OverrideField, jt as QueryProcedure, kn as Model, kt as QueryCtx, ln as OrderByOptions, lt as getContextRegistry, mn as jsonContains, mt as ActionProcedure, nt as JobProcedures, on as JsonContainsOptions, ot as QueryProcedures, pn as buildPredicateSql, pt as ActionFn, rt as MutationProcedures, sn as JsonHasKeyMultiOptions, st as RegisteredProcedure, tt as ContextRegistry, un as OrderDirection, ut as router, vn as ActionArgs, vt as HandlerObservability, wn as DeclarativeIndex, wt as MutationCtx, xn as ComputedDataType, xt as JobFn, yn as ColumnCodec, yt as JobConfig, zn as SchemaDefinition } from "../../index-
|
|
1
|
+
import { $ as ActionProcedures, An as MutationArgs, At as QueryFn, Bn as StoredSchemaColumnsOptions, Cn as ComputedFieldConfig, Ct as MutationConfig, Dn as InferSchema, Dt as OutputOf, En as IndexDefinition, Et as MutationProcedure, Fn as ReturnQuery, Ft as createMutationBuilder, Gn as tableNameToId, Hn as defineSchema, In as SchemaCodecs, It as createQueryBuilder, Ln as SchemaColumnMapping, Mn as Prettify, Mt as TypeOf, Nn as QueryArgs, Nt as createActionBuilder, On as InsertData, Ot as QueryConfig, Pn as ReturnLiveQuery, Pt as createJobBuilder, Rn as SchemaColumnsOptions, Sn as ComputedField, St as JobProcedure, Tn as DefineSchemaConfig, Tt as MutationFn, Un as schemaRegistry, Vn as defineComputedField, Wn as shouldTrackSchema, _n as matchesPredicate, _t as HandlerLogger, an as IdAndCommitTs, at as PublicProcedures, bn as ComputedCodec, bt as JobCtx, cn as JsonOpOptions, ct as Router, dn as PaginationClause, dt as ActionConfig, et as AppRouter, fn as QueryBuilder, ft as ActionCtx, gn as jsonPathExtract, gt as ContextOf, hn as jsonPathExists, ht as AnyProcedure, it as ProcedureNames, jn as OverrideField, jt as QueryProcedure, kn as Model, kt as QueryCtx, ln as OrderByOptions, lt as getContextRegistry, mn as jsonContains, mt as ActionProcedure, nt as JobProcedures, on as JsonContainsOptions, ot as QueryProcedures, pn as buildPredicateSql, pt as ActionFn, rt as MutationProcedures, sn as JsonHasKeyMultiOptions, st as RegisteredProcedure, tt as ContextRegistry, un as OrderDirection, ut as router, vn as ActionArgs, vt as HandlerObservability, wn as DeclarativeIndex, wt as MutationCtx, xn as ComputedDataType, xt as JobFn, yn as ColumnCodec, yt as JobConfig, zn as SchemaDefinition } from "../../index-DsBAE32T.js";
|
|
2
2
|
import { n as stableStringify, r as supaliveStringify, t as groupByToMap } from "../../helper-CiacMqje.js";
|
|
3
3
|
import * as z$2 from "zod";
|
|
4
4
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Bt as IncomingJobRequest, D as Context, Gt as ScheduleHandle, Ht as JobScheduler, Kt as ScheduledJobDef, Lt as DevScheduler, Rt as DevSchedulerOptions, Ut as QStashScheduler, Vt as JobClient, Wt as QStashSchedulerOptions, _r as UpdateSubscriptionReadSetParamsSchema, ar as InvalidateWritesetParams, cr as RegisterSubscriptionParams, ct as Router, dr as UnregisterSubscriptionParams, fr as UnregisterSubscriptionParamsSchema, gr as UpdateSubscriptionReadSetParams, ir as AffectedSubscription, lr as RegisterSubscriptionParamsSchema, nr as CacheLayer, or as InvalidateWritesetParamsSchema, pr as UnregisterSubscriptionResult, qn as Database, qt as SupaliveDb, rr as IORedisClient, sr as InvalidateWritesetResult, ur as RegisterSubscriptionResult, vr as UpdateSubscriptionReadSetResult, zt as EnqueueOptions } from "../../index-
|
|
2
|
-
import { t as MySqlDatabase } from "../../mysql-
|
|
3
|
-
import { t as PgDatabase } from "../../postgres-
|
|
4
|
-
import { A as QueryTrigger, C as SubManagerLink, D as OperationKind, E as OperationEvent, F as startTimer, I as traceQueryExecution, M as configureRootLogger, N as elapsedMs, O as OperationOutcome, P as getRootLogger, S as SubManagerClient, T as ObservabilityOptions, b as SupaliveServerConfig, j as RequestLogMode, k as OperationTransport, p as DatabaseConfig, u as SubId, w as Observability, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-
|
|
1
|
+
import { Bt as IncomingJobRequest, D as Context, Gt as ScheduleHandle, Ht as JobScheduler, Kt as ScheduledJobDef, Lt as DevScheduler, Rt as DevSchedulerOptions, Ut as QStashScheduler, Vt as JobClient, Wt as QStashSchedulerOptions, _r as UpdateSubscriptionReadSetParamsSchema, ar as InvalidateWritesetParams, cr as RegisterSubscriptionParams, ct as Router, dr as UnregisterSubscriptionParams, fr as UnregisterSubscriptionParamsSchema, gr as UpdateSubscriptionReadSetParams, ir as AffectedSubscription, lr as RegisterSubscriptionParamsSchema, nr as CacheLayer, or as InvalidateWritesetParamsSchema, pr as UnregisterSubscriptionResult, qn as Database, qt as SupaliveDb, rr as IORedisClient, sr as InvalidateWritesetResult, ur as RegisterSubscriptionResult, vr as UpdateSubscriptionReadSetResult, zt as EnqueueOptions } from "../../index-DsBAE32T.js";
|
|
2
|
+
import { t as MySqlDatabase } from "../../mysql-C79_qNQO.js";
|
|
3
|
+
import { t as PgDatabase } from "../../postgres-SM8muZqN.js";
|
|
4
|
+
import { A as QueryTrigger, C as SubManagerLink, D as OperationKind, E as OperationEvent, F as startTimer, I as traceQueryExecution, M as configureRootLogger, N as elapsedMs, O as OperationOutcome, P as getRootLogger, S as SubManagerClient, T as ObservabilityOptions, b as SupaliveServerConfig, j as RequestLogMode, k as OperationTransport, p as DatabaseConfig, u as SubId, w as Observability, x as UpstashConfig, y as SubscriptionManagerConfig } from "../../types_server-gSgzZHuD.js";
|
|
5
5
|
import pino, { Level } from "pino";
|
|
6
6
|
|
|
7
7
|
//#region src/server/supalive-server.d.ts
|
|
@@ -83,6 +83,13 @@ declare class SupaliveWebSocketServer<TContext extends Context> {
|
|
|
83
83
|
* procedures can schedule/cancel one-shots without reaching for the server.
|
|
84
84
|
*/
|
|
85
85
|
getJobClient(): JobClient;
|
|
86
|
+
/**
|
|
87
|
+
* The current in-memory retention watermark (oldest retained commit-log ts) —
|
|
88
|
+
* the value the one-shot query cache freshness scan uses as its retention
|
|
89
|
+
* guard. Hand it to the internal caller's `init({ getMinRetainedTs })` so its
|
|
90
|
+
* cache-first reads use the SAME guard as the WS/HTTP query paths.
|
|
91
|
+
*/
|
|
92
|
+
getMinRetainedTs(): bigint;
|
|
86
93
|
stop(): Promise<void>;
|
|
87
94
|
getStats(): {
|
|
88
95
|
instanceId: string;
|
|
@@ -1 +1 @@
|
|
|
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":";;;;;;;;;AAiIA;cAAa,uBAAA,kBAAyC,OAAA;EAAA,QAC5C,MAAA;EAAA,QACA,UAAA;EAAA,QAEA,UAAA;EAAA,QACA,GAAA;EAAA,QAEA,KAAA;EAAA,QACA,QAAA;EAmNO;EAAA,iBAjNE,iBAAA;EAAA,QACT,EAAA;EAAA,QACA,eAAA;EAAA,QACA,MAAA;EAAA,QAMA,kBAAA;EAAA,QAIA,WAAA;EAAA,QAEA,SAAA;EAAA,QACA,OAAA;EAAA,QACA,SAAA;
|
|
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":";;;;;;;;;AAiIA;cAAa,uBAAA,kBAAyC,OAAA;EAAA,QAC5C,MAAA;EAAA,QACA,UAAA;EAAA,QAEA,UAAA;EAAA,QACA,GAAA;EAAA,QAEA,KAAA;EAAA,QACA,QAAA;EAmNO;EAAA,iBAjNE,iBAAA;EAAA,QACT,EAAA;EAAA,QACA,eAAA;EAAA,QACA,MAAA;EAAA,QAMA,kBAAA;EAAA,QAIA,WAAA;EAAA,QAEA,SAAA;EAAA,QACA,OAAA;EAAA,QACA,SAAA;EA63C0D;;EAAA,QA13C1D,UAAA;EA9B4C;EAAA,QAgC5C,OAAA;EAAA,QAEA,QAAA;EAAA,QACA,mBAAA;EAAA,QACA,gBAAA;EAAA,QAEA,KAAA;EA9BA;EAAA,QAqCA,GAAA;EAlCA;EAAA,QAoCA,GAAA;cAEI,MAAA,EAAQ,oBAAA,CAAqB,QAAA;EA9BjC;;;;;;;;;;;;EAAA,QAuKA,qBAAA;EAAA,QAqBA,kBAAA;EAOR,cAAA,CAAe,MAAA,EAAQ,MAAA,MAAY,QAAA;EAK7B,KAAA,IAAS,OAAA;EA1KH;;;;;;EAAA,QAkME,gBAAA;EAxBR;;;;;EA4CC,gBAAA,IAAoB,aAAA;EAKf;EAAZ,WAAA,CAAY,IAAA;EASM;;;;;EAAZ,WAAA,CAAY,IAAA,UAAc,IAAA,WAAe,IAAA,GAAO,cAAA,GAAiB,OAAA,CAAQ,cAAA;EAMzE;EAAA,SAAA,CAAU,QAAA,WAAmB,OAAA;EAAA;;;;EASnC,YAAA,IAAgB,SAAA;EAcF;;;;;;EAJd,gBAAA;EAIM,IAAA,IAAQ,OAAA;EAUd,QAAA;;;;;;EASA,aAAA;EAwQc;;;;;EAAA,QA/PA,iBAAA;EAAA,QA8CN,sBAAA;EAAA,QAQM,gBAAA;EAAA,QA4DA,aAAA;EAAA,QAmBA,cAAA;EAAA,QAqBA,UAAA;EAytBA;;;;;;;EAAA,QAtrBN,eAAA;EA23BsC;;;;;EAAA,QAj2BhC,kBAAA;EAAA,QAwCA,UAAA;EAAA,QAmLA,eAAA;EAAA,QAwLA,iBAAA;EAAA,QA2BA,gBAAA;;;;AC/kChB;;;;AAA+D;AAC/D;;;;AAAiE;AACjE;UDmoCgB,qBAAA;EAAA,QAgEA,kBAAA;ECnsCG;AAAA;AAqBnB;;;;EArBmB,QD+sCT,uBAAA;EAAA,QAoBM,oBAAA;ECzsCiB;;;;;;;;;;;;;EAAA,QD0uCjB,gBAAA;EAAA,QAgBN,kBAAA;EAAA,QAOM,oBAAA;EAAA,QAgCA,sBAAA;EAAA,QA2EA,gBAAA;EAAA,QAsCN,WAAA;EAAA,QAYA,SAAA;EAAA,QAIM,0BAAA;;;;AElwChB;;;UFozCgB,kBAAA;EE3tCC;;;;;;EF6uCT,mBAAA,CAAoB,MAAA,cAAoB,OAAA;EEn0CtC;;;;;;;;EFq1CF,iBAAA,CAAkB,OAAA,UAAiB,OAAA,WAAkB,OAAA;EAAA,QAI7C,2BAAA;EAAA,QAmCA,yBAAA;EAAA,QA0BA,yBAAA;AAAA;;;cCrlDH,aAAA;AAAA,cACA,YAAA;AAAA,cACA,MAAA,EAAM,IAAA,CAAA,MAAA;AAAA,iBAqBH,UAAA,CAAW,IAAA,WAAe,KAAK;AAAA,iBAK/B,iBAAA,wCAAyD,GAAA,EAAK,GAAA,CAAI,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;;;;cCgKvF,mBAAA;EAAA,QACH,GAAA;EAAA,QACA,IAAA;EAAA,QACA,MAAA;EAAA,QACA,OAAA;EAAA,QACA,aAAA;EAAA,wBACgB,iBAAA;cAEZ,MAAA,EAAQ,yBAAA;EFnEZ;;;;;;EAAA,QEmHA,iBAAA;EAiCF,KAAA,IAAS,OAAA;EAuBT,IAAA,IAAQ,OAAA;EFvJN;;;;;;;EEwKR,SAAA,IAAa,cAAA;EAAA,QAaC,eAAA;EFjKN;;;EAAA,QE6LM,2BAAA;EAUd,WAAA;EAIA,cAAA,CAAe,KAAA;EAAA,QAIP,SAAA;EAAA,QAIA,eAAA;EAAA,QAWA,SAAA;EAAA,QAIM,oBAAA;EAAA,QAKA,yBAAA;EAAA,QAwBA,yBAAA;EAAA,QAKA,sBAAA;EAAA,QAKA,uBAAA;EAAA,QAsBA,kBAAA;AAAA;;;;AF5UhB;;;;;;;;;;cG7Ga,YAAA;;;;;;;;;;iBAWS,QAAA,CAAS,EAAA,EAAI,QAAA,GAAW,OAAO;;;;;;cA6BxC,mBAAA;AAAA,KAID,aAAA;EACR,QAAA,EAAU,cAAA;EH2EJ;;;;;;EGpEN,eAAA,GAAkB,cAAA;EAClB,UAAA,EAAY,UAAA;EACZ,oBAAA;EACA,QAAA;AAAA;AAAA,iBAGkB,YAAA,CAAa,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,UAAA;AAAA,iBAgBpD,cAAA,CAAe,MAAA,EAAQ,cAAA,EAAgB,QAAA,YAAoB,UAAA,GAAa,aAAA;;;;;;;;;;;;;;iBA4DlE,gBAAA,CAAiB,EAAA,EAAI,UAAA,EAAY,KAAA,EAAO,UAAA,EAAY,WAAA,aAAsB,OAAA;AAAA,KAsBpF,qBAAA;EACR,OAAA,EAAS,aAAa;EAEtB,eAAA;EAEA,iBAAA;EAEA,iBAAA;EAEA,oBAAA;EAEA,eAAA;AAAA;AAAA,iBAGY,cAAA,CAAe,MAAA,EAAQ,qBAAA;EAA0B,UAAA,EAAY,UAAA;EAAY,cAAA,GAAiB,aAAA;AAAA;;;;iBCvLpF,SAAA,CAAU,KAAA,YAAiB,OAAO;;;;;cAS3C,kBAAA;;;;;;;;;iBAgDS,sBAAA,CACpB,SAAA,UACA,KAAA,oBACA,aAAA,WACC,OAAO;;;;;;iBAWY,gBAAA,CACpB,SAAA,UACA,KAAA,oBACA,aAAA,WACC,OAAO"}
|
|
@@ -1,418 +1,19 @@
|
|
|
1
1
|
import { l as primaryOnlyConn, n as getContextRegistry } from "../../router-DP2ThAwh.js";
|
|
2
2
|
import { n as stableStringify, r as supaliveStringify, t as groupByToMap } from "../../helper-zdJT5FUc.js";
|
|
3
|
-
import { s as traceCacheCall } from "../../query-LJranz0c.js";
|
|
4
|
-
import { r as evaluateCacheFreshness } from "../../overlap-checker-1TwhkAwy.js";
|
|
5
3
|
import { i as AuthenticationError, o as ClientMessageSchema } from "../../types_client_rpc-B8jh6MKl.js";
|
|
6
4
|
import { n as RemoteSubscriptionMessageSchema } from "../../types_server-BhC19R2a.js";
|
|
7
|
-
import { n as sleep } from "../../
|
|
8
|
-
import {
|
|
9
|
-
import { n as
|
|
10
|
-
import {
|
|
5
|
+
import { a as serializeMapValue, i as logger, n as isProduction, r as logLevelOf, s as sleep, t as appInstanceId } from "../../logger-DrXccWFZ.js";
|
|
6
|
+
import { a as elapsedMs, c as traceQueryExecution, d as generateSubscriptionId, f as getHashOf, i as configureRootLogger, l as ANONYMOUS_IDENTITY, o as getRootLogger, p as resolveQueryIdentity, r as Observability, s as startTimer, t as runCachedQuery, u as generateCacheKey } from "../../one-shot-query-BBiv9LGP.js";
|
|
7
|
+
import { S as RetentionWatermark, _ 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, x as RETENTION_WATERMARK_CHANNEL, y as UpdateSubscriptionReadSetParamsSchema } from "../../sub-worker-dispatch-BLujn13f.js";
|
|
8
|
+
import { context, trace } from "@opentelemetry/api";
|
|
11
9
|
import { v4 } from "uuid";
|
|
12
10
|
import { createServer } from "http";
|
|
13
11
|
import { WebSocket, WebSocketServer } from "ws";
|
|
14
12
|
import { ExponentialBackoff, handleAll, retry } from "cockatiel";
|
|
15
|
-
import { sha1 } from "hash-wasm";
|
|
16
13
|
import { Client, Server } from "rpc-websockets";
|
|
17
14
|
import { Client as Client$1, Receiver } from "@upstash/qstash";
|
|
18
15
|
import { Worker } from "node:worker_threads";
|
|
19
16
|
import { fileURLToPath } from "node:url";
|
|
20
|
-
//#region src/server/sub_hash.ts
|
|
21
|
-
/** Compute stable hash using json-stable-stringify and SHA1. */
|
|
22
|
-
async function getHashOf(input) {
|
|
23
|
-
return sha1((typeof input === "string" ? input : stableStringify(input)) ?? "");
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Default identity literal used when neither a procedure-level
|
|
27
|
-
* `queryIdentity` override nor a per-session user is available.
|
|
28
|
-
*/
|
|
29
|
-
const ANONYMOUS_IDENTITY = "anonymous";
|
|
30
|
-
/**
|
|
31
|
-
* Resolve the segmentation key that participates in the subId/cacheKey hash for
|
|
32
|
-
* a query. Shared by every query transport (WS subscribe, WS one-shot call, and
|
|
33
|
-
* HTTP RPC) so identity — and therefore cache segmentation — is computed
|
|
34
|
-
* identically regardless of how the query is invoked. Priority:
|
|
35
|
-
*
|
|
36
|
-
* 1. Procedure-level `queryIdentity` override:
|
|
37
|
-
* - `false` → `ANONYMOUS_IDENTITY` (cache shared across users)
|
|
38
|
-
* - `string` → that literal
|
|
39
|
-
* - `function` → serverCtx + input → string | null | undefined
|
|
40
|
-
* 2. `getUserId(serverCtx)` → falls back to `ANONYMOUS_IDENTITY` when it
|
|
41
|
-
* returns null/undefined or isn't configured.
|
|
42
|
-
*/
|
|
43
|
-
function resolveQueryIdentity(procedure, serverCtx, input, getUserId) {
|
|
44
|
-
const override = procedure.queryIdentity;
|
|
45
|
-
if (override !== void 0) {
|
|
46
|
-
if (override === false) return ANONYMOUS_IDENTITY;
|
|
47
|
-
if (typeof override === "string") return override;
|
|
48
|
-
if (typeof override === "function") {
|
|
49
|
-
const value = override(serverCtx, input);
|
|
50
|
-
if (value === null || value === void 0) return ANONYMOUS_IDENTITY;
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (!getUserId) return ANONYMOUS_IDENTITY;
|
|
55
|
-
return getUserId(serverCtx) ?? "anonymous";
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Compute a subscription id from hash of procedure + input + queryIdentity.
|
|
59
|
-
*
|
|
60
|
-
* `queryIdentity` is the segmentation key for cache/subscriptions. By default
|
|
61
|
-
* it is the authenticated user's id (so two users get distinct subscriptions
|
|
62
|
-
* for the same query), but a procedure may override it to share cache/subs
|
|
63
|
-
* across users (e.g. for a public, user-independent query).
|
|
64
|
-
*/
|
|
65
|
-
async function generateSubscriptionId(procedure, input, queryIdentity) {
|
|
66
|
-
return `sub_${await getHashOf(`${procedure}_${typeof input === "string" ? input : stableStringify(input)}_${queryIdentity}`)}`;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Compute cache key from query name + args + queryIdentity. The identity
|
|
70
|
-
* participates so that user-specific results don't poison each other's
|
|
71
|
-
* cache when a procedure does authorize per-user.
|
|
72
|
-
*/
|
|
73
|
-
async function generateCacheKey(queryName, input, queryIdentity) {
|
|
74
|
-
return `ck_${await getHashOf(`${queryName}_${typeof input === "string" ? input : stableStringify(input)}_${queryIdentity}`)}`;
|
|
75
|
-
}
|
|
76
|
-
//#endregion
|
|
77
|
-
//#region src/observability.ts
|
|
78
|
-
let rootLogger = logger;
|
|
79
|
-
/** Replace the process-wide root logger. Call once at startup, before creating
|
|
80
|
-
* the server, to route Supalive's request/lifecycle logs through your own pino
|
|
81
|
-
* instance. */
|
|
82
|
-
function configureRootLogger(logger) {
|
|
83
|
-
rootLogger = logger;
|
|
84
|
-
}
|
|
85
|
-
/** The current root logger (defaults to the env-configured singleton). */
|
|
86
|
-
function getRootLogger() {
|
|
87
|
-
return rootLogger;
|
|
88
|
-
}
|
|
89
|
-
const CLIENT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
90
|
-
"NOT_FOUND",
|
|
91
|
-
"BAD_REQUEST",
|
|
92
|
-
"INVALID_MESSAGE",
|
|
93
|
-
"PARSE_ERROR",
|
|
94
|
-
"UNAUTHENTICATED",
|
|
95
|
-
"RATE_LIMITED",
|
|
96
|
-
"METHOD_NOT_ALLOWED",
|
|
97
|
-
"TOO_MANY_CONCURRENT_MUTATIONS",
|
|
98
|
-
"TOO_MANY_CONCURRENT_ACTIONS",
|
|
99
|
-
"SUBSCRIBE_ERROR"
|
|
100
|
-
]);
|
|
101
|
-
/**
|
|
102
|
-
* The bound observability surface for one server instance: an injectable logger,
|
|
103
|
-
* OTel metric instruments, and the `record()` seam every operation funnels
|
|
104
|
-
* through. Built once in the server constructor and shared with the HTTP RPC
|
|
105
|
-
* handler.
|
|
106
|
-
*/
|
|
107
|
-
var Observability = class {
|
|
108
|
-
logger;
|
|
109
|
-
requestLog;
|
|
110
|
-
onEvent;
|
|
111
|
-
requestCounter;
|
|
112
|
-
durationHistogram;
|
|
113
|
-
activeConnections;
|
|
114
|
-
activeSubscriptions;
|
|
115
|
-
tracer;
|
|
116
|
-
constructor(opts = {}) {
|
|
117
|
-
const base = opts.logger ?? getRootLogger();
|
|
118
|
-
this.logger = opts.level ? base.child({}, { level: opts.level }) : base;
|
|
119
|
-
this.requestLog = opts.requestLog ?? "all";
|
|
120
|
-
this.onEvent = opts.onEvent;
|
|
121
|
-
const meterName = opts.meterName ?? "@supalive/core";
|
|
122
|
-
const meter = metrics.getMeter(meterName, opts.meterVersion);
|
|
123
|
-
this.tracer = trace.getTracer(meterName, opts.meterVersion);
|
|
124
|
-
this.requestCounter = meter.createCounter("supalive_requests_total", { description: "Total Supalive operations (WS calls, subscribes, HTTP RPC, jobs)." });
|
|
125
|
-
this.durationHistogram = meter.createHistogram("supalive_request_duration_ms", {
|
|
126
|
-
description: "Operation duration in milliseconds.",
|
|
127
|
-
unit: "ms"
|
|
128
|
-
});
|
|
129
|
-
this.activeConnections = meter.createUpDownCounter("supalive_active_connections", { description: "Currently open WebSocket connections." });
|
|
130
|
-
this.activeSubscriptions = meter.createUpDownCounter("supalive_active_subscriptions", { description: "Currently registered live-query subscriptions on this instance." });
|
|
131
|
-
}
|
|
132
|
-
/** Record one completed operation: metrics + optional onEvent + one log line,
|
|
133
|
-
* and finalize the op's span (status/attributes/exception) when present. */
|
|
134
|
-
record(e) {
|
|
135
|
-
const traceId = this.finalizeSpan(e);
|
|
136
|
-
if (traceId) (e.extra ??= {}).traceId = traceId;
|
|
137
|
-
const attrs = {
|
|
138
|
-
transport: e.transport,
|
|
139
|
-
procedure: e.procedure ?? "",
|
|
140
|
-
kind: e.kind ?? "",
|
|
141
|
-
outcome: e.outcome,
|
|
142
|
-
code: e.code ?? ""
|
|
143
|
-
};
|
|
144
|
-
this.requestCounter.add(1, attrs);
|
|
145
|
-
this.durationHistogram.record(e.durationMs, attrs);
|
|
146
|
-
if (this.onEvent) try {
|
|
147
|
-
this.onEvent(e);
|
|
148
|
-
} catch {}
|
|
149
|
-
if (this.requestLog === "off") return;
|
|
150
|
-
if (e.outcome === "ok") {
|
|
151
|
-
if (this.requestLog !== "all") return;
|
|
152
|
-
this.logger.info(logFields(e), summary(e));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const fields = logFields(e);
|
|
156
|
-
if (!e.error && e.code != null && CLIENT_ERROR_CODES.has(e.code)) this.logger.warn(fields, summary(e));
|
|
157
|
-
else this.logger.error({
|
|
158
|
-
...fields,
|
|
159
|
-
err: e.error
|
|
160
|
-
}, summary(e));
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Start a SERVER span for an operation, as a child of `parent` (or the active
|
|
164
|
-
* context). No-op — returns a non-recording span — until a TracerProvider is
|
|
165
|
-
* registered by the app, so this costs nothing when tracing is disabled.
|
|
166
|
-
* Returns the span plus a context that has it active, for `context.with(...)`
|
|
167
|
-
* wrapping so nested handler work / `ctx.span` children attach correctly.
|
|
168
|
-
*/
|
|
169
|
-
startSpan(name, opts = {}) {
|
|
170
|
-
const parent = opts.parent ?? context.active();
|
|
171
|
-
const span = this.tracer.startSpan(name, {
|
|
172
|
-
kind: SpanKind.SERVER,
|
|
173
|
-
attributes: {
|
|
174
|
-
...opts.transport ? { "supalive.transport": opts.transport } : {},
|
|
175
|
-
...opts.procedure ? { "supalive.procedure": opts.procedure } : {},
|
|
176
|
-
...opts.kind ? { "supalive.kind": opts.kind } : {}
|
|
177
|
-
}
|
|
178
|
-
}, parent);
|
|
179
|
-
return {
|
|
180
|
-
span,
|
|
181
|
-
context: trace.setSpan(parent, span)
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Continue a trace from an inbound carrier (HTTP headers, or a WS message's
|
|
186
|
-
* `trace` field). Returns a context to pass as `startSpan({ parent })`. When
|
|
187
|
-
* no propagator is registered / no trace context is present, returns the root
|
|
188
|
-
* context (a fresh trace).
|
|
189
|
-
*/
|
|
190
|
-
extractContext(carrier) {
|
|
191
|
-
return propagation.extract(ROOT_CONTEXT, carrier);
|
|
192
|
-
}
|
|
193
|
-
/** Set the span's final status/attributes from the event, then end it.
|
|
194
|
-
* Returns the (valid) trace id for log correlation, or undefined. */
|
|
195
|
-
finalizeSpan(e) {
|
|
196
|
-
const span = e.span;
|
|
197
|
-
if (!span) return void 0;
|
|
198
|
-
try {
|
|
199
|
-
span.setAttribute("supalive.outcome", e.outcome);
|
|
200
|
-
if (e.code) span.setAttribute("supalive.code", e.code);
|
|
201
|
-
if (e.procedure) span.setAttribute("supalive.procedure", e.procedure);
|
|
202
|
-
if (e.kind) span.setAttribute("supalive.kind", e.kind);
|
|
203
|
-
if (e.outcome === "error") {
|
|
204
|
-
if (e.error instanceof Error) span.recordException(e.error);
|
|
205
|
-
span.setStatus({
|
|
206
|
-
code: SpanStatusCode.ERROR,
|
|
207
|
-
message: e.code
|
|
208
|
-
});
|
|
209
|
-
} else span.setStatus({ code: SpanStatusCode.OK });
|
|
210
|
-
} finally {
|
|
211
|
-
span.end();
|
|
212
|
-
}
|
|
213
|
-
const sc = span.spanContext();
|
|
214
|
-
return isSpanContextValid(sc) ? sc.traceId : void 0;
|
|
215
|
-
}
|
|
216
|
-
/** A request-scoped child logger for `ctx.log`, bound with the given fields
|
|
217
|
-
* (reqId, procedure, and trace id when tracing is on). */
|
|
218
|
-
childLogger(fields) {
|
|
219
|
-
return this.logger.child(fields);
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* A LAZY request-scoped `ctx.log`. Most handlers never log, and pino's
|
|
223
|
-
* `.child()` (bindings merge + serialization) isn't free — so this defers the
|
|
224
|
-
* child creation until the first `debug/info/warn/error` call and reuses it
|
|
225
|
-
* after. When a handler logs nothing, no child logger is ever allocated; the
|
|
226
|
-
* only per-request cost is this small delegator object. Returns the narrow
|
|
227
|
-
* {@link HandlerLogger} surface the handler context exposes.
|
|
228
|
-
*/
|
|
229
|
-
lazyChildLogger(fields) {
|
|
230
|
-
const base = this.logger;
|
|
231
|
-
let child;
|
|
232
|
-
const get = () => child ??= base.child(fields);
|
|
233
|
-
return {
|
|
234
|
-
debug: (...args) => get().debug(...args),
|
|
235
|
-
info: (...args) => get().info(...args),
|
|
236
|
-
warn: (...args) => get().warn(...args),
|
|
237
|
-
error: (...args) => get().error(...args)
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
/** The span's trace id, or undefined when tracing is off / the span is
|
|
241
|
-
* non-recording. For binding onto `ctx.log`. */
|
|
242
|
-
traceIdOf(span) {
|
|
243
|
-
if (!span) return void 0;
|
|
244
|
-
const sc = span.spanContext();
|
|
245
|
-
return isSpanContextValid(sc) ? sc.traceId : void 0;
|
|
246
|
-
}
|
|
247
|
-
connectionOpened() {
|
|
248
|
-
this.activeConnections.add(1);
|
|
249
|
-
}
|
|
250
|
-
connectionClosed() {
|
|
251
|
-
this.activeConnections.add(-1);
|
|
252
|
-
}
|
|
253
|
-
subscriptionAdded(n = 1) {
|
|
254
|
-
this.activeSubscriptions.add(n);
|
|
255
|
-
}
|
|
256
|
-
subscriptionRemoved(n = 1) {
|
|
257
|
-
this.activeSubscriptions.add(-n);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
/** Start marker for an operation; pass to {@link elapsedMs} to get its duration. */
|
|
261
|
-
function startTimer() {
|
|
262
|
-
return performance.now();
|
|
263
|
-
}
|
|
264
|
-
function elapsedMs(startedAt) {
|
|
265
|
-
return Math.round((performance.now() - startedAt) * 1e3) / 1e3;
|
|
266
|
-
}
|
|
267
|
-
const coreTracer = trace.getTracer("@supalive/core");
|
|
268
|
-
/**
|
|
269
|
-
* Run a query handler inside a `query <procedureName>` span, nested under
|
|
270
|
-
* `parentContext` (the operation span) or the active context. The span is handed
|
|
271
|
-
* to `run` so it becomes the query handler's `ctx.span` and the parent of any DB
|
|
272
|
-
* spans. Cache HITs never call this (no execution), so a span appearing means a
|
|
273
|
-
* real recompute — including the otherwise-invisible background reactive path,
|
|
274
|
-
* where `parentContext` is omitted and the span is a root.
|
|
275
|
-
*
|
|
276
|
-
* No-op-cheap when tracing is off: the started span is non-recording, so we skip
|
|
277
|
-
* the `context.with` and just run.
|
|
278
|
-
*/
|
|
279
|
-
function traceQueryExecution(procedureName, trigger, parentContext, run) {
|
|
280
|
-
const parent = parentContext ?? context.active();
|
|
281
|
-
const span = coreTracer.startSpan(`query ${procedureName}`, { attributes: {
|
|
282
|
-
"supalive.kind": "query",
|
|
283
|
-
"supalive.procedure": procedureName,
|
|
284
|
-
"supalive.trigger": trigger
|
|
285
|
-
} }, parent);
|
|
286
|
-
const exec = async () => {
|
|
287
|
-
try {
|
|
288
|
-
const r = await run(span);
|
|
289
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
290
|
-
return r;
|
|
291
|
-
} catch (err) {
|
|
292
|
-
if (err instanceof Error) span.recordException(err);
|
|
293
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
294
|
-
throw err;
|
|
295
|
-
} finally {
|
|
296
|
-
span.end();
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
return span.isRecording() ? context.with(trace.setSpan(parent, span), exec) : exec();
|
|
300
|
-
}
|
|
301
|
-
function logFields(e) {
|
|
302
|
-
const f = {
|
|
303
|
-
op: e.op,
|
|
304
|
-
transport: e.transport,
|
|
305
|
-
outcome: e.outcome,
|
|
306
|
-
durMs: e.durationMs
|
|
307
|
-
};
|
|
308
|
-
if (e.procedure) f.procedure = e.procedure;
|
|
309
|
-
if (e.kind) f.kind = e.kind;
|
|
310
|
-
if (e.code) f.code = e.code;
|
|
311
|
-
if (e.reqId) f.reqId = e.reqId;
|
|
312
|
-
if (e.sessionId) f.sessionId = e.sessionId;
|
|
313
|
-
if (e.userId != null) f.userId = e.userId;
|
|
314
|
-
if (e.extra) Object.assign(f, e.extra);
|
|
315
|
-
return f;
|
|
316
|
-
}
|
|
317
|
-
function summary(e) {
|
|
318
|
-
const label = e.procedure ? `${e.kind ?? e.op} ${e.procedure}` : e.op;
|
|
319
|
-
return `${e.transport} ${label} ${e.outcome}`;
|
|
320
|
-
}
|
|
321
|
-
//#endregion
|
|
322
|
-
//#region src/server/one-shot-query.ts
|
|
323
|
-
/**
|
|
324
|
-
* Execute a one-shot query (WS `call` or HTTP RPC) with the same caching model
|
|
325
|
-
* as a live subscription — but without registering a tracked subscription.
|
|
326
|
-
*
|
|
327
|
-
* Flow (mirrors `handleSubscribe` / worker `register`):
|
|
328
|
-
* 1. Resolve `queryIdentity` → `cacheKey` (identical segmentation to subs).
|
|
329
|
-
* 2. Pick the read connection ONCE (replica when it's caught up to the client's
|
|
330
|
-
* `clientMinTs`, else primary) and use it for BOTH the freshness scan and
|
|
331
|
-
* the row reads, so a cache HIT offloads the primary too — not just a miss.
|
|
332
|
-
* 3. Freshness check via {@link evaluateCacheFreshness} on that connection:
|
|
333
|
-
* cached metadata + the retention watermark + a between-ts commit-log scan.
|
|
334
|
-
* 4. Fresh → return the cached data.
|
|
335
|
-
* Stale/miss → recompute via `queryInternalOn` (on the chosen connection)
|
|
336
|
-
* and write BOTH the data and the metadata (readSet + snapshotTs) back, so
|
|
337
|
-
* a later one-shot or a fresh subscription can reuse it.
|
|
338
|
-
*
|
|
339
|
-
* The retention guard is what makes an untracked one-shot safe: nothing holds
|
|
340
|
-
* the prune watermark down for it, so an entry whose snapshot has fallen below
|
|
341
|
-
* the oldest retained commit log is treated as a miss (its freshness scan would
|
|
342
|
-
* be incomplete).
|
|
343
|
-
*
|
|
344
|
-
* `lastSnapshotTs` is the primary freshness upper bound — the latest snapshot ts
|
|
345
|
-
* as of this call. The caller loads it (via `db.getLatestSnapshotTimestamp()`)
|
|
346
|
-
* and passes it in, exactly like the subscribe path loads it before `register`,
|
|
347
|
-
* so the snapshot read lives at the caller seam rather than being hidden here.
|
|
348
|
-
*
|
|
349
|
-
* `clientMinTs` is the caller's read high-water (the largest commit/snapshot ts
|
|
350
|
-
* it has already observed). It gates replica routing: a query only runs on the
|
|
351
|
-
* replica when the replica has applied at least `clientMinTs` — otherwise reads
|
|
352
|
-
* (and the freshness scan) stay on the primary, preserving read-your-writes and
|
|
353
|
-
* monotonic reads. It is clamped to `lastSnapshotTs` here, so a client can at
|
|
354
|
-
* most force the primary (never a value beyond it). Returns the data plus the
|
|
355
|
-
* `ts` the result reflects, which the caller echoes so the client can advance
|
|
356
|
-
* its high-water.
|
|
357
|
-
*/
|
|
358
|
-
async function runCachedQuery(deps, handlerObs, procedure, procedureName, input, serverCtx, lastSnapshotTs, clientMinTs) {
|
|
359
|
-
const queryIdentity = resolveQueryIdentity(procedure, serverCtx, input, deps.getUserId);
|
|
360
|
-
const cacheKey = await generateCacheKey(procedureName, stableStringify(input), queryIdentity);
|
|
361
|
-
const routing = await resolveReadRouting(deps.db, lastSnapshotTs, clientMinTs ?? 0n);
|
|
362
|
-
const readTs = routing.readTs;
|
|
363
|
-
const { metadata, freshness } = await traceCacheCall("freshness", async () => {
|
|
364
|
-
const metadata = await deps.cache.getQueryCacheMetaData(cacheKey);
|
|
365
|
-
return {
|
|
366
|
-
metadata,
|
|
367
|
-
freshness: await evaluateCacheFreshness(routing.readImpl, metadata, readTs, deps.getMinRetainedTs())
|
|
368
|
-
};
|
|
369
|
-
});
|
|
370
|
-
if (freshness.fresh) {
|
|
371
|
-
const cached = await traceCacheCall("get", async (setHit) => {
|
|
372
|
-
const c = await deps.cache.getQueryCacheFor(cacheKey);
|
|
373
|
-
setHit(!!(c && metadata && c.version === metadata.version));
|
|
374
|
-
return c;
|
|
375
|
-
});
|
|
376
|
-
if (cached && metadata && cached.version === metadata.version) {
|
|
377
|
-
if (readTs > metadata.lastSnapshotTs) deps.cache.advanceQueryCacheMetadata(cacheKey, {
|
|
378
|
-
lastSnapshotTs: readTs,
|
|
379
|
-
version: metadata.version,
|
|
380
|
-
readSet: metadata.readSet
|
|
381
|
-
}).catch((err) => logger.error(err, "one-shot horizon advance failed"));
|
|
382
|
-
const servedTs = readTs > metadata.lastSnapshotTs ? readTs : metadata.lastSnapshotTs;
|
|
383
|
-
return {
|
|
384
|
-
data: cached.data,
|
|
385
|
-
ts: servedTs
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
const result = await deps.db.queryInternalOn({
|
|
390
|
-
impl: routing.readImpl,
|
|
391
|
-
beginTs: readTs
|
|
392
|
-
}, async (ctx) => {
|
|
393
|
-
const fn = procedure.fn;
|
|
394
|
-
return traceQueryExecution(procedureName, "request", void 0, (querySpan) => {
|
|
395
|
-
const qctx = {
|
|
396
|
-
db: ctx,
|
|
397
|
-
serverCtx,
|
|
398
|
-
...makeConnRouting(ctx, routing),
|
|
399
|
-
log: handlerObs.log,
|
|
400
|
-
span: querySpan
|
|
401
|
-
};
|
|
402
|
-
return fn(qctx, input);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
await deps.cache.setQueryCacheAndMetadataFor(cacheKey, result.data, {
|
|
406
|
-
lastSnapshotTs: result.ts,
|
|
407
|
-
version: String(result.ts),
|
|
408
|
-
readSet: result.readSet
|
|
409
|
-
}).catch((err) => logger.error(err, "one-shot query cache write failed"));
|
|
410
|
-
return {
|
|
411
|
-
data: result.data,
|
|
412
|
-
ts: result.ts
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
//#endregion
|
|
416
17
|
//#region src/server/sub-manager-client.ts
|
|
417
18
|
const logger$3 = logger.child({ name: "SUBS_MGR_CLIENT" }, { level: logLevelOf("SUB_MANAGER_CLIENT_LOG_LEVEL") });
|
|
418
19
|
const subscriptionManagerDataPack = {
|
|
@@ -1081,6 +682,8 @@ var HttpRpcHandler = class {
|
|
|
1081
682
|
const fn = procedure.fn;
|
|
1082
683
|
const result = await context.with(started.context, () => fn({
|
|
1083
684
|
db: this.deps.db,
|
|
685
|
+
storage: this.deps.storage,
|
|
686
|
+
scheduler: this.deps.scheduler,
|
|
1084
687
|
serverCtx,
|
|
1085
688
|
log: jobLog,
|
|
1086
689
|
span
|
|
@@ -1361,7 +964,7 @@ var SupaliveWebSocketServer = class {
|
|
|
1361
964
|
this.oneShotDeps = {
|
|
1362
965
|
db: this.db,
|
|
1363
966
|
cache: this.cache,
|
|
1364
|
-
getUserId: config.getUserId,
|
|
967
|
+
getUserId: config.getUserId ?? (() => null),
|
|
1365
968
|
getMinRetainedTs: () => this.retentionWatermark.get()
|
|
1366
969
|
};
|
|
1367
970
|
this.rpcHandler = new HttpRpcHandler({
|
|
@@ -1504,6 +1107,15 @@ var SupaliveWebSocketServer = class {
|
|
|
1504
1107
|
getJobClient() {
|
|
1505
1108
|
return this.jobClient;
|
|
1506
1109
|
}
|
|
1110
|
+
/**
|
|
1111
|
+
* The current in-memory retention watermark (oldest retained commit-log ts) —
|
|
1112
|
+
* the value the one-shot query cache freshness scan uses as its retention
|
|
1113
|
+
* guard. Hand it to the internal caller's `init({ getMinRetainedTs })` so its
|
|
1114
|
+
* cache-first reads use the SAME guard as the WS/HTTP query paths.
|
|
1115
|
+
*/
|
|
1116
|
+
getMinRetainedTs() {
|
|
1117
|
+
return this.retentionWatermark.get();
|
|
1118
|
+
}
|
|
1507
1119
|
async stop() {
|
|
1508
1120
|
this.wss.close();
|
|
1509
1121
|
for (const [ws] of this.sessions) ws.close(1001, "Server shutting down");
|
|
@@ -1529,10 +1141,18 @@ var SupaliveWebSocketServer = class {
|
|
|
1529
1141
|
*/
|
|
1530
1142
|
async handleHttpRequest(req, res) {
|
|
1531
1143
|
const path = (req.url ?? "/").split("?")[0];
|
|
1532
|
-
if (req.method === "GET" && path === "/_health")
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1144
|
+
if (req.method === "GET" && path === "/_health") {
|
|
1145
|
+
const guard = this.config.guardHealthByHeader;
|
|
1146
|
+
if (guard) {
|
|
1147
|
+
const raw = req.headers[guard.name.toLowerCase()];
|
|
1148
|
+
const headerValue = Array.isArray(raw) ? raw[0] : raw;
|
|
1149
|
+
if (headerValue !== void 0 && (guard.value === void 0 || headerValue === guard.value)) return sendJson(res, 404, { error: "not found" });
|
|
1150
|
+
}
|
|
1151
|
+
return sendJson(res, 200, {
|
|
1152
|
+
status: "ok",
|
|
1153
|
+
instanceId: this.instanceId
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1536
1156
|
if (this.rpcHandler.handlesPath(path)) return this.rpcHandler.handle(req, res, path);
|
|
1537
1157
|
if (this.rpcHandler.handlesJobPath(path)) return this.rpcHandler.handleJob(req, res, path);
|
|
1538
1158
|
if (this.config.onHttpRequest) {
|
|
@@ -1964,13 +1584,14 @@ var SupaliveWebSocketServer = class {
|
|
|
1964
1584
|
this.obs.subscriptionAdded();
|
|
1965
1585
|
}
|
|
1966
1586
|
if (liveResult) {
|
|
1967
|
-
const
|
|
1587
|
+
const updateResult = await this.subManagerClient.updateSubscriptionReadSet({
|
|
1968
1588
|
cacheKey,
|
|
1969
1589
|
subId,
|
|
1970
1590
|
lastSnapshotTs: liveResult.ts,
|
|
1971
1591
|
readSet: liveResult.readSet,
|
|
1972
1592
|
instanceName: this.instanceId
|
|
1973
|
-
})
|
|
1593
|
+
});
|
|
1594
|
+
this.cache.setQueryCacheFor(cacheKey, data, liveResult.ts).catch((err) => this.log.warn(err, "writing query cache failed"));
|
|
1974
1595
|
if (!updateResult.tracked) this.log.warn(`updateSubscriptionReadSet returned tracked:false immediately after register for ${subId} — possible sub-manager restart mid-subscribe`);
|
|
1975
1596
|
}
|
|
1976
1597
|
dataHash = await getHashOf(data);
|