@rocicorp/zero 0.25.0-canary.8 → 0.25.0-canary.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/shared/src/deep-merge.d.ts +20 -3
- package/out/shared/src/deep-merge.d.ts.map +1 -1
- package/out/shared/src/deep-merge.js +27 -0
- package/out/shared/src/deep-merge.js.map +1 -0
- package/out/shared/src/logging.d.ts.map +1 -1
- package/out/shared/src/logging.js +25 -9
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/object-traversal.d.ts +19 -0
- package/out/shared/src/object-traversal.d.ts.map +1 -0
- package/out/shared/src/object-traversal.js +27 -0
- package/out/shared/src/object-traversal.js.map +1 -0
- package/out/zero/package.json.js +1 -1
- package/out/zero/src/pg.js +0 -2
- package/out/zero/src/pg.js.map +1 -1
- package/out/zero/src/server.js +0 -2
- package/out/zero/src/server.js.map +1 -1
- package/out/zero/src/zero.js +19 -3
- package/out/zero/src/zero.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.d.ts +3 -0
- package/out/zero-cache/src/auth/jwt.d.ts.map +1 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts +2 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +1 -11
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +27 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +35 -7
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +5 -5
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +14 -11
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +2 -4
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +9 -9
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +20 -8
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +10 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +5 -5
- package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +11 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +36 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +5 -4
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +15 -10
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +2 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +3 -2
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +198 -0
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +5 -5
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts +4 -0
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +8 -1
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +15 -8
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +4 -4
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +48 -25
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +20 -15
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +3 -3
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/bindings.d.ts +4 -4
- package/out/zero-client/src/client/bindings.d.ts.map +1 -1
- package/out/zero-client/src/client/bindings.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +1 -1
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +7 -5
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js +7 -7
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.d.ts +7 -5
- package/out/zero-client/src/client/custom.d.ts.map +1 -1
- package/out/zero-client/src/client/custom.js +12 -7
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.d.ts +5 -1
- package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js +7 -0
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +13 -13
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/make-mutate-property.d.ts +43 -0
- package/out/zero-client/src/client/make-mutate-property.d.ts.map +1 -0
- package/out/zero-client/src/client/make-mutate-property.js +38 -0
- package/out/zero-client/src/client/make-mutate-property.js.map +1 -0
- package/out/zero-client/src/client/make-replicache-mutators.d.ts +34 -0
- package/out/zero-client/src/client/make-replicache-mutators.d.ts.map +1 -0
- package/out/zero-client/src/client/make-replicache-mutators.js +103 -0
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -0
- package/out/zero-client/src/client/options.d.ts +39 -27
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +23 -33
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +52 -118
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +12 -7
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.d.ts +236 -0
- package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.js +128 -2
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +1 -1
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +4 -0
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.d.ts +1 -1
- package/out/zero-protocol/src/down.d.ts +99 -0
- package/out/zero-protocol/src/down.d.ts.map +1 -1
- package/out/zero-protocol/src/error.d.ts +4 -4
- package/out/zero-protocol/src/inspect-down.d.ts +297 -0
- package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-up.d.ts +4 -0
- package/out/zero-protocol/src/inspect-up.d.ts.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +2 -1
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +1 -1
- package/out/zero-protocol/src/up.d.ts +1 -0
- package/out/zero-protocol/src/up.d.ts.map +1 -1
- package/out/zero-react/src/components/inspector.d.ts +3 -2
- package/out/zero-react/src/components/inspector.d.ts.map +1 -1
- package/out/zero-react/src/components/inspector.js.map +1 -1
- package/out/zero-react/src/components/zero-inspector.d.ts +3 -2
- package/out/zero-react/src/components/zero-inspector.d.ts.map +1 -1
- package/out/zero-react/src/components/zero-inspector.js.map +1 -1
- package/out/zero-react/src/use-query.d.ts +5 -4
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +4 -3
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts +7 -7
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-schema/src/builder/schema-builder.js +1 -1
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-server/src/custom.d.ts +4 -5
- package/out/zero-server/src/custom.d.ts.map +1 -1
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/mod.d.ts +0 -1
- package/out/zero-server/src/mod.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +9 -14
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +151 -105
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.d.ts +5 -3
- package/out/zero-server/src/push-processor.d.ts.map +1 -1
- package/out/zero-server/src/push-processor.js +17 -25
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +1 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/zql-database.d.ts.map +1 -1
- package/out/zero-server/src/zql-database.js +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/use-query.d.ts +3 -3
- package/out/zero-solid/src/use-query.d.ts.map +1 -1
- package/out/zero-solid/src/use-query.js +27 -38
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero-connection-state.d.ts.map +1 -1
- package/out/zero-solid/src/use-zero-connection-state.js +7 -5
- package/out/zero-solid/src/use-zero-connection-state.js.map +1 -1
- package/out/zero-solid/src/use-zero-online.d.ts.map +1 -1
- package/out/zero-solid/src/use-zero-online.js +7 -5
- package/out/zero-solid/src/use-zero-online.js.map +1 -1
- package/out/zero-solid/src/use-zero.d.ts +6 -5
- package/out/zero-solid/src/use-zero.d.ts.map +1 -1
- package/out/zero-solid/src/use-zero.js +2 -6
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts +2 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +4 -3
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/mutate/custom.d.ts +15 -6
- package/out/zql/src/mutate/custom.d.ts.map +1 -1
- package/out/zql/src/mutate/custom.js +6 -6
- package/out/zql/src/mutate/custom.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts +142 -0
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -0
- package/out/zql/src/mutate/mutator-registry.js +97 -0
- package/out/zql/src/mutate/mutator-registry.js.map +1 -0
- package/out/zql/src/mutate/mutator.d.ts +98 -0
- package/out/zql/src/mutate/mutator.d.ts.map +1 -0
- package/out/zql/src/mutate/mutator.js +35 -0
- package/out/zql/src/mutate/mutator.js.map +1 -0
- package/out/zql/src/planner/planner-connection.d.ts +7 -15
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +30 -24
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts +37 -43
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js +242 -0
- package/out/zql/src/planner/planner-debug.js.map +1 -0
- package/out/zql/src/planner/planner-fan-in.d.ts.map +1 -1
- package/out/zql/src/planner/planner-fan-in.js +11 -8
- package/out/zql/src/planner/planner-fan-in.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js +11 -8
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +13 -5
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.d.ts.map +1 -1
- package/out/zql/src/planner/planner-join.js +12 -9
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.d.ts +4 -0
- package/out/zql/src/planner/planner-node.d.ts.map +1 -1
- package/out/zql/src/planner/planner-node.js +8 -0
- package/out/zql/src/planner/planner-node.js.map +1 -0
- package/out/zql/src/query/create-builder.d.ts +7 -0
- package/out/zql/src/query/create-builder.d.ts.map +1 -0
- package/out/zql/src/query/create-builder.js +44 -0
- package/out/zql/src/query/create-builder.js.map +1 -0
- package/out/zql/src/query/named.d.ts +1 -7
- package/out/zql/src/query/named.d.ts.map +1 -1
- package/out/zql/src/query/named.js +0 -21
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-impl.d.ts +4 -3
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +3 -0
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.js +0 -4
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +253 -0
- package/out/zql/src/query/query-registry.d.ts.map +1 -0
- package/out/zql/src/query/query-registry.js +131 -0
- package/out/zql/src/query/query-registry.js.map +1 -0
- package/out/zql/src/query/query.d.ts +16 -1
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/schema-query.d.ts +6 -0
- package/out/zql/src/query/schema-query.d.ts.map +1 -0
- package/out/zql/src/query/validate-input.js +12 -13
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/package.json +2 -1
- package/out/zero-server/src/query-registry.d.ts +0 -10
- package/out/zero-server/src/query-registry.d.ts.map +0 -1
- package/out/zero-server/src/query-registry.js +0 -35
- package/out/zero-server/src/query-registry.js.map +0 -1
- package/out/zql/src/query/define-query.d.ts +0 -75
- package/out/zql/src/query/define-query.d.ts.map +0 -1
- package/out/zql/src/query/define-query.js +0 -47
- package/out/zql/src/query/define-query.js.map +0 -1
- package/out/zql/src/query/query-definitions.d.ts +0 -32
- package/out/zql/src/query/query-definitions.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/http-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAgB,EAAC,KAAK,eAAe,EAAC,MAAM,SAAS,CAAC;AAGtD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AAE1C,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,qBAAa,WAAY,YAAW,OAAO;;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/http-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAgB,EAAC,KAAK,eAAe,EAAC,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AAE1C,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,qBAAa,WAAY,YAAW,OAAO;;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IAGnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;gBAKtC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1D,SAAS,CAAC,QAAQ;IAClB,SAAS,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAGlC,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM;IAI/B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAmBxB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAKpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Fastify from "fastify";
|
|
2
|
+
import { promiseVoid } from "../../../shared/src/resolved-promises.js";
|
|
2
3
|
import { HeartbeatMonitor } from "./life-cycle.js";
|
|
3
4
|
import { RunningState } from "./running-state.js";
|
|
4
5
|
class HttpService {
|
|
@@ -6,7 +7,7 @@ class HttpService {
|
|
|
6
7
|
_lc;
|
|
7
8
|
#fastify;
|
|
8
9
|
#port;
|
|
9
|
-
|
|
10
|
+
_state;
|
|
10
11
|
#heartbeatMonitor;
|
|
11
12
|
#init;
|
|
12
13
|
constructor(id, lc, opts, init) {
|
|
@@ -15,23 +16,25 @@ class HttpService {
|
|
|
15
16
|
this.#fastify = Fastify();
|
|
16
17
|
this.#port = opts.port;
|
|
17
18
|
this.#init = init;
|
|
18
|
-
this
|
|
19
|
+
this._state = new RunningState(id);
|
|
19
20
|
this.#heartbeatMonitor = new HeartbeatMonitor(this._lc);
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return true;
|
|
22
|
+
// Life-cycle hooks for subclass implementations
|
|
23
|
+
_onStart() {
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
_onStop() {
|
|
26
|
+
return promiseVoid;
|
|
27
|
+
}
|
|
28
|
+
_onHeartbeat(_count) {
|
|
28
29
|
}
|
|
29
30
|
// start() is used in unit tests.
|
|
30
31
|
// run() is the lifecycle method called by the ServiceRunner.
|
|
31
32
|
async start() {
|
|
33
|
+
let heartbeats = 0;
|
|
32
34
|
this.#fastify.get("/", (_req, res) => res.send("OK"));
|
|
33
35
|
this.#fastify.get("/keepalive", ({ headers }, res) => {
|
|
34
36
|
this.#heartbeatMonitor.onHeartbeat(headers);
|
|
37
|
+
this._onHeartbeat(++heartbeats);
|
|
35
38
|
return res.send("OK");
|
|
36
39
|
});
|
|
37
40
|
await this.#init(this.#fastify);
|
|
@@ -40,17 +43,19 @@ class HttpService {
|
|
|
40
43
|
port: this.#port
|
|
41
44
|
});
|
|
42
45
|
this._lc.info?.(`${this.id} listening at ${address}`);
|
|
46
|
+
this._onStart();
|
|
43
47
|
return address;
|
|
44
48
|
}
|
|
45
49
|
async run() {
|
|
46
50
|
await this.start();
|
|
47
|
-
await this
|
|
51
|
+
await this._state.stopped();
|
|
48
52
|
}
|
|
49
53
|
async stop() {
|
|
50
54
|
this._lc.info?.(`${this.id}: no longer accepting connections`);
|
|
51
55
|
this.#heartbeatMonitor.stop();
|
|
56
|
+
this._state.stop(this._lc);
|
|
52
57
|
await this.#fastify.close();
|
|
53
|
-
this
|
|
58
|
+
await this._onStop();
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.js","sources":["../../../../../zero-cache/src/services/http-service.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport Fastify, {type FastifyInstance} from 'fastify';\nimport {HeartbeatMonitor} from './life-cycle.ts';\nimport {RunningState} from './running-state.ts';\nimport type {Service} from './service.ts';\n\nexport type Options = {\n port: number;\n};\n\n/**\n * Common functionality for all HttpServices. These include:\n * * Responding to health checks at \"/\"\n * * Tracking optional heartbeats at \"/keepalive\" and draining when they stop.\n */\nexport class HttpService implements Service {\n readonly id: string;\n protected readonly _lc: LogContext;\n readonly #fastify: FastifyInstance;\n readonly #port: number;\n readonly
|
|
1
|
+
{"version":3,"file":"http-service.js","sources":["../../../../../zero-cache/src/services/http-service.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport Fastify, {type FastifyInstance} from 'fastify';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {HeartbeatMonitor} from './life-cycle.ts';\nimport {RunningState} from './running-state.ts';\nimport type {Service} from './service.ts';\n\nexport type Options = {\n port: number;\n};\n\n/**\n * Common functionality for all HttpServices. These include:\n * * Responding to health checks at \"/\"\n * * Tracking optional heartbeats at \"/keepalive\" and draining when they stop.\n */\nexport class HttpService implements Service {\n readonly id: string;\n protected readonly _lc: LogContext;\n readonly #fastify: FastifyInstance;\n readonly #port: number;\n protected readonly _state: RunningState;\n readonly #heartbeatMonitor: HeartbeatMonitor;\n readonly #init: (fastify: FastifyInstance) => void | Promise<void>;\n\n constructor(\n id: string,\n lc: LogContext,\n opts: Options,\n init: (fastify: FastifyInstance) => void | Promise<void>,\n ) {\n this.id = id;\n this._lc = lc.withContext('component', this.id);\n this.#fastify = Fastify();\n this.#port = opts.port;\n this.#init = init;\n this._state = new RunningState(id);\n this.#heartbeatMonitor = new HeartbeatMonitor(this._lc);\n }\n\n // Life-cycle hooks for subclass implementations\n protected _onStart() {}\n protected _onStop(): Promise<void> {\n return promiseVoid;\n }\n protected _onHeartbeat(_count: number) {}\n\n // start() is used in unit tests.\n // run() is the lifecycle method called by the ServiceRunner.\n async start(): Promise<string> {\n let heartbeats = 0;\n\n this.#fastify.get('/', (_req, res) => res.send('OK'));\n this.#fastify.get('/keepalive', ({headers}, res) => {\n this.#heartbeatMonitor.onHeartbeat(headers);\n this._onHeartbeat(++heartbeats);\n return res.send('OK');\n });\n await this.#init(this.#fastify);\n const address = await this.#fastify.listen({\n host: '::',\n port: this.#port,\n });\n this._lc.info?.(`${this.id} listening at ${address}`);\n this._onStart();\n return address;\n }\n\n async run(): Promise<void> {\n await this.start();\n await this._state.stopped();\n }\n\n async stop(): Promise<void> {\n this._lc.info?.(`${this.id}: no longer accepting connections`);\n this.#heartbeatMonitor.stop();\n this._state.stop(this._lc);\n await this.#fastify.close();\n await this._onStop();\n }\n}\n"],"names":[],"mappings":";;;;AAgBO,MAAM,YAA+B;AAAA,EACjC;AAAA,EACU;AAAA,EACV;AAAA,EACA;AAAA,EACU;AAAA,EACV;AAAA,EACA;AAAA,EAET,YACE,IACA,IACA,MACA,MACA;AACA,SAAK,KAAK;AACV,SAAK,MAAM,GAAG,YAAY,aAAa,KAAK,EAAE;AAC9C,SAAK,WAAW,QAAA;AAChB,SAAK,QAAQ,KAAK;AAClB,SAAK,QAAQ;AACb,SAAK,SAAS,IAAI,aAAa,EAAE;AACjC,SAAK,oBAAoB,IAAI,iBAAiB,KAAK,GAAG;AAAA,EACxD;AAAA;AAAA,EAGU,WAAW;AAAA,EAAC;AAAA,EACZ,UAAyB;AACjC,WAAO;AAAA,EACT;AAAA,EACU,aAAa,QAAgB;AAAA,EAAC;AAAA;AAAA;AAAA,EAIxC,MAAM,QAAyB;AAC7B,QAAI,aAAa;AAEjB,SAAK,SAAS,IAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,KAAK,IAAI,CAAC;AACpD,SAAK,SAAS,IAAI,cAAc,CAAC,EAAC,QAAA,GAAU,QAAQ;AAClD,WAAK,kBAAkB,YAAY,OAAO;AAC1C,WAAK,aAAa,EAAE,UAAU;AAC9B,aAAO,IAAI,KAAK,IAAI;AAAA,IACtB,CAAC;AACD,UAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,UAAM,UAAU,MAAM,KAAK,SAAS,OAAO;AAAA,MACzC,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,IAAA,CACZ;AACD,SAAK,IAAI,OAAO,GAAG,KAAK,EAAE,iBAAiB,OAAO,EAAE;AACpD,SAAK,SAAA;AACL,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,KAAK,MAAA;AACX,UAAM,KAAK,OAAO,QAAA;AAAA,EACpB;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,IAAI,OAAO,GAAG,KAAK,EAAE,mCAAmC;AAC7D,SAAK,kBAAkB,KAAA;AACvB,SAAK,OAAO,KAAK,KAAK,GAAG;AACzB,UAAM,KAAK,SAAS,MAAA;AACpB,UAAM,KAAK,QAAA;AAAA,EACb;AACF;"}
|
|
@@ -3,6 +3,7 @@ import type { JWTPayload } from 'jose';
|
|
|
3
3
|
import postgres from 'postgres';
|
|
4
4
|
import { ErrorKind } from '../../../../zero-protocol/src/error-kind.ts';
|
|
5
5
|
import { type CRUDMutation, type InsertOp, type Mutation, type UpsertOp } from '../../../../zero-protocol/src/push.ts';
|
|
6
|
+
import type { DatabaseStorage } from '../../../../zqlite/src/database-storage.ts';
|
|
6
7
|
import { type WriteAuthorizer } from '../../auth/write-authorizer.ts';
|
|
7
8
|
import { type ZeroConfig } from '../../config/zero-config.ts';
|
|
8
9
|
import type { PostgresDB, PostgresTransaction } from '../../types/pg.ts';
|
|
@@ -18,7 +19,7 @@ export interface Mutagen extends RefCountedService {
|
|
|
18
19
|
export declare class MutagenService implements Mutagen, Service {
|
|
19
20
|
#private;
|
|
20
21
|
readonly id: string;
|
|
21
|
-
constructor(lc: LogContext, shard: ShardID, clientGroupID: string, upstream: PostgresDB, config: ZeroConfig);
|
|
22
|
+
constructor(lc: LogContext, shard: ShardID, clientGroupID: string, upstream: PostgresDB, config: ZeroConfig, writeAuthzStorage: DatabaseStorage);
|
|
22
23
|
ref(): void;
|
|
23
24
|
unref(): void;
|
|
24
25
|
hasRefs(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutagen.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/mutagen/mutagen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,MAAM,CAAC;AACrC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAGhC,OAAO,EAAC,SAAS,EAAC,MAAM,6CAA6C,CAAC;AAMtE,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,QAAQ,EACb,KAAK,QAAQ,EAEb,KAAK,QAAQ,EACd,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,6BAA6B,CAAC;AAI5D,OAAO,KAAK,EAAC,UAAU,EAAE,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAA4B,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9E,OAAO,KAAK,EAAC,iBAAiB,EAAE,OAAO,EAAC,MAAM,eAAe,CAAC;AAU9D,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,CAAC,cAAc,GAAG,SAAS,CAAC,mBAAmB;IAC9D,IAAI,EAAE,MAAM;CACb,CAAC;AAEF,MAAM,WAAW,OAAQ,SAAQ,iBAAiB;IAChD,eAAe,CACb,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,qBAAqB,EAAE,OAAO,GAC7B,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;CACvC;AAED,qBAAa,cAAe,YAAW,OAAO,EAAE,OAAO;;IACrD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAkBlB,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,UAAU,EACpB,MAAM,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"mutagen.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/mutagen/mutagen.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ class MutagenService {
|
|
|
35
35
|
"crud",
|
|
36
36
|
"Number of CRUD mutations processed"
|
|
37
37
|
);
|
|
38
|
-
constructor(lc, shard, clientGroupID, upstream, config) {
|
|
38
|
+
constructor(lc, shard, clientGroupID, upstream, config, writeAuthzStorage) {
|
|
39
39
|
this.id = clientGroupID;
|
|
40
40
|
this.#lc = lc;
|
|
41
41
|
this.#upstream = upstream;
|
|
@@ -48,7 +48,8 @@ class MutagenService {
|
|
|
48
48
|
config,
|
|
49
49
|
this.#replica,
|
|
50
50
|
shard.appID,
|
|
51
|
-
clientGroupID
|
|
51
|
+
clientGroupID,
|
|
52
|
+
writeAuthzStorage
|
|
52
53
|
);
|
|
53
54
|
if (config.perUserMutationLimit.max !== void 0) {
|
|
54
55
|
this.#limiter = new SlidingWindowLimiter(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutagen.js","sources":["../../../../../../zero-cache/src/services/mutagen/mutagen.ts"],"sourcesContent":["import {PG_SERIALIZATION_FAILURE} from '@drdgvhbh/postgres-error-codes';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {JWTPayload} from 'jose';\nimport postgres from 'postgres';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport * as MutationType from '../../../../zero-protocol/src/mutation-type-enum.ts';\nimport {\n primaryKeyValueSchema,\n type PrimaryKeyValue,\n} from '../../../../zero-protocol/src/primary-key.ts';\nimport {\n type CRUDMutation,\n type DeleteOp,\n type InsertOp,\n type Mutation,\n type UpdateOp,\n type UpsertOp,\n} from '../../../../zero-protocol/src/push.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {\n WriteAuthorizerImpl,\n type WriteAuthorizer,\n} from '../../auth/write-authorizer.ts';\nimport {type ZeroConfig} from '../../config/zero-config.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport {recordMutation} from '../../server/anonymous-otel-start.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../types/pg.ts';\nimport {throwProtocolErrorIfSchemaVersionNotSupported} from '../../types/schema-versions.ts';\nimport {appSchema, upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport {SlidingWindowLimiter} from '../limiter/sliding-window-limiter.ts';\nimport type {RefCountedService, Service} from '../service.ts';\nimport {MutationAlreadyProcessedError} from './error.ts';\nimport {\n isProtocolError,\n ProtocolError,\n} from '../../../../zero-protocol/src/error.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\n\n// An error encountered processing a mutation.\n// Returned back to application for display to user.\nexport type MutationError = [\n kind: ErrorKind.MutationFailed | ErrorKind.MutationRateLimited,\n desc: string,\n];\n\nexport interface Mutagen extends RefCountedService {\n processMutation(\n mutation: Mutation,\n authData: JWTPayload | undefined,\n schemaVersion: number | undefined,\n customMutatorsEnabled: boolean,\n ): Promise<MutationError | undefined>;\n}\n\nexport class MutagenService implements Mutagen, Service {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #upstream: PostgresDB;\n readonly #shard: ShardID;\n readonly #stopped = resolver();\n readonly #replica: Database;\n readonly #writeAuthorizer: WriteAuthorizerImpl;\n readonly #limiter: SlidingWindowLimiter | undefined;\n #refCount = 0;\n #isStopped = false;\n\n readonly #crudMutations = getOrCreateCounter(\n 'mutation',\n 'crud',\n 'Number of CRUD mutations processed',\n );\n\n constructor(\n lc: LogContext,\n shard: ShardID,\n clientGroupID: string,\n upstream: PostgresDB,\n config: ZeroConfig,\n ) {\n this.id = clientGroupID;\n this.#lc = lc;\n this.#upstream = upstream;\n this.#shard = shard;\n this.#replica = new Database(this.#lc, config.replica.file, {\n fileMustExist: true,\n });\n this.#writeAuthorizer = new WriteAuthorizerImpl(\n this.#lc,\n config,\n this.#replica,\n shard.appID,\n clientGroupID,\n );\n\n if (config.perUserMutationLimit.max !== undefined) {\n this.#limiter = new SlidingWindowLimiter(\n config.perUserMutationLimit.windowMs,\n config.perUserMutationLimit.max,\n );\n }\n }\n\n ref() {\n assert(!this.#isStopped, 'MutagenService is already stopped');\n ++this.#refCount;\n }\n\n unref() {\n assert(!this.#isStopped, 'MutagenService is already stopped');\n --this.#refCount;\n if (this.#refCount <= 0) {\n void this.stop();\n }\n }\n\n hasRefs(): boolean {\n return this.#refCount > 0;\n }\n\n processMutation(\n mutation: Mutation,\n authData: JWTPayload | undefined,\n schemaVersion: number | undefined,\n customMutatorsEnabled = false,\n ): Promise<MutationError | undefined> {\n if (this.#limiter?.canDo() === false) {\n return Promise.resolve([\n ErrorKind.MutationRateLimited,\n 'Rate limit exceeded',\n ]);\n }\n this.#crudMutations.add(1, {\n clientGroupID: this.id,\n });\n return processMutation(\n this.#lc,\n authData,\n this.#upstream,\n this.#shard,\n this.id,\n mutation,\n this.#writeAuthorizer,\n schemaVersion,\n undefined,\n customMutatorsEnabled,\n );\n }\n\n run(): Promise<void> {\n return this.#stopped.promise;\n }\n\n stop(): Promise<void> {\n if (this.#isStopped) {\n return this.#stopped.promise;\n }\n this.#writeAuthorizer.destroy();\n this.#isStopped = true;\n this.#stopped.resolve();\n return this.#stopped.promise;\n }\n}\n\nconst MAX_SERIALIZATION_ATTEMPTS = 10;\n\nexport async function processMutation(\n lc: LogContext,\n authData: JWTPayload | undefined,\n db: PostgresDB,\n shard: ShardID,\n clientGroupID: string,\n mutation: Mutation,\n writeAuthorizer: WriteAuthorizer,\n schemaVersion: number | undefined,\n onTxStart?: () => void | Promise<void>, // for testing\n customMutatorsEnabled = false,\n): Promise<MutationError | undefined> {\n assert(\n mutation.type === MutationType.CRUD,\n 'Only CRUD mutations are supported',\n );\n lc = lc.withContext('mutationID', mutation.id);\n lc = lc.withContext('processMutation');\n lc.debug?.('Process mutation start', mutation);\n\n // Record mutation processing attempt for telemetry (regardless of success/failure)\n recordMutation('crud');\n\n let result: MutationError | undefined;\n\n const start = Date.now();\n try {\n // Mutations can fail for a variety of reasons:\n //\n // - application error\n // - network/db error\n // - zero bug\n //\n // For application errors what we want is to re-run the mutation in\n // \"error mode\", which skips the actual mutation and just updates the\n // lastMutationID. Then return the error to the app.\n //\n // However, it's hard to tell the difference between application errors\n // and the other types.\n //\n // A reasonable policy ends up being to just retry every mutation once\n // in error mode. If the error mode mutation succeeds then we assume it\n // was an application error and return the error to the app. Otherwise,\n // we know it was something internal and we log it.\n //\n // This is not 100% correct - there are theoretical cases where we\n // return an internal error to the app that shouldn't have been. But it\n // would have to be a crazy coincidence: we'd have to have a network\n // error on the first attempt that resolves by the second attempt.\n //\n // One might ask why not try/catch just the calls to the mutators and\n // consider those application errors. That is actually what we do in\n // Replicache:\n //\n // https://github.com/rocicorp/todo-row-versioning/blob/9a0a79dc2d2de32c4fac61b5d1634bd9a9e66b7c/server/src/push.ts#L131\n //\n // We don't do it here because:\n //\n // 1. It's still not perfect. It's hard to isolate SQL errors in\n // mutators due to app developer mistakes from SQL errors due to\n // Zero mistakes.\n // 2. It's not possible to do this with the pg library we're using in\n // Zero anyway: https://github.com/porsager/postgres/issues/455.\n //\n // Personally I think this simple retry policy is nice.\n let errorMode = false;\n for (let i = 0; i < MAX_SERIALIZATION_ATTEMPTS; i++) {\n try {\n await db.begin(Mode.SERIALIZABLE, async tx => {\n // Simulates a concurrent request for testing. In production this is a noop.\n const done = onTxStart?.();\n try {\n return await processMutationWithTx(\n lc,\n tx,\n authData,\n shard,\n clientGroupID,\n schemaVersion,\n mutation,\n errorMode,\n writeAuthorizer,\n );\n } finally {\n await done;\n }\n });\n if (errorMode) {\n lc.debug?.('Ran mutation successfully in error mode');\n }\n break;\n } catch (e) {\n if (e instanceof MutationAlreadyProcessedError) {\n lc.debug?.(e.message);\n // Don't double-count already processed mutations, but they were counted above\n return undefined;\n }\n if (\n isProtocolError(e) &&\n !errorMode &&\n e.kind === ErrorKind.InvalidPush &&\n customMutatorsEnabled &&\n i < 2\n ) {\n // We're temporarily supporting custom mutators AND CRUD mutators at the same time.\n // This can create a lot of OOO mutation errors since we do not know when the API server\n // has applied a custom mutation before moving on to process CRUD mutations.\n // The temporary workaround (since CRUD is being deprecated) is to retry the mutation\n // after a small delay. Users are not expected to be running both CRUD and Custom mutators.\n // They should migrate completely to custom mutators.\n lc.info?.(\n 'Both CRUD and Custom mutators are being used at once. This is supported for now but IS NOT RECOMMENDED. Migrate completely to custom mutators.',\n e,\n );\n await new Promise(resolve => setTimeout(resolve, 100));\n continue;\n }\n if (isProtocolError(e) || errorMode) {\n lc.error?.('Process mutation error', e);\n throw e;\n }\n if (\n e instanceof postgres.PostgresError &&\n e.code === PG_SERIALIZATION_FAILURE\n ) {\n lc.info?.(`attempt ${i + 1}: ${String(e)}`, e);\n continue; // Retry up to MAX_SERIALIZATION_ATTEMPTS.\n }\n result = [ErrorKind.MutationFailed, String(e)];\n if (errorMode) {\n break;\n }\n lc.error?.('Got error running mutation, re-running in error mode', e);\n errorMode = true;\n i--;\n }\n }\n } finally {\n lc.debug?.('Process mutation complete in', Date.now() - start);\n }\n return result;\n}\n\nexport async function processMutationWithTx(\n lc: LogContext,\n tx: PostgresTransaction,\n authData: JWTPayload | undefined,\n shard: ShardID,\n clientGroupID: string,\n schemaVersion: number | undefined,\n mutation: CRUDMutation,\n errorMode: boolean,\n authorizer: WriteAuthorizer,\n) {\n const tasks: (() => Promise<unknown>)[] = [];\n\n async function execute(stmt: postgres.PendingQuery<postgres.Row[]>) {\n try {\n return await stmt.execute();\n } finally {\n const q = stmt as unknown as Query;\n lc.debug?.(`${q.string}: ${JSON.stringify(q.parameters)}`);\n }\n }\n\n authorizer.reloadPermissions();\n\n if (!errorMode) {\n const {ops} = mutation.args[0];\n const normalizedOps = authorizer.normalizeOps(ops);\n const [canPre, canPost] = await Promise.all([\n authorizer.canPreMutation(authData, normalizedOps),\n authorizer.canPostMutation(authData, normalizedOps),\n ]);\n if (canPre && canPost) {\n for (const op of ops) {\n switch (op.op) {\n case 'insert':\n tasks.push(() => execute(getInsertSQL(tx, op)));\n break;\n case 'upsert':\n tasks.push(() => execute(getUpsertSQL(tx, op)));\n break;\n case 'update':\n tasks.push(() => execute(getUpdateSQL(tx, op)));\n break;\n case 'delete':\n tasks.push(() => execute(getDeleteSQL(tx, op)));\n break;\n default:\n unreachable(op);\n }\n }\n }\n }\n\n // Confirm the mutation even though it may have been blocked by the authorizer.\n // Authorizer blocking a mutation is not an error but the correct result of the mutation.\n tasks.unshift(() =>\n checkSchemaVersionAndIncrementLastMutationID(\n tx,\n shard,\n clientGroupID,\n schemaVersion,\n mutation.clientID,\n mutation.id,\n ),\n );\n\n // Note: An error thrown from any Promise aborts the entire transaction.\n await Promise.all(tasks.map(task => task()));\n}\n\nexport function getInsertSQL(\n tx: postgres.TransactionSql,\n create: InsertOp,\n): postgres.PendingQuery<postgres.Row[]> {\n return tx`INSERT INTO ${tx(create.tableName)} ${tx(create.value)}`;\n}\n\nexport function getUpsertSQL(\n tx: postgres.TransactionSql,\n set: UpsertOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const {tableName, primaryKey, value} = set;\n return tx`\n INSERT INTO ${tx(tableName)} ${tx(value)}\n ON CONFLICT (${tx(primaryKey)})\n DO UPDATE SET ${tx(value)}\n `;\n}\n\nfunction getUpdateSQL(\n tx: postgres.TransactionSql,\n update: UpdateOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const table = update.tableName;\n const {primaryKey, value} = update;\n const id: Record<string, PrimaryKeyValue> = {};\n for (const key of primaryKey) {\n id[key] = v.parse(value[key], primaryKeyValueSchema);\n }\n return tx`UPDATE ${tx(table)} SET ${tx(value)} WHERE ${Object.entries(\n id,\n ).flatMap(([key, value], i) =>\n i ? [tx`AND`, tx`${tx(key)} = ${value}`] : tx`${tx(key)} = ${value}`,\n )}`;\n}\n\nfunction getDeleteSQL(\n tx: postgres.TransactionSql,\n deleteOp: DeleteOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const {tableName, primaryKey, value} = deleteOp;\n\n const conditions = [];\n for (const key of primaryKey) {\n if (conditions.length > 0) {\n conditions.push(tx`AND`);\n }\n conditions.push(tx`${tx(key)} = ${value[key]}`);\n }\n\n return tx`DELETE FROM ${tx(tableName)} WHERE ${conditions}`;\n}\n\nasync function checkSchemaVersionAndIncrementLastMutationID(\n tx: PostgresTransaction,\n shard: ShardID,\n clientGroupID: string,\n schemaVersion: number | undefined,\n clientID: string,\n receivedMutationID: number,\n) {\n const [[{lastMutationID}], supportedVersionRange] = await Promise.all([\n tx<{lastMutationID: bigint}[]>`\n INSERT INTO ${tx(upstreamSchema(shard))}.clients \n as current (\"clientGroupID\", \"clientID\", \"lastMutationID\")\n VALUES (${clientGroupID}, ${clientID}, ${1})\n ON CONFLICT (\"clientGroupID\", \"clientID\")\n DO UPDATE SET \"lastMutationID\" = current.\"lastMutationID\" + 1\n RETURNING \"lastMutationID\"\n `,\n schemaVersion === undefined\n ? undefined\n : tx<\n {\n minSupportedVersion: number;\n maxSupportedVersion: number;\n }[]\n >`SELECT \"minSupportedVersion\", \"maxSupportedVersion\" \n FROM ${tx(appSchema(shard))}.\"schemaVersions\"`,\n ]);\n\n // ABORT if the resulting lastMutationID is not equal to the receivedMutationID.\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new ProtocolError({\n kind: ErrorKind.InvalidPush,\n message: `Push contains unexpected mutation id ${receivedMutationID} for client ${clientID}. Expected mutation id ${lastMutationID.toString()}.`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n if (schemaVersion !== undefined && supportedVersionRange !== undefined) {\n assert(supportedVersionRange.length === 1);\n throwProtocolErrorIfSchemaVersionNotSupported(\n schemaVersion,\n supportedVersionRange[0],\n );\n }\n}\n\n// The slice of information from the Query object in Postgres.js that gets logged for debugging.\n// https://github.com/porsager/postgres/blob/f58cd4f3affd3e8ce8f53e42799672d86cd2c70b/src/connection.js#L219\ntype Query = {string: string; parameters: object[]};\n"],"names":["ErrorKind.MutationRateLimited","MutationType.CRUD","Mode.SERIALIZABLE","ErrorKind.InvalidPush","ErrorKind.MutationFailed","v.parse","value","ErrorOrigin.ZeroCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mutagen.js","sources":["../../../../../../zero-cache/src/services/mutagen/mutagen.ts"],"sourcesContent":["import {PG_SERIALIZATION_FAILURE} from '@drdgvhbh/postgres-error-codes';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {JWTPayload} from 'jose';\nimport postgres from 'postgres';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport * as MutationType from '../../../../zero-protocol/src/mutation-type-enum.ts';\nimport {\n primaryKeyValueSchema,\n type PrimaryKeyValue,\n} from '../../../../zero-protocol/src/primary-key.ts';\nimport {\n type CRUDMutation,\n type DeleteOp,\n type InsertOp,\n type Mutation,\n type UpdateOp,\n type UpsertOp,\n} from '../../../../zero-protocol/src/push.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport type {DatabaseStorage} from '../../../../zqlite/src/database-storage.ts';\nimport {\n WriteAuthorizerImpl,\n type WriteAuthorizer,\n} from '../../auth/write-authorizer.ts';\nimport {type ZeroConfig} from '../../config/zero-config.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport {recordMutation} from '../../server/anonymous-otel-start.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../types/pg.ts';\nimport {throwProtocolErrorIfSchemaVersionNotSupported} from '../../types/schema-versions.ts';\nimport {appSchema, upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport {SlidingWindowLimiter} from '../limiter/sliding-window-limiter.ts';\nimport type {RefCountedService, Service} from '../service.ts';\nimport {MutationAlreadyProcessedError} from './error.ts';\nimport {\n isProtocolError,\n ProtocolError,\n} from '../../../../zero-protocol/src/error.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\n\n// An error encountered processing a mutation.\n// Returned back to application for display to user.\nexport type MutationError = [\n kind: ErrorKind.MutationFailed | ErrorKind.MutationRateLimited,\n desc: string,\n];\n\nexport interface Mutagen extends RefCountedService {\n processMutation(\n mutation: Mutation,\n authData: JWTPayload | undefined,\n schemaVersion: number | undefined,\n customMutatorsEnabled: boolean,\n ): Promise<MutationError | undefined>;\n}\n\nexport class MutagenService implements Mutagen, Service {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #upstream: PostgresDB;\n readonly #shard: ShardID;\n readonly #stopped = resolver();\n readonly #replica: Database;\n readonly #writeAuthorizer: WriteAuthorizerImpl;\n readonly #limiter: SlidingWindowLimiter | undefined;\n #refCount = 0;\n #isStopped = false;\n\n readonly #crudMutations = getOrCreateCounter(\n 'mutation',\n 'crud',\n 'Number of CRUD mutations processed',\n );\n\n constructor(\n lc: LogContext,\n shard: ShardID,\n clientGroupID: string,\n upstream: PostgresDB,\n config: ZeroConfig,\n writeAuthzStorage: DatabaseStorage,\n ) {\n this.id = clientGroupID;\n this.#lc = lc;\n this.#upstream = upstream;\n this.#shard = shard;\n this.#replica = new Database(this.#lc, config.replica.file, {\n fileMustExist: true,\n });\n this.#writeAuthorizer = new WriteAuthorizerImpl(\n this.#lc,\n config,\n this.#replica,\n shard.appID,\n clientGroupID,\n writeAuthzStorage,\n );\n\n if (config.perUserMutationLimit.max !== undefined) {\n this.#limiter = new SlidingWindowLimiter(\n config.perUserMutationLimit.windowMs,\n config.perUserMutationLimit.max,\n );\n }\n }\n\n ref() {\n assert(!this.#isStopped, 'MutagenService is already stopped');\n ++this.#refCount;\n }\n\n unref() {\n assert(!this.#isStopped, 'MutagenService is already stopped');\n --this.#refCount;\n if (this.#refCount <= 0) {\n void this.stop();\n }\n }\n\n hasRefs(): boolean {\n return this.#refCount > 0;\n }\n\n processMutation(\n mutation: Mutation,\n authData: JWTPayload | undefined,\n schemaVersion: number | undefined,\n customMutatorsEnabled = false,\n ): Promise<MutationError | undefined> {\n if (this.#limiter?.canDo() === false) {\n return Promise.resolve([\n ErrorKind.MutationRateLimited,\n 'Rate limit exceeded',\n ]);\n }\n this.#crudMutations.add(1, {\n clientGroupID: this.id,\n });\n return processMutation(\n this.#lc,\n authData,\n this.#upstream,\n this.#shard,\n this.id,\n mutation,\n this.#writeAuthorizer,\n schemaVersion,\n undefined,\n customMutatorsEnabled,\n );\n }\n\n run(): Promise<void> {\n return this.#stopped.promise;\n }\n\n stop(): Promise<void> {\n if (this.#isStopped) {\n return this.#stopped.promise;\n }\n this.#writeAuthorizer.destroy();\n this.#isStopped = true;\n this.#stopped.resolve();\n return this.#stopped.promise;\n }\n}\n\nconst MAX_SERIALIZATION_ATTEMPTS = 10;\n\nexport async function processMutation(\n lc: LogContext,\n authData: JWTPayload | undefined,\n db: PostgresDB,\n shard: ShardID,\n clientGroupID: string,\n mutation: Mutation,\n writeAuthorizer: WriteAuthorizer,\n schemaVersion: number | undefined,\n onTxStart?: () => void | Promise<void>, // for testing\n customMutatorsEnabled = false,\n): Promise<MutationError | undefined> {\n assert(\n mutation.type === MutationType.CRUD,\n 'Only CRUD mutations are supported',\n );\n lc = lc.withContext('mutationID', mutation.id);\n lc = lc.withContext('processMutation');\n lc.debug?.('Process mutation start', mutation);\n\n // Record mutation processing attempt for telemetry (regardless of success/failure)\n recordMutation('crud');\n\n let result: MutationError | undefined;\n\n const start = Date.now();\n try {\n // Mutations can fail for a variety of reasons:\n //\n // - application error\n // - network/db error\n // - zero bug\n //\n // For application errors what we want is to re-run the mutation in\n // \"error mode\", which skips the actual mutation and just updates the\n // lastMutationID. Then return the error to the app.\n //\n // However, it's hard to tell the difference between application errors\n // and the other types.\n //\n // A reasonable policy ends up being to just retry every mutation once\n // in error mode. If the error mode mutation succeeds then we assume it\n // was an application error and return the error to the app. Otherwise,\n // we know it was something internal and we log it.\n //\n // This is not 100% correct - there are theoretical cases where we\n // return an internal error to the app that shouldn't have been. But it\n // would have to be a crazy coincidence: we'd have to have a network\n // error on the first attempt that resolves by the second attempt.\n //\n // One might ask why not try/catch just the calls to the mutators and\n // consider those application errors. That is actually what we do in\n // Replicache:\n //\n // https://github.com/rocicorp/todo-row-versioning/blob/9a0a79dc2d2de32c4fac61b5d1634bd9a9e66b7c/server/src/push.ts#L131\n //\n // We don't do it here because:\n //\n // 1. It's still not perfect. It's hard to isolate SQL errors in\n // mutators due to app developer mistakes from SQL errors due to\n // Zero mistakes.\n // 2. It's not possible to do this with the pg library we're using in\n // Zero anyway: https://github.com/porsager/postgres/issues/455.\n //\n // Personally I think this simple retry policy is nice.\n let errorMode = false;\n for (let i = 0; i < MAX_SERIALIZATION_ATTEMPTS; i++) {\n try {\n await db.begin(Mode.SERIALIZABLE, async tx => {\n // Simulates a concurrent request for testing. In production this is a noop.\n const done = onTxStart?.();\n try {\n return await processMutationWithTx(\n lc,\n tx,\n authData,\n shard,\n clientGroupID,\n schemaVersion,\n mutation,\n errorMode,\n writeAuthorizer,\n );\n } finally {\n await done;\n }\n });\n if (errorMode) {\n lc.debug?.('Ran mutation successfully in error mode');\n }\n break;\n } catch (e) {\n if (e instanceof MutationAlreadyProcessedError) {\n lc.debug?.(e.message);\n // Don't double-count already processed mutations, but they were counted above\n return undefined;\n }\n if (\n isProtocolError(e) &&\n !errorMode &&\n e.kind === ErrorKind.InvalidPush &&\n customMutatorsEnabled &&\n i < 2\n ) {\n // We're temporarily supporting custom mutators AND CRUD mutators at the same time.\n // This can create a lot of OOO mutation errors since we do not know when the API server\n // has applied a custom mutation before moving on to process CRUD mutations.\n // The temporary workaround (since CRUD is being deprecated) is to retry the mutation\n // after a small delay. Users are not expected to be running both CRUD and Custom mutators.\n // They should migrate completely to custom mutators.\n lc.info?.(\n 'Both CRUD and Custom mutators are being used at once. This is supported for now but IS NOT RECOMMENDED. Migrate completely to custom mutators.',\n e,\n );\n await new Promise(resolve => setTimeout(resolve, 100));\n continue;\n }\n if (isProtocolError(e) || errorMode) {\n lc.error?.('Process mutation error', e);\n throw e;\n }\n if (\n e instanceof postgres.PostgresError &&\n e.code === PG_SERIALIZATION_FAILURE\n ) {\n lc.info?.(`attempt ${i + 1}: ${String(e)}`, e);\n continue; // Retry up to MAX_SERIALIZATION_ATTEMPTS.\n }\n result = [ErrorKind.MutationFailed, String(e)];\n if (errorMode) {\n break;\n }\n lc.error?.('Got error running mutation, re-running in error mode', e);\n errorMode = true;\n i--;\n }\n }\n } finally {\n lc.debug?.('Process mutation complete in', Date.now() - start);\n }\n return result;\n}\n\nexport async function processMutationWithTx(\n lc: LogContext,\n tx: PostgresTransaction,\n authData: JWTPayload | undefined,\n shard: ShardID,\n clientGroupID: string,\n schemaVersion: number | undefined,\n mutation: CRUDMutation,\n errorMode: boolean,\n authorizer: WriteAuthorizer,\n) {\n const tasks: (() => Promise<unknown>)[] = [];\n\n async function execute(stmt: postgres.PendingQuery<postgres.Row[]>) {\n try {\n return await stmt.execute();\n } finally {\n const q = stmt as unknown as Query;\n lc.debug?.(`${q.string}: ${JSON.stringify(q.parameters)}`);\n }\n }\n\n authorizer.reloadPermissions();\n\n if (!errorMode) {\n const {ops} = mutation.args[0];\n const normalizedOps = authorizer.normalizeOps(ops);\n const [canPre, canPost] = await Promise.all([\n authorizer.canPreMutation(authData, normalizedOps),\n authorizer.canPostMutation(authData, normalizedOps),\n ]);\n if (canPre && canPost) {\n for (const op of ops) {\n switch (op.op) {\n case 'insert':\n tasks.push(() => execute(getInsertSQL(tx, op)));\n break;\n case 'upsert':\n tasks.push(() => execute(getUpsertSQL(tx, op)));\n break;\n case 'update':\n tasks.push(() => execute(getUpdateSQL(tx, op)));\n break;\n case 'delete':\n tasks.push(() => execute(getDeleteSQL(tx, op)));\n break;\n default:\n unreachable(op);\n }\n }\n }\n }\n\n // Confirm the mutation even though it may have been blocked by the authorizer.\n // Authorizer blocking a mutation is not an error but the correct result of the mutation.\n tasks.unshift(() =>\n checkSchemaVersionAndIncrementLastMutationID(\n tx,\n shard,\n clientGroupID,\n schemaVersion,\n mutation.clientID,\n mutation.id,\n ),\n );\n\n // Note: An error thrown from any Promise aborts the entire transaction.\n await Promise.all(tasks.map(task => task()));\n}\n\nexport function getInsertSQL(\n tx: postgres.TransactionSql,\n create: InsertOp,\n): postgres.PendingQuery<postgres.Row[]> {\n return tx`INSERT INTO ${tx(create.tableName)} ${tx(create.value)}`;\n}\n\nexport function getUpsertSQL(\n tx: postgres.TransactionSql,\n set: UpsertOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const {tableName, primaryKey, value} = set;\n return tx`\n INSERT INTO ${tx(tableName)} ${tx(value)}\n ON CONFLICT (${tx(primaryKey)})\n DO UPDATE SET ${tx(value)}\n `;\n}\n\nfunction getUpdateSQL(\n tx: postgres.TransactionSql,\n update: UpdateOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const table = update.tableName;\n const {primaryKey, value} = update;\n const id: Record<string, PrimaryKeyValue> = {};\n for (const key of primaryKey) {\n id[key] = v.parse(value[key], primaryKeyValueSchema);\n }\n return tx`UPDATE ${tx(table)} SET ${tx(value)} WHERE ${Object.entries(\n id,\n ).flatMap(([key, value], i) =>\n i ? [tx`AND`, tx`${tx(key)} = ${value}`] : tx`${tx(key)} = ${value}`,\n )}`;\n}\n\nfunction getDeleteSQL(\n tx: postgres.TransactionSql,\n deleteOp: DeleteOp,\n): postgres.PendingQuery<postgres.Row[]> {\n const {tableName, primaryKey, value} = deleteOp;\n\n const conditions = [];\n for (const key of primaryKey) {\n if (conditions.length > 0) {\n conditions.push(tx`AND`);\n }\n conditions.push(tx`${tx(key)} = ${value[key]}`);\n }\n\n return tx`DELETE FROM ${tx(tableName)} WHERE ${conditions}`;\n}\n\nasync function checkSchemaVersionAndIncrementLastMutationID(\n tx: PostgresTransaction,\n shard: ShardID,\n clientGroupID: string,\n schemaVersion: number | undefined,\n clientID: string,\n receivedMutationID: number,\n) {\n const [[{lastMutationID}], supportedVersionRange] = await Promise.all([\n tx<{lastMutationID: bigint}[]>`\n INSERT INTO ${tx(upstreamSchema(shard))}.clients \n as current (\"clientGroupID\", \"clientID\", \"lastMutationID\")\n VALUES (${clientGroupID}, ${clientID}, ${1})\n ON CONFLICT (\"clientGroupID\", \"clientID\")\n DO UPDATE SET \"lastMutationID\" = current.\"lastMutationID\" + 1\n RETURNING \"lastMutationID\"\n `,\n schemaVersion === undefined\n ? undefined\n : tx<\n {\n minSupportedVersion: number;\n maxSupportedVersion: number;\n }[]\n >`SELECT \"minSupportedVersion\", \"maxSupportedVersion\" \n FROM ${tx(appSchema(shard))}.\"schemaVersions\"`,\n ]);\n\n // ABORT if the resulting lastMutationID is not equal to the receivedMutationID.\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new ProtocolError({\n kind: ErrorKind.InvalidPush,\n message: `Push contains unexpected mutation id ${receivedMutationID} for client ${clientID}. Expected mutation id ${lastMutationID.toString()}.`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n\n if (schemaVersion !== undefined && supportedVersionRange !== undefined) {\n assert(supportedVersionRange.length === 1);\n throwProtocolErrorIfSchemaVersionNotSupported(\n schemaVersion,\n supportedVersionRange[0],\n );\n }\n}\n\n// The slice of information from the Query object in Postgres.js that gets logged for debugging.\n// https://github.com/porsager/postgres/blob/f58cd4f3affd3e8ce8f53e42799672d86cd2c70b/src/connection.js#L219\ntype Query = {string: string; parameters: object[]};\n"],"names":["ErrorKind.MutationRateLimited","MutationType.CRUD","Mode.SERIALIZABLE","ErrorKind.InvalidPush","ErrorKind.MutationFailed","v.parse","value","ErrorOrigin.ZeroCache"],"mappings":"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"}
|
|
@@ -140,6 +140,105 @@ export declare class PusherService implements Service, Pusher {
|
|
|
140
140
|
readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
|
|
141
141
|
readRowCount?: number | undefined;
|
|
142
142
|
dbScansByQuery?: Record<string, Record<string, number>> | undefined;
|
|
143
|
+
plannerEvents?: ({
|
|
144
|
+
type: "attempt-start";
|
|
145
|
+
attemptNumber: number;
|
|
146
|
+
totalAttempts: number;
|
|
147
|
+
} | {
|
|
148
|
+
type: "connection-costs";
|
|
149
|
+
attemptNumber: number;
|
|
150
|
+
costs: {
|
|
151
|
+
connection: string;
|
|
152
|
+
cost: number;
|
|
153
|
+
costEstimate: {
|
|
154
|
+
limit?: number | undefined;
|
|
155
|
+
startupCost: number;
|
|
156
|
+
scanEst: number;
|
|
157
|
+
cost: number;
|
|
158
|
+
returnedRows: number;
|
|
159
|
+
selectivity: number;
|
|
160
|
+
};
|
|
161
|
+
pinned: boolean;
|
|
162
|
+
constraints: Record<string, Record<string, unknown> | null>;
|
|
163
|
+
constraintCosts: Record<string, {
|
|
164
|
+
limit?: number | undefined;
|
|
165
|
+
startupCost: number;
|
|
166
|
+
scanEst: number;
|
|
167
|
+
cost: number;
|
|
168
|
+
returnedRows: number;
|
|
169
|
+
selectivity: number;
|
|
170
|
+
}>;
|
|
171
|
+
}[];
|
|
172
|
+
} | {
|
|
173
|
+
type: "connection-selected";
|
|
174
|
+
attemptNumber: number;
|
|
175
|
+
connection: string;
|
|
176
|
+
cost: number;
|
|
177
|
+
isRoot: boolean;
|
|
178
|
+
} | {
|
|
179
|
+
type: "constraints-propagated";
|
|
180
|
+
attemptNumber: number;
|
|
181
|
+
connectionConstraints: {
|
|
182
|
+
connection: string;
|
|
183
|
+
constraints: Record<string, Record<string, unknown> | null>;
|
|
184
|
+
constraintCosts: Record<string, {
|
|
185
|
+
limit?: number | undefined;
|
|
186
|
+
startupCost: number;
|
|
187
|
+
scanEst: number;
|
|
188
|
+
cost: number;
|
|
189
|
+
returnedRows: number;
|
|
190
|
+
selectivity: number;
|
|
191
|
+
}>;
|
|
192
|
+
}[];
|
|
193
|
+
} | {
|
|
194
|
+
type: "plan-complete";
|
|
195
|
+
attemptNumber: number;
|
|
196
|
+
totalCost: number;
|
|
197
|
+
flipPattern: number;
|
|
198
|
+
joinStates: {
|
|
199
|
+
join: string;
|
|
200
|
+
type: "semi" | "flipped" | "unflippable";
|
|
201
|
+
}[];
|
|
202
|
+
} | {
|
|
203
|
+
type: "plan-failed";
|
|
204
|
+
attemptNumber: number;
|
|
205
|
+
reason: string;
|
|
206
|
+
} | {
|
|
207
|
+
type: "best-plan-selected";
|
|
208
|
+
bestAttemptNumber: number;
|
|
209
|
+
totalCost: number;
|
|
210
|
+
flipPattern: number;
|
|
211
|
+
joinStates: {
|
|
212
|
+
join: string;
|
|
213
|
+
type: "semi" | "flipped" | "unflippable";
|
|
214
|
+
}[];
|
|
215
|
+
} | {
|
|
216
|
+
attemptNumber?: number | undefined;
|
|
217
|
+
filters?: import("../../../../zero-protocol/src/ast.ts").Condition | undefined;
|
|
218
|
+
ordering?: readonly (readonly [string, "desc" | "asc"])[] | undefined;
|
|
219
|
+
joinType?: "semi" | "flipped" | "unflippable" | undefined;
|
|
220
|
+
type: "node-cost";
|
|
221
|
+
nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
|
|
222
|
+
node: string;
|
|
223
|
+
branchPattern: number[];
|
|
224
|
+
downstreamChildSelectivity: number;
|
|
225
|
+
costEstimate: {
|
|
226
|
+
limit?: number | undefined;
|
|
227
|
+
startupCost: number;
|
|
228
|
+
scanEst: number;
|
|
229
|
+
cost: number;
|
|
230
|
+
returnedRows: number;
|
|
231
|
+
selectivity: number;
|
|
232
|
+
};
|
|
233
|
+
} | {
|
|
234
|
+
attemptNumber?: number | undefined;
|
|
235
|
+
constraint?: Record<string, unknown> | null | undefined;
|
|
236
|
+
type: "node-constraint";
|
|
237
|
+
nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
|
|
238
|
+
node: string;
|
|
239
|
+
branchPattern: number[];
|
|
240
|
+
from: string;
|
|
241
|
+
})[] | undefined;
|
|
143
242
|
warnings: string[];
|
|
144
243
|
syncedRowCount: number;
|
|
145
244
|
start: number;
|
|
@@ -340,6 +439,105 @@ export declare class PusherService implements Service, Pusher {
|
|
|
340
439
|
readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
|
|
341
440
|
readRowCount?: number | undefined;
|
|
342
441
|
dbScansByQuery?: Record<string, Record<string, number>> | undefined;
|
|
442
|
+
plannerEvents?: ({
|
|
443
|
+
type: "attempt-start";
|
|
444
|
+
attemptNumber: number;
|
|
445
|
+
totalAttempts: number;
|
|
446
|
+
} | {
|
|
447
|
+
type: "connection-costs";
|
|
448
|
+
attemptNumber: number;
|
|
449
|
+
costs: {
|
|
450
|
+
connection: string;
|
|
451
|
+
cost: number;
|
|
452
|
+
costEstimate: {
|
|
453
|
+
limit?: number | undefined;
|
|
454
|
+
startupCost: number;
|
|
455
|
+
scanEst: number;
|
|
456
|
+
cost: number;
|
|
457
|
+
returnedRows: number;
|
|
458
|
+
selectivity: number;
|
|
459
|
+
};
|
|
460
|
+
pinned: boolean;
|
|
461
|
+
constraints: Record<string, Record<string, unknown> | null>;
|
|
462
|
+
constraintCosts: Record<string, {
|
|
463
|
+
limit?: number | undefined;
|
|
464
|
+
startupCost: number;
|
|
465
|
+
scanEst: number;
|
|
466
|
+
cost: number;
|
|
467
|
+
returnedRows: number;
|
|
468
|
+
selectivity: number;
|
|
469
|
+
}>;
|
|
470
|
+
}[];
|
|
471
|
+
} | {
|
|
472
|
+
type: "connection-selected";
|
|
473
|
+
attemptNumber: number;
|
|
474
|
+
connection: string;
|
|
475
|
+
cost: number;
|
|
476
|
+
isRoot: boolean;
|
|
477
|
+
} | {
|
|
478
|
+
type: "constraints-propagated";
|
|
479
|
+
attemptNumber: number;
|
|
480
|
+
connectionConstraints: {
|
|
481
|
+
connection: string;
|
|
482
|
+
constraints: Record<string, Record<string, unknown> | null>;
|
|
483
|
+
constraintCosts: Record<string, {
|
|
484
|
+
limit?: number | undefined;
|
|
485
|
+
startupCost: number;
|
|
486
|
+
scanEst: number;
|
|
487
|
+
cost: number;
|
|
488
|
+
returnedRows: number;
|
|
489
|
+
selectivity: number;
|
|
490
|
+
}>;
|
|
491
|
+
}[];
|
|
492
|
+
} | {
|
|
493
|
+
type: "plan-complete";
|
|
494
|
+
attemptNumber: number;
|
|
495
|
+
totalCost: number;
|
|
496
|
+
flipPattern: number;
|
|
497
|
+
joinStates: {
|
|
498
|
+
join: string;
|
|
499
|
+
type: "semi" | "flipped" | "unflippable";
|
|
500
|
+
}[];
|
|
501
|
+
} | {
|
|
502
|
+
type: "plan-failed";
|
|
503
|
+
attemptNumber: number;
|
|
504
|
+
reason: string;
|
|
505
|
+
} | {
|
|
506
|
+
type: "best-plan-selected";
|
|
507
|
+
bestAttemptNumber: number;
|
|
508
|
+
totalCost: number;
|
|
509
|
+
flipPattern: number;
|
|
510
|
+
joinStates: {
|
|
511
|
+
join: string;
|
|
512
|
+
type: "semi" | "flipped" | "unflippable";
|
|
513
|
+
}[];
|
|
514
|
+
} | {
|
|
515
|
+
attemptNumber?: number | undefined;
|
|
516
|
+
filters?: import("../../../../zero-protocol/src/ast.ts").Condition | undefined;
|
|
517
|
+
ordering?: readonly (readonly [string, "desc" | "asc"])[] | undefined;
|
|
518
|
+
joinType?: "semi" | "flipped" | "unflippable" | undefined;
|
|
519
|
+
type: "node-cost";
|
|
520
|
+
nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
|
|
521
|
+
node: string;
|
|
522
|
+
branchPattern: number[];
|
|
523
|
+
downstreamChildSelectivity: number;
|
|
524
|
+
costEstimate: {
|
|
525
|
+
limit?: number | undefined;
|
|
526
|
+
startupCost: number;
|
|
527
|
+
scanEst: number;
|
|
528
|
+
cost: number;
|
|
529
|
+
returnedRows: number;
|
|
530
|
+
selectivity: number;
|
|
531
|
+
};
|
|
532
|
+
} | {
|
|
533
|
+
attemptNumber?: number | undefined;
|
|
534
|
+
constraint?: Record<string, unknown> | null | undefined;
|
|
535
|
+
type: "node-constraint";
|
|
536
|
+
nodeType: "join" | "connection" | "fan-out" | "fan-in" | "terminus";
|
|
537
|
+
node: string;
|
|
538
|
+
branchPattern: number[];
|
|
539
|
+
from: string;
|
|
540
|
+
})[] | undefined;
|
|
343
541
|
warnings: string[];
|
|
344
542
|
syncedRowCount: number;
|
|
345
543
|
start: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pusher.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/mutagen/pusher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uCAAuC,CAAC;AAQtE,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,QAAQ,EAEd,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,6BAA6B,CAAC;AAK5D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAC,iBAAiB,EAAE,OAAO,EAAC,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,MAAO,SAAQ,iBAAiB;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAErC,cAAc,CACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,CAAC,UAAU,CAAC,CAAC;IACtB,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,aAAa,CAAC;IACjB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,OAAO,EAAE,MAAM;;IACnD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAWlB,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG;QAAC,GAAG,EAAE,MAAM,EAAE,CAAA;KAAC,EAChD,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,MAAM;IAgBvB,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;IAED,cAAc,CACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,SAAS
|
|
1
|
+
{"version":3,"file":"pusher.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/mutagen/pusher.ts"],"names":[],"mappings":"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"}
|
|
@@ -137,7 +137,7 @@ class PushWorker {
|
|
|
137
137
|
this.#userPushURL = userPushURL;
|
|
138
138
|
} else {
|
|
139
139
|
if (this.#userPushURL !== userPushURL) {
|
|
140
|
-
this.#lc.
|
|
140
|
+
this.#lc.warn?.(
|
|
141
141
|
"Client provided different mutate parameters than client group",
|
|
142
142
|
{
|
|
143
143
|
clientID,
|
|
@@ -178,7 +178,7 @@ class PushWorker {
|
|
|
178
178
|
const connectionTerminations = [];
|
|
179
179
|
if ("kind" in response || "error" in response) {
|
|
180
180
|
this.#lc.warn?.(
|
|
181
|
-
"The server behind
|
|
181
|
+
"The server behind ZERO_MUTATE_URL returned a push error.",
|
|
182
182
|
response
|
|
183
183
|
);
|
|
184
184
|
const groupedMutationIDs = groupBy(
|
|
@@ -232,7 +232,7 @@ class PushWorker {
|
|
|
232
232
|
const m = mutations[i];
|
|
233
233
|
if ("error" in m.result) {
|
|
234
234
|
this.#lc.warn?.(
|
|
235
|
-
"The server behind
|
|
235
|
+
"The server behind ZERO_MUTATE_URL returned a mutation error.",
|
|
236
236
|
m.result
|
|
237
237
|
);
|
|
238
238
|
}
|
|
@@ -252,7 +252,7 @@ class PushWorker {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
if (failure && i < mutations.length - 1) {
|
|
255
|
-
this.#lc.
|
|
255
|
+
this.#lc.warn?.(
|
|
256
256
|
"push-response contains mutations after a mutation which should fatal the connection"
|
|
257
257
|
);
|
|
258
258
|
}
|
|
@@ -292,6 +292,7 @@ class PushWorker {
|
|
|
292
292
|
"push",
|
|
293
293
|
this.#lc,
|
|
294
294
|
url,
|
|
295
|
+
url === this.#userPushURL,
|
|
295
296
|
this.#pushURLPatterns,
|
|
296
297
|
{
|
|
297
298
|
appID: this.#config.app.id,
|
|
@@ -305,7 +306,6 @@ class PushWorker {
|
|
|
305
306
|
entry.push
|
|
306
307
|
);
|
|
307
308
|
} catch (e) {
|
|
308
|
-
this.#lc.error?.("failed to push", e);
|
|
309
309
|
if (isProtocolError(e) && e.errorBody.kind === PushFailed) {
|
|
310
310
|
return {
|
|
311
311
|
...e.errorBody,
|