@rikalabs/akter 0.1.0-alpha.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/CHANGELOG.md +15 -0
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +116 -0
- package/dist/actor/codecs.d.ts +77 -0
- package/dist/actor/codecs.js +87 -0
- package/dist/actor/connections.d.ts +34 -0
- package/dist/actor/connections.js +145 -0
- package/dist/actor/definition.d.ts +893 -0
- package/dist/actor/definition.js +147 -0
- package/dist/actor/descriptor.d.ts +172 -0
- package/dist/actor/descriptor.js +367 -0
- package/dist/actor/handles.d.ts +77 -0
- package/dist/actor/handles.js +265 -0
- package/dist/actor/jobs.d.ts +10 -0
- package/dist/actor/jobs.js +137 -0
- package/dist/actor/layers.d.ts +41 -0
- package/dist/actor/layers.js +177 -0
- package/dist/actor/reads.d.ts +47 -0
- package/dist/actor/reads.js +224 -0
- package/dist/actor/served.d.ts +120 -0
- package/dist/actor/served.js +72 -0
- package/dist/actor/turns.d.ts +13 -0
- package/dist/actor/turns.js +332 -0
- package/dist/actor/workflows.d.ts +16 -0
- package/dist/actor/workflows.js +37 -0
- package/dist/client/calls.d.ts +47 -0
- package/dist/client/calls.js +425 -0
- package/dist/client/clock.d.ts +56 -0
- package/dist/client/clock.js +119 -0
- package/dist/client/fleet-page.d.ts +15 -0
- package/dist/client/fleet-page.js +13 -0
- package/dist/client/fleet.d.ts +29 -0
- package/dist/client/fleet.js +51 -0
- package/dist/client/index.d.ts +17 -0
- package/dist/client/index.js +6 -0
- package/dist/client/make.d.ts +157 -0
- package/dist/client/make.js +221 -0
- package/dist/client/offline/queue.d.ts +118 -0
- package/dist/client/offline/queue.js +292 -0
- package/dist/client/offline/store.d.ts +74 -0
- package/dist/client/offline/store.js +78 -0
- package/dist/client/optimistic.d.ts +57 -0
- package/dist/client/optimistic.js +136 -0
- package/dist/client/report.d.ts +2 -0
- package/dist/client/report.js +8 -0
- package/dist/client/sessions/connection.d.ts +118 -0
- package/dist/client/sessions/connection.js +162 -0
- package/dist/client/sessions/feed.d.ts +42 -0
- package/dist/client/sessions/feed.js +43 -0
- package/dist/client/sessions/sse.d.ts +47 -0
- package/dist/client/sessions/sse.js +112 -0
- package/dist/client/sessions/stream.d.ts +28 -0
- package/dist/client/sessions/stream.js +35 -0
- package/dist/client/sessions/watch.d.ts +34 -0
- package/dist/client/sessions/watch.js +40 -0
- package/dist/client/transport.d.ts +37 -0
- package/dist/client/transport.js +66 -0
- package/dist/contexts/command.d.ts +132 -0
- package/dist/contexts/command.js +20 -0
- package/dist/contexts/connection.d.ts +68 -0
- package/dist/contexts/connection.js +0 -0
- package/dist/contexts/job.d.ts +49 -0
- package/dist/contexts/job.js +0 -0
- package/dist/contexts/workflow.d.ts +147 -0
- package/dist/contexts/workflow.js +22 -0
- package/dist/errors/actor.d.ts +200 -0
- package/dist/errors/actor.js +271 -0
- package/dist/errors/content.d.ts +19 -0
- package/dist/errors/content.js +14 -0
- package/dist/errors/database.d.ts +22 -0
- package/dist/errors/database.js +23 -0
- package/dist/errors/events.d.ts +17 -0
- package/dist/errors/events.js +14 -0
- package/dist/errors/subscription.d.ts +12 -0
- package/dist/errors/subscription.js +8 -0
- package/dist/errors/workflow.d.ts +28 -0
- package/dist/errors/workflow.js +17 -0
- package/dist/handles/actors.d.ts +17 -0
- package/dist/handles/actors.js +7 -0
- package/dist/handles/content.d.ts +59 -0
- package/dist/handles/content.js +36 -0
- package/dist/handles/intents.d.ts +164 -0
- package/dist/handles/intents.js +214 -0
- package/dist/handles/run.d.ts +31 -0
- package/dist/handles/run.js +21 -0
- package/dist/handles/workflow.d.ts +27 -0
- package/dist/handles/workflow.js +23 -0
- package/dist/identity/caller.d.ts +105 -0
- package/dist/identity/caller.js +68 -0
- package/dist/identity/child.d.ts +18 -0
- package/dist/identity/child.js +39 -0
- package/dist/identity/command.d.ts +19 -0
- package/dist/identity/command.js +21 -0
- package/dist/identity/connection.d.ts +24 -0
- package/dist/identity/connection.js +29 -0
- package/dist/identity/content.d.ts +18 -0
- package/dist/identity/content.js +12 -0
- package/dist/identity/digest.d.ts +4 -0
- package/dist/identity/digest.js +5 -0
- package/dist/identity/execution.d.ts +32 -0
- package/dist/identity/execution.js +63 -0
- package/dist/identity/mint.d.ts +51 -0
- package/dist/identity/mint.js +76 -0
- package/dist/identity/version.d.ts +2 -0
- package/dist/identity/version.js +3 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +97 -0
- package/dist/members/blob.d.ts +43 -0
- package/dist/members/blob.js +29 -0
- package/dist/members/command.d.ts +109 -0
- package/dist/members/command.js +61 -0
- package/dist/members/connection.d.ts +55 -0
- package/dist/members/connection.js +49 -0
- package/dist/members/event.d.ts +36 -0
- package/dist/members/event.js +24 -0
- package/dist/members/job.d.ts +157 -0
- package/dist/members/job.js +67 -0
- package/dist/members/payload.d.ts +93 -0
- package/dist/members/payload.js +145 -0
- package/dist/members/reducer.d.ts +64 -0
- package/dist/members/reducer.js +27 -0
- package/dist/members/stream.d.ts +36 -0
- package/dist/members/stream.js +14 -0
- package/dist/members/subscription.d.ts +182 -0
- package/dist/members/subscription.js +91 -0
- package/dist/members/workflow.d.ts +99 -0
- package/dist/members/workflow.js +2 -0
- package/dist/policies/access.d.ts +35 -0
- package/dist/policies/access.js +6 -0
- package/dist/policies/command.d.ts +127 -0
- package/dist/policies/command.js +45 -0
- package/dist/policies/job.d.ts +28 -0
- package/dist/policies/job.js +60 -0
- package/dist/policies/schedules.d.ts +33 -0
- package/dist/policies/schedules.js +131 -0
- package/dist/protocol/frames.d.ts +66 -0
- package/dist/protocol/frames.js +55 -0
- package/dist/protocol/inspection.d.ts +807 -0
- package/dist/protocol/inspection.js +243 -0
- package/dist/protocol/wire.d.ts +184 -0
- package/dist/protocol/wire.js +150 -0
- package/dist/runtime/actors.d.ts +155 -0
- package/dist/runtime/actors.js +4 -0
- package/dist/runtime/admission.d.ts +34 -0
- package/dist/runtime/admission.js +72 -0
- package/dist/runtime/adoption/backfill.d.ts +27 -0
- package/dist/runtime/adoption/backfill.js +144 -0
- package/dist/runtime/adoption/enforce.d.ts +55 -0
- package/dist/runtime/adoption/enforce.js +241 -0
- package/dist/runtime/adoption/observe.d.ts +43 -0
- package/dist/runtime/adoption/observe.js +139 -0
- package/dist/runtime/adoption/plan.d.ts +45 -0
- package/dist/runtime/adoption/plan.js +192 -0
- package/dist/runtime/adoption/startup.d.ts +32 -0
- package/dist/runtime/adoption/startup.js +76 -0
- package/dist/runtime/adoption/status.d.ts +23 -0
- package/dist/runtime/adoption/status.js +47 -0
- package/dist/runtime/adoption/target.d.ts +33 -0
- package/dist/runtime/adoption/target.js +49 -0
- package/dist/runtime/connections/holder.d.ts +221 -0
- package/dist/runtime/connections/holder.js +816 -0
- package/dist/runtime/connections/owner.d.ts +263 -0
- package/dist/runtime/connections/owner.js +896 -0
- package/dist/runtime/connections/protocol.d.ts +441 -0
- package/dist/runtime/connections/protocol.js +222 -0
- package/dist/runtime/connections/reads.d.ts +24 -0
- package/dist/runtime/connections/reads.js +19 -0
- package/dist/runtime/connections/streams.d.ts +62 -0
- package/dist/runtime/connections/streams.js +128 -0
- package/dist/runtime/connections/transport.d.ts +40 -0
- package/dist/runtime/connections/transport.js +68 -0
- package/dist/runtime/connections/watch.d.ts +52 -0
- package/dist/runtime/connections/watch.js +159 -0
- package/dist/runtime/content/grant.d.ts +34 -0
- package/dist/runtime/content/grant.js +58 -0
- package/dist/runtime/content/store.d.ts +63 -0
- package/dist/runtime/content/store.js +294 -0
- package/dist/runtime/cron/key.d.ts +2 -0
- package/dist/runtime/cron/key.js +2 -0
- package/dist/runtime/cron/schedule.d.ts +69 -0
- package/dist/runtime/cron/schedule.js +144 -0
- package/dist/runtime/database/bounded.d.ts +43 -0
- package/dist/runtime/database/bounded.js +108 -0
- package/dist/runtime/database/connection.d.ts +8 -0
- package/dist/runtime/database/connection.js +18 -0
- package/dist/runtime/database/coordination.d.ts +19 -0
- package/dist/runtime/database/coordination.js +72 -0
- package/dist/runtime/database/flock.d.ts +10 -0
- package/dist/runtime/database/flock.js +60 -0
- package/dist/runtime/database/gate.d.ts +26 -0
- package/dist/runtime/database/gate.js +54 -0
- package/dist/runtime/database/keepalive.d.ts +11 -0
- package/dist/runtime/database/keepalive.js +22 -0
- package/dist/runtime/database/migrations.d.ts +177 -0
- package/dist/runtime/database/migrations.js +1018 -0
- package/dist/runtime/database/neki/migrations.d.ts +31 -0
- package/dist/runtime/database/neki/migrations.js +276 -0
- package/dist/runtime/database/neki/session.d.ts +23 -0
- package/dist/runtime/database/neki/session.js +31 -0
- package/dist/runtime/database/pglite.d.ts +21 -0
- package/dist/runtime/database/pglite.js +74 -0
- package/dist/runtime/database/replica.d.ts +41 -0
- package/dist/runtime/database/replica.js +51 -0
- package/dist/runtime/database/shards.d.ts +21 -0
- package/dist/runtime/database/shards.js +37 -0
- package/dist/runtime/database/snapshot.d.ts +7 -0
- package/dist/runtime/database/snapshot.js +7 -0
- package/dist/runtime/database/tenancy.d.ts +69 -0
- package/dist/runtime/database/tenancy.js +145 -0
- package/dist/runtime/database/transient.d.ts +10 -0
- package/dist/runtime/database/transient.js +25 -0
- package/dist/runtime/drain.d.ts +132 -0
- package/dist/runtime/drain.js +109 -0
- package/dist/runtime/entity/mailbox.d.ts +86 -0
- package/dist/runtime/entity/mailbox.js +121 -0
- package/dist/runtime/entity/register.d.ts +424 -0
- package/dist/runtime/entity/register.js +669 -0
- package/dist/runtime/events/append.d.ts +192 -0
- package/dist/runtime/events/append.js +128 -0
- package/dist/runtime/events/cursor.d.ts +2 -0
- package/dist/runtime/events/cursor.js +5 -0
- package/dist/runtime/events/replay.d.ts +20 -0
- package/dist/runtime/events/replay.js +45 -0
- package/dist/runtime/feeds.d.ts +26 -0
- package/dist/runtime/feeds.js +100 -0
- package/dist/runtime/fleet/checks.d.ts +17 -0
- package/dist/runtime/fleet/checks.js +106 -0
- package/dist/runtime/fleet/maintainer.d.ts +38 -0
- package/dist/runtime/fleet/maintainer.js +383 -0
- package/dist/runtime/fleet/pgoutput.d.ts +41 -0
- package/dist/runtime/fleet/pgoutput.js +102 -0
- package/dist/runtime/fleet/setup.d.ts +36 -0
- package/dist/runtime/fleet/setup.js +58 -0
- package/dist/runtime/fleet/subscribe.d.ts +42 -0
- package/dist/runtime/fleet/subscribe.js +147 -0
- package/dist/runtime/index.d.ts +66 -0
- package/dist/runtime/index.js +37 -0
- package/dist/runtime/inspector/http.d.ts +19 -0
- package/dist/runtime/inspector/http.js +36 -0
- package/dist/runtime/inspector/layer.d.ts +46 -0
- package/dist/runtime/inspector/layer.js +180 -0
- package/dist/runtime/inspector/queries.d.ts +711 -0
- package/dist/runtime/inspector/queries.js +494 -0
- package/dist/runtime/jobs/attempt.d.ts +254 -0
- package/dist/runtime/jobs/attempt.js +413 -0
- package/dist/runtime/jobs/isolation.d.ts +20 -0
- package/dist/runtime/jobs/isolation.js +10 -0
- package/dist/runtime/jobs/progress.d.ts +95 -0
- package/dist/runtime/jobs/progress.js +144 -0
- package/dist/runtime/layer.d.ts +260 -0
- package/dist/runtime/layer.js +876 -0
- package/dist/runtime/members.d.ts +381 -0
- package/dist/runtime/members.js +8 -0
- package/dist/runtime/operators/audit.d.ts +64 -0
- package/dist/runtime/operators/audit.js +56 -0
- package/dist/runtime/operators/auth.d.ts +29 -0
- package/dist/runtime/operators/auth.js +42 -0
- package/dist/runtime/operators/export.d.ts +68 -0
- package/dist/runtime/operators/export.js +137 -0
- package/dist/runtime/operators/grants.d.ts +45 -0
- package/dist/runtime/operators/grants.js +40 -0
- package/dist/runtime/operators/repair.d.ts +206 -0
- package/dist/runtime/operators/repair.js +226 -0
- package/dist/runtime/operators/routes.d.ts +35 -0
- package/dist/runtime/operators/routes.js +190 -0
- package/dist/runtime/operators/seed.d.ts +123 -0
- package/dist/runtime/operators/seed.js +123 -0
- package/dist/runtime/payloads/versions.d.ts +65 -0
- package/dist/runtime/payloads/versions.js +240 -0
- package/dist/runtime/peering/authority.d.ts +42 -0
- package/dist/runtime/peering/authority.js +132 -0
- package/dist/runtime/peering/credentials.d.ts +21 -0
- package/dist/runtime/peering/credentials.js +13 -0
- package/dist/runtime/peering/transport.d.ts +51 -0
- package/dist/runtime/peering/transport.js +175 -0
- package/dist/runtime/queries.d.ts +18 -0
- package/dist/runtime/queries.js +169 -0
- package/dist/runtime/readiness.d.ts +36 -0
- package/dist/runtime/readiness.js +44 -0
- package/dist/runtime/registration.d.ts +58 -0
- package/dist/runtime/registration.js +186 -0
- package/dist/runtime/request.d.ts +158 -0
- package/dist/runtime/request.js +102 -0
- package/dist/runtime/retry.d.ts +8 -0
- package/dist/runtime/retry.js +11 -0
- package/dist/runtime/runner.d.ts +93 -0
- package/dist/runtime/runner.js +122 -0
- package/dist/runtime/storage/codec.d.ts +39 -0
- package/dist/runtime/storage/codec.js +41 -0
- package/dist/runtime/storage/generation.d.ts +69 -0
- package/dist/runtime/storage/generation.js +49 -0
- package/dist/runtime/storage/placements.d.ts +15 -0
- package/dist/runtime/storage/placements.js +38 -0
- package/dist/runtime/storage/platform.d.ts +6 -0
- package/dist/runtime/storage/platform.js +9 -0
- package/dist/runtime/storage/retention.d.ts +73 -0
- package/dist/runtime/storage/retention.js +158 -0
- package/dist/runtime/subscriptions/identity.d.ts +31 -0
- package/dist/runtime/subscriptions/identity.js +45 -0
- package/dist/runtime/subscriptions/relay.d.ts +175 -0
- package/dist/runtime/subscriptions/relay.js +763 -0
- package/dist/runtime/subscriptions/storage.d.ts +40 -0
- package/dist/runtime/subscriptions/storage.js +98 -0
- package/dist/runtime/sweeps.d.ts +39 -0
- package/dist/runtime/sweeps.js +54 -0
- package/dist/runtime/telemetry/defects.d.ts +57 -0
- package/dist/runtime/telemetry/defects.js +43 -0
- package/dist/runtime/telemetry/metrics.d.ts +52 -0
- package/dist/runtime/telemetry/metrics.js +138 -0
- package/dist/runtime/telemetry/routes.d.ts +19 -0
- package/dist/runtime/telemetry/routes.js +18 -0
- package/dist/runtime/telemetry/sampler.d.ts +26 -0
- package/dist/runtime/telemetry/sampler.js +101 -0
- package/dist/runtime/telemetry/spans.d.ts +38 -0
- package/dist/runtime/telemetry/spans.js +44 -0
- package/dist/runtime/telemetry/usage.d.ts +52 -0
- package/dist/runtime/telemetry/usage.js +30 -0
- package/dist/runtime/topology/admission.d.ts +13 -0
- package/dist/runtime/topology/admission.js +36 -0
- package/dist/runtime/topology/locks.d.ts +56 -0
- package/dist/runtime/topology/locks.js +81 -0
- package/dist/runtime/topology/messages.d.ts +15 -0
- package/dist/runtime/topology/messages.js +32 -0
- package/dist/runtime/turn/admission.d.ts +22 -0
- package/dist/runtime/turn/admission.js +41 -0
- package/dist/runtime/turn/blobs.d.ts +38 -0
- package/dist/runtime/turn/blobs.js +215 -0
- package/dist/runtime/turn/execute.d.ts +208 -0
- package/dist/runtime/turn/execute.js +972 -0
- package/dist/runtime/turn/group.d.ts +165 -0
- package/dist/runtime/turn/group.js +326 -0
- package/dist/runtime/turn/hooks.d.ts +44 -0
- package/dist/runtime/turn/hooks.js +28 -0
- package/dist/runtime/turn/outbox.d.ts +159 -0
- package/dist/runtime/turn/outbox.js +269 -0
- package/dist/runtime/turn/pipeline.d.ts +68 -0
- package/dist/runtime/turn/pipeline.js +83 -0
- package/dist/runtime/turn/receipt.d.ts +81 -0
- package/dist/runtime/turn/receipt.js +32 -0
- package/dist/runtime/turn/relay.d.ts +179 -0
- package/dist/runtime/turn/relay.js +685 -0
- package/dist/runtime/turn/rows.d.ts +43 -0
- package/dist/runtime/turn/rows.js +592 -0
- package/dist/runtime/workflows/compatibility.d.ts +125 -0
- package/dist/runtime/workflows/compatibility.js +285 -0
- package/dist/runtime/workflows/engine.d.ts +107 -0
- package/dist/runtime/workflows/engine.js +826 -0
- package/dist/runtime/workflows/manifest.d.ts +34 -0
- package/dist/runtime/workflows/manifest.js +39 -0
- package/dist/runtime/workflows/steps.d.ts +33 -0
- package/dist/runtime/workflows/steps.js +152 -0
- package/dist/serve/api.d.ts +99 -0
- package/dist/serve/api.js +403 -0
- package/dist/serve/assertion/binding.d.ts +41 -0
- package/dist/serve/assertion/binding.js +92 -0
- package/dist/serve/assertion/verify.d.ts +93 -0
- package/dist/serve/assertion/verify.js +189 -0
- package/dist/serve/auth.d.ts +200 -0
- package/dist/serve/auth.js +92 -0
- package/dist/serve/clock.d.ts +10 -0
- package/dist/serve/clock.js +25 -0
- package/dist/serve/jwt.d.ts +65 -0
- package/dist/serve/jwt.js +202 -0
- package/dist/serve/layer.d.ts +98 -0
- package/dist/serve/layer.js +809 -0
- package/dist/serve/mcp/endpoint.d.ts +39 -0
- package/dist/serve/mcp/endpoint.js +187 -0
- package/dist/serve/mcp/tools.d.ts +37 -0
- package/dist/serve/mcp/tools.js +125 -0
- package/dist/serve/origin.d.ts +6 -0
- package/dist/serve/origin.js +21 -0
- package/dist/serve/sessions/feed.d.ts +79 -0
- package/dist/serve/sessions/feed.js +115 -0
- package/dist/serve/sessions/socket.d.ts +56 -0
- package/dist/serve/sessions/socket.js +212 -0
- package/dist/serve/sessions/sse.d.ts +27 -0
- package/dist/serve/sessions/sse.js +26 -0
- package/dist/serve/sessions/stream.d.ts +9 -0
- package/dist/serve/sessions/stream.js +9 -0
- package/dist/serve/sessions/watch.d.ts +12 -0
- package/dist/serve/sessions/watch.js +11 -0
- package/dist/serve/wire.d.ts +16 -0
- package/dist/serve/wire.js +50 -0
- package/dist/state/blob.d.ts +71 -0
- package/dist/state/blob.js +0 -0
- package/dist/state/migration.d.ts +53 -0
- package/dist/state/migration.js +29 -0
- package/dist/tables/fleet.d.ts +170 -0
- package/dist/tables/fleet.js +214 -0
- package/dist/tables/owned.d.ts +250 -0
- package/dist/tables/owned.js +233 -0
- package/dist/testing/actor-test.d.ts +360 -0
- package/dist/testing/actor-test.js +337 -0
- package/dist/testing/cluster.d.ts +93 -0
- package/dist/testing/cluster.js +296 -0
- package/dist/testing/conformance/access.d.ts +5 -0
- package/dist/testing/conformance/access.js +531 -0
- package/dist/testing/conformance/admission.d.ts +11 -0
- package/dist/testing/conformance/admission.js +182 -0
- package/dist/testing/conformance/adoption.d.ts +2 -0
- package/dist/testing/conformance/adoption.js +1019 -0
- package/dist/testing/conformance/assertions.d.ts +202 -0
- package/dist/testing/conformance/assertions.js +849 -0
- package/dist/testing/conformance/batches.d.ts +41 -0
- package/dist/testing/conformance/batches.js +388 -0
- package/dist/testing/conformance/blobs.d.ts +14 -0
- package/dist/testing/conformance/blobs.js +405 -0
- package/dist/testing/conformance/capacity.d.ts +8 -0
- package/dist/testing/conformance/capacity.js +433 -0
- package/dist/testing/conformance/client.d.ts +5 -0
- package/dist/testing/conformance/client.js +719 -0
- package/dist/testing/conformance/cold-serve.d.ts +16 -0
- package/dist/testing/conformance/cold-serve.js +254 -0
- package/dist/testing/conformance/connections/actors.d.ts +1143 -0
- package/dist/testing/conformance/connections/actors.js +180 -0
- package/dist/testing/conformance/connections/harness.d.ts +1754 -0
- package/dist/testing/conformance/connections/harness.js +142 -0
- package/dist/testing/conformance/connections/holders.d.ts +4 -0
- package/dist/testing/conformance/connections/holders.js +165 -0
- package/dist/testing/conformance/connections/lifecycle.d.ts +4 -0
- package/dist/testing/conformance/connections/lifecycle.js +113 -0
- package/dist/testing/conformance/connections/limits.d.ts +4 -0
- package/dist/testing/conformance/connections/limits.js +127 -0
- package/dist/testing/conformance/connections/reauthorization.d.ts +4 -0
- package/dist/testing/conformance/connections/reauthorization.js +170 -0
- package/dist/testing/conformance/connections/resync.d.ts +4 -0
- package/dist/testing/conformance/connections/resync.js +360 -0
- package/dist/testing/conformance/connections/takeover.d.ts +4 -0
- package/dist/testing/conformance/connections/takeover.js +287 -0
- package/dist/testing/conformance/connections.d.ts +4 -0
- package/dist/testing/conformance/connections.js +15 -0
- package/dist/testing/conformance/content-blobs.d.ts +16 -0
- package/dist/testing/conformance/content-blobs.js +534 -0
- package/dist/testing/conformance/counter.d.ts +16 -0
- package/dist/testing/conformance/counter.js +813 -0
- package/dist/testing/conformance/cron.d.ts +5 -0
- package/dist/testing/conformance/cron.js +665 -0
- package/dist/testing/conformance/drain.d.ts +26 -0
- package/dist/testing/conformance/drain.js +540 -0
- package/dist/testing/conformance/effect-control.d.ts +38 -0
- package/dist/testing/conformance/effect-control.js +1059 -0
- package/dist/testing/conformance/effects.d.ts +23 -0
- package/dist/testing/conformance/effects.js +432 -0
- package/dist/testing/conformance/events.d.ts +28 -0
- package/dist/testing/conformance/events.js +335 -0
- package/dist/testing/conformance/export.d.ts +2 -0
- package/dist/testing/conformance/export.js +368 -0
- package/dist/testing/conformance/fleet.d.ts +444 -0
- package/dist/testing/conformance/fleet.js +1009 -0
- package/dist/testing/conformance/groups.d.ts +7 -0
- package/dist/testing/conformance/groups.js +353 -0
- package/dist/testing/conformance/heap.d.ts +3 -0
- package/dist/testing/conformance/heap.js +149 -0
- package/dist/testing/conformance/http.d.ts +2809 -0
- package/dist/testing/conformance/http.js +1234 -0
- package/dist/testing/conformance/inspection-views.d.ts +8 -0
- package/dist/testing/conformance/inspection-views.js +272 -0
- package/dist/testing/conformance/inspector.d.ts +7 -0
- package/dist/testing/conformance/inspector.js +650 -0
- package/dist/testing/conformance/mint.d.ts +58 -0
- package/dist/testing/conformance/mint.js +692 -0
- package/dist/testing/conformance/multi-runner.d.ts +8 -0
- package/dist/testing/conformance/multi-runner.js +403 -0
- package/dist/testing/conformance/neki/plan.d.ts +19 -0
- package/dist/testing/conformance/neki/plan.js +30 -0
- package/dist/testing/conformance/observability.d.ts +3 -0
- package/dist/testing/conformance/observability.js +343 -0
- package/dist/testing/conformance/offline.d.ts +4 -0
- package/dist/testing/conformance/offline.js +474 -0
- package/dist/testing/conformance/operator-harness.d.ts +57 -0
- package/dist/testing/conformance/operator-harness.js +78 -0
- package/dist/testing/conformance/operator.d.ts +3 -0
- package/dist/testing/conformance/operator.js +379 -0
- package/dist/testing/conformance/outbox-cross-shard.d.ts +14 -0
- package/dist/testing/conformance/outbox-cross-shard.js +160 -0
- package/dist/testing/conformance/outbox.d.ts +36 -0
- package/dist/testing/conformance/outbox.js +437 -0
- package/dist/testing/conformance/payload-migrations.d.ts +3 -0
- package/dist/testing/conformance/payload-migrations.js +612 -0
- package/dist/testing/conformance/pipeline.d.ts +3 -0
- package/dist/testing/conformance/pipeline.js +1040 -0
- package/dist/testing/conformance/placement.d.ts +22 -0
- package/dist/testing/conformance/placement.js +539 -0
- package/dist/testing/conformance/platform.d.ts +40 -0
- package/dist/testing/conformance/platform.js +101 -0
- package/dist/testing/conformance/progress.d.ts +39 -0
- package/dist/testing/conformance/progress.js +666 -0
- package/dist/testing/conformance/properties.d.ts +6 -0
- package/dist/testing/conformance/properties.js +295 -0
- package/dist/testing/conformance/protocols.d.ts +4 -0
- package/dist/testing/conformance/protocols.js +618 -0
- package/dist/testing/conformance/provider/advertise.d.ts +21 -0
- package/dist/testing/conformance/provider/advertise.js +44 -0
- package/dist/testing/conformance/read-your-writes.d.ts +20 -0
- package/dist/testing/conformance/read-your-writes.js +192 -0
- package/dist/testing/conformance/reducers.d.ts +8 -0
- package/dist/testing/conformance/reducers.js +258 -0
- package/dist/testing/conformance/relay.d.ts +42 -0
- package/dist/testing/conformance/relay.js +1115 -0
- package/dist/testing/conformance/restore.d.ts +43 -0
- package/dist/testing/conformance/restore.js +341 -0
- package/dist/testing/conformance/retention.d.ts +14 -0
- package/dist/testing/conformance/retention.js +463 -0
- package/dist/testing/conformance/rls.d.ts +24 -0
- package/dist/testing/conformance/rls.js +576 -0
- package/dist/testing/conformance/simulation.d.ts +31 -0
- package/dist/testing/conformance/simulation.js +167 -0
- package/dist/testing/conformance/single-shard.d.ts +2 -0
- package/dist/testing/conformance/single-shard.js +125 -0
- package/dist/testing/conformance/singleton.d.ts +3 -0
- package/dist/testing/conformance/singleton.js +293 -0
- package/dist/testing/conformance/statements.d.ts +60 -0
- package/dist/testing/conformance/statements.js +108 -0
- package/dist/testing/conformance/streams.d.ts +7 -0
- package/dist/testing/conformance/streams.js +280 -0
- package/dist/testing/conformance/subscriptions/access.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/access.js +98 -0
- package/dist/testing/conformance/subscriptions/actors.d.ts +4910 -0
- package/dist/testing/conformance/subscriptions/actors.js +372 -0
- package/dist/testing/conformance/subscriptions/cluster.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/cluster.js +89 -0
- package/dist/testing/conformance/subscriptions/delivery.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/delivery.js +253 -0
- package/dist/testing/conformance/subscriptions/deploys.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/deploys.js +162 -0
- package/dist/testing/conformance/subscriptions/epochs.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/epochs.js +169 -0
- package/dist/testing/conformance/subscriptions/failures.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/failures.js +108 -0
- package/dist/testing/conformance/subscriptions/feeds.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/feeds.js +218 -0
- package/dist/testing/conformance/subscriptions/harness.d.ts +124 -0
- package/dist/testing/conformance/subscriptions/harness.js +143 -0
- package/dist/testing/conformance/subscriptions/operator.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/operator.js +128 -0
- package/dist/testing/conformance/subscriptions/retention.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/retention.js +288 -0
- package/dist/testing/conformance/subscriptions/routing.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/routing.js +179 -0
- package/dist/testing/conformance/subscriptions.d.ts +4 -0
- package/dist/testing/conformance/subscriptions.js +19 -0
- package/dist/testing/conformance/tables.d.ts +125 -0
- package/dist/testing/conformance/tables.js +664 -0
- package/dist/testing/conformance/transports/actors.d.ts +1907 -0
- package/dist/testing/conformance/transports/actors.js +163 -0
- package/dist/testing/conformance/transports/client.d.ts +3 -0
- package/dist/testing/conformance/transports/client.js +237 -0
- package/dist/testing/conformance/transports/feeds.d.ts +3 -0
- package/dist/testing/conformance/transports/feeds.js +232 -0
- package/dist/testing/conformance/transports/harness.d.ts +93 -0
- package/dist/testing/conformance/transports/harness.js +84 -0
- package/dist/testing/conformance/transports/streams.d.ts +3 -0
- package/dist/testing/conformance/transports/streams.js +96 -0
- package/dist/testing/conformance/transports/websocket.d.ts +3 -0
- package/dist/testing/conformance/transports/websocket.js +384 -0
- package/dist/testing/conformance/transports/wire.d.ts +141 -0
- package/dist/testing/conformance/transports/wire.js +204 -0
- package/dist/testing/conformance/transports.d.ts +3 -0
- package/dist/testing/conformance/transports.js +11 -0
- package/dist/testing/conformance/watch.d.ts +59 -0
- package/dist/testing/conformance/watch.js +879 -0
- package/dist/testing/conformance/workflow-engine.d.ts +97 -0
- package/dist/testing/conformance/workflow-engine.js +253 -0
- package/dist/testing/conformance/workflow-versions.d.ts +3 -0
- package/dist/testing/conformance/workflow-versions.js +545 -0
- package/dist/testing/conformance/workflows/access.d.ts +4 -0
- package/dist/testing/conformance/workflows/access.js +147 -0
- package/dist/testing/conformance/workflows/actors.d.ts +2317 -0
- package/dist/testing/conformance/workflows/actors.js +305 -0
- package/dist/testing/conformance/workflows/basics.d.ts +4 -0
- package/dist/testing/conformance/workflows/basics.js +144 -0
- package/dist/testing/conformance/workflows/engine.d.ts +4 -0
- package/dist/testing/conformance/workflows/engine.js +80 -0
- package/dist/testing/conformance/workflows/guards.d.ts +4 -0
- package/dist/testing/conformance/workflows/guards.js +172 -0
- package/dist/testing/conformance/workflows/harness.d.ts +14 -0
- package/dist/testing/conformance/workflows/harness.js +44 -0
- package/dist/testing/conformance/workflows/recovery.d.ts +4 -0
- package/dist/testing/conformance/workflows/recovery.js +181 -0
- package/dist/testing/conformance/workflows/starts.d.ts +4 -0
- package/dist/testing/conformance/workflows/starts.js +57 -0
- package/dist/testing/conformance/workflows/suspension.d.ts +4 -0
- package/dist/testing/conformance/workflows/suspension.js +106 -0
- package/dist/testing/conformance/workflows/waits.d.ts +4 -0
- package/dist/testing/conformance/workflows/waits.js +284 -0
- package/dist/testing/conformance/workflows.d.ts +4 -0
- package/dist/testing/conformance/workflows.js +19 -0
- package/dist/testing/conformance.d.ts +330 -0
- package/dist/testing/conformance.js +364 -0
- package/dist/testing/database.d.ts +31 -0
- package/dist/testing/database.js +70 -0
- package/dist/testing/foundation.d.ts +41 -0
- package/dist/testing/foundation.js +517 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/property.d.ts +40 -0
- package/dist/testing/property.js +95 -0
- package/dist/testing/simulate-cluster.d.ts +112 -0
- package/dist/testing/simulate-cluster.js +257 -0
- package/dist/testing/simulate.d.ts +90 -0
- package/dist/testing/simulate.js +127 -0
- package/package.json +1 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
import { Migrator, SqlClient } from "effect/sql";
|
|
3
|
+
import type { SqlConnection, SqlError } from "effect/sql";
|
|
4
|
+
import type { Scope } from "effect";
|
|
5
|
+
import { ShardingConfig } from "effect/cluster";
|
|
6
|
+
/** Holds startup coordination before even the history table is created, on one leased session. */
|
|
7
|
+
export declare const withMigrationCoordination: <A, E>(effect: Effect.Effect<A, E, SqlClient.SqlClient | Scope.Scope>) => Effect.Effect<A, E | SqlError.SqlError, SqlClient.SqlClient>;
|
|
8
|
+
/** Propagation must finish before a later statement uses the changed schema. */
|
|
9
|
+
export declare const MigrationBarrier: Context.Reference<(connection: SqlConnection.Connection) => Effect.Effect<unknown, SqlError.SqlError>>;
|
|
10
|
+
/** A reserved session owns coordination across autocommit DDL and progress writes. */
|
|
11
|
+
export declare const MigrationCoordination: Context.Reference<(connection: SqlConnection.Connection) => Effect.Effect<unknown, SqlError.SqlError, Scope.Scope>>;
|
|
12
|
+
/** Failure injection runs after a durable boundary, never in place of the database operation. */
|
|
13
|
+
export declare const MigrationBoundary: Context.Reference<(point: string) => Effect.Effect<void>>;
|
|
14
|
+
/** A preflight is already satisfied when this migration has started changing its schema. */
|
|
15
|
+
export declare const MigrationResuming: Context.Reference<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Cluster creates its tables outside migration history. Creating its default schema
|
|
18
|
+
* under startup coordination first prevents concurrent CREATE races without lending
|
|
19
|
+
* Cluster a migration-scoped SQL client or consuming a third off-turn connection.
|
|
20
|
+
*/
|
|
21
|
+
export declare const prepareRunnerStorage: Effect.Effect<void, SqlError.SqlError, ShardingConfig.ShardingConfig | SqlClient.SqlClient>;
|
|
22
|
+
/**
|
|
23
|
+
* DDL and its propagation run without BEGIN. A durable statement journal closes the
|
|
24
|
+
* crash gap between applying DDL and recording it; replay touches only an unfinished
|
|
25
|
+
* step, so a later drop/recreate cannot make an earlier create run again. Existing
|
|
26
|
+
* data rewrites set fixed values and are replayable, never increments or external work.
|
|
27
|
+
*/
|
|
28
|
+
export declare const nekiMigrator: ({ record, refuseSkipped, }: {
|
|
29
|
+
readonly record: Record<string, Effect.Effect<void, never, SqlClient.SqlClient>>;
|
|
30
|
+
readonly refuseSkipped: Effect.Effect<void, Migrator.MigrationError | SqlError.SqlError, SqlClient.SqlClient>;
|
|
31
|
+
}) => Effect.Effect<(readonly [number, string])[], Migrator.MigrationError | SqlError.SqlError, SqlClient.SqlClient>;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { Context, Effect } from "effect";
|
|
3
|
+
import { Migrator, SqlClient } from "effect/sql";
|
|
4
|
+
import { Reactivity } from "effect/reactivity";
|
|
5
|
+
import { ShardingConfig } from "effect/cluster";
|
|
6
|
+
import { NekiTurnSessions } from "./session.js";
|
|
7
|
+
/** Holds startup coordination before even the history table is created, on one leased session. */
|
|
8
|
+
export const withMigrationCoordination = (effect) => Effect.scoped(Effect.gen(function* () {
|
|
9
|
+
const source = yield* SqlClient.SqlClient;
|
|
10
|
+
const connection = yield* source.reserve;
|
|
11
|
+
yield* (yield* MigrationCoordination)(connection);
|
|
12
|
+
const reactivity = yield* Reactivity.make;
|
|
13
|
+
const client = yield* SqlClient.make({
|
|
14
|
+
acquirer: Effect.succeed(connection),
|
|
15
|
+
compiler: PgClient.makeCompiler(),
|
|
16
|
+
spanAttributes: [],
|
|
17
|
+
}).pipe(Effect.provideService(Reactivity.Reactivity, reactivity));
|
|
18
|
+
return yield* Effect.provideService(effect, SqlClient.SqlClient, client);
|
|
19
|
+
}));
|
|
20
|
+
/** Propagation must finish before a later statement uses the changed schema. */
|
|
21
|
+
export const MigrationBarrier = Context.Reference("akter/MigrationBarrier", {
|
|
22
|
+
defaultValue: () => (connection) => connection.execute("SELECT __neki.wait_for_ddl()", [], undefined),
|
|
23
|
+
});
|
|
24
|
+
/** A reserved session owns coordination across autocommit DDL and progress writes. */
|
|
25
|
+
export const MigrationCoordination = Context.Reference("akter/MigrationCoordination", {
|
|
26
|
+
defaultValue: () => (connection) => Effect.acquireRelease(connection.execute("SELECT pg_advisory_lock(1935764837, 487)", [], undefined), () => connection
|
|
27
|
+
.execute("SELECT pg_advisory_unlock(1935764837, 487)", [], undefined)
|
|
28
|
+
.pipe(Effect.orDie)),
|
|
29
|
+
});
|
|
30
|
+
/** Failure injection runs after a durable boundary, never in place of the database operation. */
|
|
31
|
+
export const MigrationBoundary = Context.Reference("akter/MigrationBoundary", { defaultValue: () => () => Effect.void });
|
|
32
|
+
/** A preflight is already satisfied when this migration has started changing its schema. */
|
|
33
|
+
export const MigrationResuming = Context.Reference("akter/MigrationResuming", {
|
|
34
|
+
defaultValue: () => false,
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Cluster creates its tables outside migration history. Creating its default schema
|
|
38
|
+
* under startup coordination first prevents concurrent CREATE races without lending
|
|
39
|
+
* Cluster a migration-scoped SQL client or consuming a third off-turn connection.
|
|
40
|
+
*/
|
|
41
|
+
export const prepareRunnerStorage = Effect.gen(function* () {
|
|
42
|
+
const config = yield* ShardingConfig.ShardingConfig;
|
|
43
|
+
const neki = yield* NekiTurnSessions;
|
|
44
|
+
return yield* withMigrationCoordination(Effect.gen(function* () {
|
|
45
|
+
const sql = yield* SqlClient.SqlClient;
|
|
46
|
+
const connection = yield* sql.reserve;
|
|
47
|
+
const boundary = yield* MigrationBoundary;
|
|
48
|
+
const barrier = yield* MigrationBarrier;
|
|
49
|
+
if (neki)
|
|
50
|
+
yield* barrier(connection);
|
|
51
|
+
yield* sql `CREATE TABLE IF NOT EXISTS cluster_runners (
|
|
52
|
+
machine_id SERIAL PRIMARY KEY,
|
|
53
|
+
address VARCHAR(255) NOT NULL,
|
|
54
|
+
runner TEXT NOT NULL,
|
|
55
|
+
healthy BOOLEAN NOT NULL DEFAULT TRUE,
|
|
56
|
+
last_heartbeat TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
57
|
+
UNIQUE(address)
|
|
58
|
+
)`;
|
|
59
|
+
yield* boundary("cluster:runners:ddl");
|
|
60
|
+
if (neki)
|
|
61
|
+
yield* barrier(connection);
|
|
62
|
+
yield* boundary("cluster:runners:propagated");
|
|
63
|
+
if (config.shardLockDisableAdvisory) {
|
|
64
|
+
yield* sql `CREATE TABLE IF NOT EXISTS cluster_locks (
|
|
65
|
+
shard_id VARCHAR(50) PRIMARY KEY,
|
|
66
|
+
address VARCHAR(255) NOT NULL,
|
|
67
|
+
acquired_at TIMESTAMP NOT NULL
|
|
68
|
+
)`;
|
|
69
|
+
yield* boundary("cluster:locks:ddl");
|
|
70
|
+
if (neki)
|
|
71
|
+
yield* barrier(connection);
|
|
72
|
+
yield* boundary("cluster:locks:propagated");
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
76
|
+
/** ALTER actions split only at commas outside quoted identifiers, literals and expressions. */
|
|
77
|
+
const alterActions = (text) => {
|
|
78
|
+
const actions = [];
|
|
79
|
+
let start = 0;
|
|
80
|
+
let depth = 0;
|
|
81
|
+
let quote = "";
|
|
82
|
+
for (let i = 0; i < text.length; i++) {
|
|
83
|
+
const character = text[i];
|
|
84
|
+
if (quote !== "") {
|
|
85
|
+
if (character === quote) {
|
|
86
|
+
if (text[i + 1] === quote)
|
|
87
|
+
i++;
|
|
88
|
+
else
|
|
89
|
+
quote = "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (character === "'" || character === '"')
|
|
93
|
+
quote = character;
|
|
94
|
+
else if (character === "(")
|
|
95
|
+
depth++;
|
|
96
|
+
else if (character === ")")
|
|
97
|
+
depth--;
|
|
98
|
+
else if (character === "," && depth === 0) {
|
|
99
|
+
actions.push(text.slice(start, i).trim());
|
|
100
|
+
start = i + 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
actions.push(text.slice(start).trim());
|
|
104
|
+
return actions;
|
|
105
|
+
};
|
|
106
|
+
const identifier = '(?:"(?:[^"]|"")+"|[a-z_][a-z_0-9]*)(?:\\.(?:"(?:[^"]|"")+"|[a-z_][a-z_0-9]*))?';
|
|
107
|
+
const alter = new RegExp(`^ALTER TABLE (${identifier})\\s+([\\s\\S]+)$`, "i");
|
|
108
|
+
const unquote = (name) => name.replace(/^"|"$/g, "").replaceAll('""', '"');
|
|
109
|
+
/**
|
|
110
|
+
* Resumption rewrites only the framework's audited DDL forms; an unknown form fails closed.
|
|
111
|
+
* A statement that already carries its own existence guard is replayed unchanged.
|
|
112
|
+
*/
|
|
113
|
+
const replayDdl = (sql, text) => Effect.gen(function* () {
|
|
114
|
+
if (/^CREATE (?:UNIQUE )?(?:TABLE|INDEX|SCHEMA) /i.test(text))
|
|
115
|
+
return text.replace(/^(CREATE (?:UNIQUE )?(?:TABLE|INDEX|SCHEMA)) (?!IF NOT EXISTS )/i, "$1 IF NOT EXISTS ");
|
|
116
|
+
if (/^DROP (?:TABLE|INDEX|VIEW) /i.test(text))
|
|
117
|
+
return text.replace(/^(DROP (?:TABLE|INDEX|VIEW)) (?!IF EXISTS )/i, "$1 IF EXISTS ");
|
|
118
|
+
if (/^CREATE (?:OR REPLACE )?(?:VIEW|FUNCTION) /i.test(text))
|
|
119
|
+
return text.replace(/^CREATE (?:OR REPLACE )?/i, "CREATE OR REPLACE ");
|
|
120
|
+
const policy = new RegExp(`^CREATE POLICY (${identifier}) ON (${identifier})`, "i").exec(text);
|
|
121
|
+
if (policy !== null) {
|
|
122
|
+
const [row] = yield* sql `SELECT EXISTS (
|
|
123
|
+
SELECT 1 FROM pg_policy WHERE polrelid = to_regclass(${policy[2]})
|
|
124
|
+
AND polname = ${unquote(policy[1])}) AS exists`;
|
|
125
|
+
return row?.exists === true ? undefined : text;
|
|
126
|
+
}
|
|
127
|
+
const match = alter.exec(text);
|
|
128
|
+
if (match !== null) {
|
|
129
|
+
const table = match[1];
|
|
130
|
+
const action = match[2];
|
|
131
|
+
if (/^ADD COLUMN /i.test(action))
|
|
132
|
+
return `ALTER TABLE ${table} ${action.replace(/^ADD COLUMN (?!IF NOT EXISTS )/i, "ADD COLUMN IF NOT EXISTS ")}`;
|
|
133
|
+
if (/^DROP CONSTRAINT /i.test(action))
|
|
134
|
+
return `ALTER TABLE ${table} ${action.replace(/^DROP CONSTRAINT (?!IF EXISTS )/i, "DROP CONSTRAINT IF EXISTS ")}`;
|
|
135
|
+
if (/^ENABLE ROW LEVEL SECURITY$/i.test(action))
|
|
136
|
+
return text;
|
|
137
|
+
const constraint = new RegExp(`^ADD CONSTRAINT (${identifier})\\s+`, "i").exec(action);
|
|
138
|
+
if (constraint !== null) {
|
|
139
|
+
const [row] = yield* sql `SELECT EXISTS (
|
|
140
|
+
SELECT 1 FROM pg_constraint WHERE conrelid = to_regclass(${table})
|
|
141
|
+
AND conname = ${unquote(constraint[1])}) AS exists`;
|
|
142
|
+
return row?.exists === true ? undefined : text;
|
|
143
|
+
}
|
|
144
|
+
const rename = new RegExp(`^RENAME COLUMN (${identifier}) TO (${identifier})$`, "i").exec(action);
|
|
145
|
+
if (rename !== null) {
|
|
146
|
+
const rows = yield* sql `SELECT attname AS name FROM pg_attribute
|
|
147
|
+
WHERE attrelid = to_regclass(${table}) AND NOT attisdropped
|
|
148
|
+
AND attname IN (${unquote(rename[1])}, ${unquote(rename[2])})`;
|
|
149
|
+
if (rows.some(({ name }) => name === unquote(rename[1])))
|
|
150
|
+
return text;
|
|
151
|
+
if (rows.some(({ name }) => name === unquote(rename[2])))
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return yield* new Migrator.MigrationError({
|
|
156
|
+
kind: "BadState",
|
|
157
|
+
message: `No replay rule for migration DDL: ${text}`,
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
/**
|
|
161
|
+
* DDL and its propagation run without BEGIN. A durable statement journal closes the
|
|
162
|
+
* crash gap between applying DDL and recording it; replay touches only an unfinished
|
|
163
|
+
* step, so a later drop/recreate cannot make an earlier create run again. Existing
|
|
164
|
+
* data rewrites set fixed values and are replayable, never increments or external work.
|
|
165
|
+
*/
|
|
166
|
+
export const nekiMigrator = ({ record, refuseSkipped, }) => Effect.scoped(Effect.gen(function* () {
|
|
167
|
+
const source = yield* SqlClient.SqlClient;
|
|
168
|
+
const connection = yield* source.reserve;
|
|
169
|
+
const barrier = yield* MigrationBarrier;
|
|
170
|
+
const boundary = yield* MigrationBoundary;
|
|
171
|
+
const reactivity = yield* Reactivity.make;
|
|
172
|
+
const direct = yield* SqlClient.make({
|
|
173
|
+
acquirer: Effect.succeed(connection),
|
|
174
|
+
compiler: PgClient.makeCompiler(),
|
|
175
|
+
spanAttributes: [],
|
|
176
|
+
}).pipe(Effect.provideService(Reactivity.Reactivity, reactivity));
|
|
177
|
+
for (const [name, ddl] of [
|
|
178
|
+
[
|
|
179
|
+
"history",
|
|
180
|
+
"CREATE TABLE IF NOT EXISTS actor_migrations (migration_id integer PRIMARY KEY, created_at timestamptz NOT NULL DEFAULT now(), name text NOT NULL)",
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
"journal",
|
|
184
|
+
"CREATE TABLE IF NOT EXISTS actor_migration_steps (migration_id integer NOT NULL, step integer NOT NULL, statement text NOT NULL, completed boolean NOT NULL DEFAULT false, PRIMARY KEY (migration_id, step))",
|
|
185
|
+
],
|
|
186
|
+
]) {
|
|
187
|
+
yield* barrier(connection);
|
|
188
|
+
yield* direct.unsafe(ddl);
|
|
189
|
+
yield* boundary(`bootstrap:${name}:ddl`);
|
|
190
|
+
yield* barrier(connection);
|
|
191
|
+
yield* boundary(`bootstrap:${name}:propagated`);
|
|
192
|
+
}
|
|
193
|
+
yield* Effect.provideService(refuseSkipped, SqlClient.SqlClient, direct);
|
|
194
|
+
const rows = yield* direct `SELECT migration_id::int AS id FROM actor_migrations`;
|
|
195
|
+
const latest = Math.max(0, ...rows.map(({ id }) => id));
|
|
196
|
+
const loaded = yield* Migrator.fromRecord(record);
|
|
197
|
+
if (new Set(loaded.map(([id]) => id)).size !== loaded.length)
|
|
198
|
+
return yield* new Migrator.MigrationError({
|
|
199
|
+
kind: "Duplicates",
|
|
200
|
+
message: "Found duplicate migration ids",
|
|
201
|
+
});
|
|
202
|
+
const applied = [];
|
|
203
|
+
for (const [id, name] of loaded) {
|
|
204
|
+
if (id <= latest)
|
|
205
|
+
continue;
|
|
206
|
+
const [progress] = yield* direct `SELECT EXISTS (
|
|
207
|
+
SELECT 1 FROM actor_migration_steps WHERE migration_id = ${id}) AS started`;
|
|
208
|
+
let step = 0;
|
|
209
|
+
const execute = (text, parameters, transform) => Effect.gen(function* () {
|
|
210
|
+
if (/^SELECT\b/i.test(text.trim()))
|
|
211
|
+
return yield* connection.execute(text, parameters, transform);
|
|
212
|
+
const mutation = text.trim();
|
|
213
|
+
const ddl = /^(CREATE|ALTER|DROP)\b/i.test(mutation);
|
|
214
|
+
if (!ddl && !(id === 26 && /^UPDATE\b/i.test(mutation)))
|
|
215
|
+
return yield* Effect.die(new Migrator.MigrationError({
|
|
216
|
+
kind: "BadState",
|
|
217
|
+
message: `Migration ${id} has no replayable data step: ${mutation}`,
|
|
218
|
+
}));
|
|
219
|
+
const alteration = alter.exec(mutation);
|
|
220
|
+
const statements = alteration !== null
|
|
221
|
+
? alterActions(alteration[2]).map((action) => `ALTER TABLE ${alteration[1]} ${action}`)
|
|
222
|
+
: [mutation];
|
|
223
|
+
for (const statement of statements) {
|
|
224
|
+
const position = ++step;
|
|
225
|
+
const point = `${id}:${position}`;
|
|
226
|
+
const [saved] = yield* direct `SELECT statement, completed
|
|
227
|
+
FROM actor_migration_steps WHERE migration_id = ${id} AND step = ${position}`;
|
|
228
|
+
if (saved !== undefined && saved.statement !== statement)
|
|
229
|
+
return yield* Effect.die(new Migrator.MigrationError({
|
|
230
|
+
kind: "BadState",
|
|
231
|
+
message: `Migration ${point} changed after it started`,
|
|
232
|
+
}));
|
|
233
|
+
if (saved?.completed === true)
|
|
234
|
+
continue;
|
|
235
|
+
if (saved === undefined) {
|
|
236
|
+
yield* direct `INSERT INTO actor_migration_steps (migration_id, step, statement) VALUES (${id}, ${position}, ${statement})`;
|
|
237
|
+
yield* boundary(`${point}:pending`);
|
|
238
|
+
}
|
|
239
|
+
if (ddl)
|
|
240
|
+
yield* barrier(connection);
|
|
241
|
+
const replay = ddl
|
|
242
|
+
? yield* replayDdl(direct, statement).pipe(Effect.orDie)
|
|
243
|
+
: statement;
|
|
244
|
+
if (replay !== undefined)
|
|
245
|
+
yield* connection.execute(replay, parameters, transform);
|
|
246
|
+
yield* boundary(`${point}:applied`);
|
|
247
|
+
if (ddl) {
|
|
248
|
+
yield* barrier(connection);
|
|
249
|
+
yield* boundary(`${point}:propagated`);
|
|
250
|
+
}
|
|
251
|
+
yield* direct `UPDATE actor_migration_steps SET completed = true WHERE migration_id = ${id} AND step = ${position}`;
|
|
252
|
+
yield* boundary(`${point}:completed`);
|
|
253
|
+
}
|
|
254
|
+
return [];
|
|
255
|
+
});
|
|
256
|
+
const client = yield* SqlClient.make({
|
|
257
|
+
acquirer: Effect.succeed({ ...connection, execute }),
|
|
258
|
+
compiler: PgClient.makeCompiler(),
|
|
259
|
+
spanAttributes: [],
|
|
260
|
+
}).pipe(Effect.provideService(Reactivity.Reactivity, reactivity));
|
|
261
|
+
yield* record[Object.keys(record).find((key) => Number(key.split("_")[0]) === id)].pipe(Effect.provideService(SqlClient.SqlClient, client), Effect.provideService(MigrationResuming, progress?.started === true));
|
|
262
|
+
const [removed] = yield* direct `SELECT EXISTS (
|
|
263
|
+
SELECT 1 FROM actor_migration_steps WHERE migration_id = ${id} AND step > ${step}
|
|
264
|
+
) AS exists`;
|
|
265
|
+
if (removed?.exists === true)
|
|
266
|
+
return yield* new Migrator.MigrationError({
|
|
267
|
+
kind: "BadState",
|
|
268
|
+
message: `Migration ${id} removed steps after it started`,
|
|
269
|
+
});
|
|
270
|
+
yield* direct `INSERT INTO actor_migrations (migration_id, name) VALUES (${id}, ${name})`;
|
|
271
|
+
yield* boundary(`${id}:recorded`);
|
|
272
|
+
applied.push([id, name]);
|
|
273
|
+
}
|
|
274
|
+
yield* Effect.provideService(refuseSkipped, SqlClient.SqlClient, direct);
|
|
275
|
+
return applied;
|
|
276
|
+
}));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PgConnection, PgPool } from "@effect/sql-pg";
|
|
2
|
+
import { Context, Effect } from "effect";
|
|
3
|
+
import type { Scope } from "effect";
|
|
4
|
+
import type { SqlError } from "effect/sql";
|
|
5
|
+
/**
|
|
6
|
+
* Whether the database is a Neki router. Turn sessions then run
|
|
7
|
+
* `NEKI_SESSION_SETTINGS`; nothing sets this outside the Neki conformance
|
|
8
|
+
* backend until Neki is a supported database.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NekiTurnSessions: Context.Reference<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Session settings a turn connection needs on Neki, in the order they are set.
|
|
13
|
+
* The router reads the transaction mode when `BEGIN` arrives, so both are set
|
|
14
|
+
* on the session before any turn opens a transaction, never inside one.
|
|
15
|
+
*/
|
|
16
|
+
export declare const NEKI_SESSION_SETTINGS: ReadonlyArray<string>;
|
|
17
|
+
/**
|
|
18
|
+
* `pool.get` with `NEKI_SESSION_SETTINGS` run once on each session, on its
|
|
19
|
+
* first lease, so a turn that reaches a second shard fails instead of
|
|
20
|
+
* committing on one shard and not the other. A session whose settings fail is
|
|
21
|
+
* dropped from the pool: a turn must never run on one that lacks them.
|
|
22
|
+
*/
|
|
23
|
+
export declare const nekiLease: (pool: PgPool.PgPool) => Effect.Effect<PgConnection.PgConnection, SqlError.SqlError, Scope.Scope>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Whether the database is a Neki router. Turn sessions then run
|
|
4
|
+
* `NEKI_SESSION_SETTINGS`; nothing sets this outside the Neki conformance
|
|
5
|
+
* backend until Neki is a supported database.
|
|
6
|
+
*/
|
|
7
|
+
export const NekiTurnSessions = Context.Reference("akter/NekiTurnSessions", {
|
|
8
|
+
defaultValue: () => false,
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Session settings a turn connection needs on Neki, in the order they are set.
|
|
12
|
+
* The router reads the transaction mode when `BEGIN` arrives, so both are set
|
|
13
|
+
* on the session before any turn opens a transaction, never inside one.
|
|
14
|
+
*/
|
|
15
|
+
export const NEKI_SESSION_SETTINGS = [
|
|
16
|
+
"SET __neki.tx_mode = 'single'",
|
|
17
|
+
"SET __neki.fanout = 'single'",
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* `pool.get` with `NEKI_SESSION_SETTINGS` run once on each session, on its
|
|
21
|
+
* first lease, so a turn that reaches a second shard fails instead of
|
|
22
|
+
* committing on one shard and not the other. A session whose settings fail is
|
|
23
|
+
* dropped from the pool: a turn must never run on one that lacks them.
|
|
24
|
+
*/
|
|
25
|
+
export const nekiLease = (pool) => {
|
|
26
|
+
const configured = new WeakSet();
|
|
27
|
+
const configure = (connection) => Effect.forEach(NEKI_SESSION_SETTINGS, (setting) => connection.query(setting, [], false), {
|
|
28
|
+
discard: true,
|
|
29
|
+
}).pipe(Effect.tap(() => Effect.sync(() => configured.add(connection))), Effect.tapCause(() => pool.invalidate(connection)));
|
|
30
|
+
return Effect.tap(pool.get, (connection) => configured.has(connection) ? Effect.void : configure(connection));
|
|
31
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { PgliteClient } from "@effect/sql-pglite";
|
|
2
|
+
import { DataDirLocked, DataDirVersion } from "../../errors/database.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The Postgres major the pinned PGlite embeds. Postgres cannot open a data
|
|
5
|
+
* directory another major wrote, so a PGlite upgrade across a major must
|
|
6
|
+
* change this, and a test checks it against what a fresh directory records.
|
|
7
|
+
*/
|
|
8
|
+
export declare const POSTGRES_MAJOR = "18";
|
|
9
|
+
/** The lock file inside a data directory; the kernel drops its lock when the holder dies. */
|
|
10
|
+
export declare const LOCK_FILE = ".akter.lock";
|
|
11
|
+
/**
|
|
12
|
+
* A PGlite client layer that owns fresh instances; a borrowed `liveClient`
|
|
13
|
+
* keeps its original methods and lifetime. A file-backed instance holds its
|
|
14
|
+
* data directory's lock from before it opens until after it closes, refuses a
|
|
15
|
+
* directory another Postgres major wrote, and refuses `relaxedDurability`
|
|
16
|
+
* because it acknowledges a commit before its WAL is written, so a crash
|
|
17
|
+
* could lose a turn whose receipt the caller already has. Closing waits for
|
|
18
|
+
* in-flight queries: interrupted SQL fibers can leave protocol exchanges
|
|
19
|
+
* running, and closing PGlite during one deadlocks its single connection.
|
|
20
|
+
*/
|
|
21
|
+
export declare const pglite: (config?: PgliteClient.PgliteClientConfig) => import("effect/Layer").Layer<PgliteClient.PgliteClient | import("effect/sql/SqlClient").SqlClient, DataDirLocked | DataDirVersion | import("effect/sql/SqlError").SqlError, never>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { PgliteClient } from "@effect/sql-pglite";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { DataDirLocked, DataDirVersion } from "../../errors/database.js";
|
|
5
|
+
import { flockExclusive } from "./flock.js";
|
|
6
|
+
/**
|
|
7
|
+
* The Postgres major the pinned PGlite embeds. Postgres cannot open a data
|
|
8
|
+
* directory another major wrote, so a PGlite upgrade across a major must
|
|
9
|
+
* change this, and a test checks it against what a fresh directory records.
|
|
10
|
+
*/
|
|
11
|
+
export const POSTGRES_MAJOR = "18";
|
|
12
|
+
/** The lock file inside a data directory; the kernel drops its lock when the holder dies. */
|
|
13
|
+
export const LOCK_FILE = ".akter.lock";
|
|
14
|
+
/** A filesystem data directory, or undefined for an in-memory database. */
|
|
15
|
+
const directoryOf = (dataDir) => {
|
|
16
|
+
if (dataDir === undefined || dataDir.startsWith("memory://"))
|
|
17
|
+
return undefined;
|
|
18
|
+
return dataDir.startsWith("file://") ? dataDir.slice("file://".length) : dataDir;
|
|
19
|
+
};
|
|
20
|
+
/** Holds an exclusive lock on the directory's lock file for the scope, so a second instance, in this process or another, never opens PGlite. */
|
|
21
|
+
const lockDataDir = (directory) => Effect.gen(function* () {
|
|
22
|
+
if (!(yield* flockExclusive(`${directory}/${LOCK_FILE}`)))
|
|
23
|
+
return yield* DataDirLocked.make({ dataDir: directory });
|
|
24
|
+
});
|
|
25
|
+
/** Refuses a directory another Postgres major wrote before PGlite fails on it opaquely. */
|
|
26
|
+
const checkVersion = (directory) => Effect.gen(function* () {
|
|
27
|
+
const found = yield* Effect.promise(() => import("node:fs/promises").then(({ readFile }) => readFile(`${directory}/PG_VERSION`, "utf8").then((text) => text.trim(), (error) => {
|
|
28
|
+
if (error.code === "ENOENT")
|
|
29
|
+
return undefined;
|
|
30
|
+
throw error;
|
|
31
|
+
})));
|
|
32
|
+
if (found !== undefined && found !== POSTGRES_MAJOR)
|
|
33
|
+
return yield* DataDirVersion.make({ dataDir: directory, found, expected: POSTGRES_MAJOR });
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* A PGlite client layer that owns fresh instances; a borrowed `liveClient`
|
|
37
|
+
* keeps its original methods and lifetime. A file-backed instance holds its
|
|
38
|
+
* data directory's lock from before it opens until after it closes, refuses a
|
|
39
|
+
* directory another Postgres major wrote, and refuses `relaxedDurability`
|
|
40
|
+
* because it acknowledges a commit before its WAL is written, so a crash
|
|
41
|
+
* could lose a turn whose receipt the caller already has. Closing waits for
|
|
42
|
+
* in-flight queries: interrupted SQL fibers can leave protocol exchanges
|
|
43
|
+
* running, and closing PGlite during one deadlocks its single connection.
|
|
44
|
+
*/
|
|
45
|
+
export const pglite = (config = {}) => {
|
|
46
|
+
if ("liveClient" in config)
|
|
47
|
+
return PgliteClient.layer(config);
|
|
48
|
+
const directory = directoryOf(config.dataDir);
|
|
49
|
+
return PgliteClient.layerFrom(Effect.gen(function* () {
|
|
50
|
+
if (directory !== undefined) {
|
|
51
|
+
if (config.relaxedDurability === true)
|
|
52
|
+
return yield* Effect.die(new Error("A file-backed PGlite database refuses relaxedDurability"));
|
|
53
|
+
yield* lockDataDir(directory);
|
|
54
|
+
yield* checkVersion(directory);
|
|
55
|
+
}
|
|
56
|
+
const pending = new Set();
|
|
57
|
+
const database = yield* Effect.acquireRelease(Effect.sync(() => {
|
|
58
|
+
const database = new PGlite(config);
|
|
59
|
+
const query = database.query.bind(database);
|
|
60
|
+
database.query = (...args) => {
|
|
61
|
+
const promise = query(...args);
|
|
62
|
+
pending.add(promise);
|
|
63
|
+
return promise.finally(() => pending.delete(promise));
|
|
64
|
+
};
|
|
65
|
+
return database;
|
|
66
|
+
}), (database) => Effect.gen(function* () {
|
|
67
|
+
while (pending.size > 0)
|
|
68
|
+
yield* Effect.promise(() => Promise.allSettled(pending));
|
|
69
|
+
yield* Effect.promise(() => database.close());
|
|
70
|
+
}));
|
|
71
|
+
yield* Effect.promise(() => database.waitReady);
|
|
72
|
+
return yield* PgliteClient.make({ ...config, liveClient: database });
|
|
73
|
+
}));
|
|
74
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
import type { SqlClient } from "effect/sql";
|
|
4
|
+
/**
|
|
5
|
+
* The primary's WAL insert position as a decimal string, and the database
|
|
6
|
+
* clock in epoch milliseconds. Read on a turn's session after its `COMMIT` or
|
|
7
|
+
* `ROLLBACK`, the position is at least the end of every commit record that
|
|
8
|
+
* session could have observed, so a replica that has replayed this far sees
|
|
9
|
+
* the turn's writes. An LSN cannot be known inside the transaction that
|
|
10
|
+
* writes it, which is why nothing stores it. The clock is read after the
|
|
11
|
+
* transaction ended, in the same flight, so an external caller's expiry
|
|
12
|
+
* recheck before its reply costs no round trip of its own.
|
|
13
|
+
*/
|
|
14
|
+
export declare const COMMIT_VERSION = "SELECT (pg_current_wal_insert_lsn() - '0/0')::text AS version,\n floor(extract(epoch FROM clock_timestamp()) * 1000)::text AS now";
|
|
15
|
+
/**
|
|
16
|
+
* This runner's streaming replica, if it has one. A query that carries a
|
|
17
|
+
* version reads there only once the replica has replayed past it, and
|
|
18
|
+
* otherwise reads the primary.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ReadReplica: Context.Reference<SqlClient.SqlClient | undefined>;
|
|
21
|
+
/** Provides `ReadReplica` as a client for `options`, or none when no replica is configured. */
|
|
22
|
+
export declare const replicaLayer: (options: PgClient.PgPoolConfig | undefined) => Layer.Layer<never, import("effect/sql/SqlError").SqlError, never>;
|
|
23
|
+
/**
|
|
24
|
+
* The primary's pool for queries, apart from the off-turn pool. Command
|
|
25
|
+
* admission and the receipt replay it serves then never queue behind a burst
|
|
26
|
+
* of readers, nor readers behind admission. Queries of types with owned
|
|
27
|
+
* tables or blobs stay on the off-turn pool, because the owned-rows binding
|
|
28
|
+
* is built on that client.
|
|
29
|
+
*/
|
|
30
|
+
export declare const QueryPool: Context.Reference<SqlClient.SqlClient | undefined>;
|
|
31
|
+
/** Provides `QueryPool` as a bounded, first-come, first-served pool for `options`. */
|
|
32
|
+
export declare const queryPoolLayer: (options: PgClient.PgPoolConfig) => Layer.Layer<never, import("effect/sql/SqlError").SqlError, never>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the replica has replayed WAL through `version`. A server not in
|
|
35
|
+
* recovery reports no replay position, so it never counts as caught up.
|
|
36
|
+
*
|
|
37
|
+
* This runs as its own statement before the query's reads: a statement's
|
|
38
|
+
* snapshot is taken before its functions run, so a check in the same statement
|
|
39
|
+
* could pass after a snapshot that predates the replayed commit.
|
|
40
|
+
*/
|
|
41
|
+
export declare const caughtUp: (replica: SqlClient.SqlClient, version: string) => Effect.Effect<boolean, import("effect/sql/SqlError").SqlError, never>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
import { Reactivity } from "effect/reactivity";
|
|
4
|
+
import { boundedPool } from "./bounded.js";
|
|
5
|
+
/**
|
|
6
|
+
* The primary's WAL insert position as a decimal string, and the database
|
|
7
|
+
* clock in epoch milliseconds. Read on a turn's session after its `COMMIT` or
|
|
8
|
+
* `ROLLBACK`, the position is at least the end of every commit record that
|
|
9
|
+
* session could have observed, so a replica that has replayed this far sees
|
|
10
|
+
* the turn's writes. An LSN cannot be known inside the transaction that
|
|
11
|
+
* writes it, which is why nothing stores it. The clock is read after the
|
|
12
|
+
* transaction ended, in the same flight, so an external caller's expiry
|
|
13
|
+
* recheck before its reply costs no round trip of its own.
|
|
14
|
+
*/
|
|
15
|
+
export const COMMIT_VERSION = `SELECT (pg_current_wal_insert_lsn() - '0/0')::text AS version,
|
|
16
|
+
floor(extract(epoch FROM clock_timestamp()) * 1000)::text AS now`;
|
|
17
|
+
/**
|
|
18
|
+
* This runner's streaming replica, if it has one. A query that carries a
|
|
19
|
+
* version reads there only once the replica has replayed past it, and
|
|
20
|
+
* otherwise reads the primary.
|
|
21
|
+
*/
|
|
22
|
+
export const ReadReplica = Context.Reference("@rikalabs/akter/runtime/database/replica/ReadReplica", { defaultValue: () => undefined });
|
|
23
|
+
/** Provides `ReadReplica` as a client for `options`, or none when no replica is configured. */
|
|
24
|
+
export const replicaLayer = (options) => Layer.effect(ReadReplica, Effect.gen(function* () {
|
|
25
|
+
if (options === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
return yield* boundedPool(options);
|
|
28
|
+
})).pipe(Layer.provide(Reactivity.layer));
|
|
29
|
+
/**
|
|
30
|
+
* The primary's pool for queries, apart from the off-turn pool. Command
|
|
31
|
+
* admission and the receipt replay it serves then never queue behind a burst
|
|
32
|
+
* of readers, nor readers behind admission. Queries of types with owned
|
|
33
|
+
* tables or blobs stay on the off-turn pool, because the owned-rows binding
|
|
34
|
+
* is built on that client.
|
|
35
|
+
*/
|
|
36
|
+
export const QueryPool = Context.Reference("@rikalabs/akter/runtime/database/replica/QueryPool", { defaultValue: () => undefined });
|
|
37
|
+
/** Provides `QueryPool` as a bounded, first-come, first-served pool for `options`. */
|
|
38
|
+
export const queryPoolLayer = (options) => Layer.effect(QueryPool, boundedPool(options)).pipe(Layer.provide(Reactivity.layer));
|
|
39
|
+
/**
|
|
40
|
+
* Whether the replica has replayed WAL through `version`. A server not in
|
|
41
|
+
* recovery reports no replay position, so it never counts as caught up.
|
|
42
|
+
*
|
|
43
|
+
* This runs as its own statement before the query's reads: a statement's
|
|
44
|
+
* snapshot is taken before its functions run, so a check in the same statement
|
|
45
|
+
* could pass after a snapshot that predates the replayed commit.
|
|
46
|
+
*/
|
|
47
|
+
export const caughtUp = Effect.fnUntraced(function* (replica, version) {
|
|
48
|
+
const rows = yield* replica `
|
|
49
|
+
SELECT coalesce(pg_last_wal_replay_lsn() - '0/0' >= ${version}::numeric, false) AS ready`;
|
|
50
|
+
return rows[0]?.ready === true;
|
|
51
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
/** One inclusive bucket range on one database shard, independent of actor compute ownership. */
|
|
4
|
+
export interface BucketRange {
|
|
5
|
+
readonly first: number;
|
|
6
|
+
readonly last: number;
|
|
7
|
+
/** Neki's shard UID; absent on an ordinary single database. */
|
|
8
|
+
readonly shard?: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
/** The data ranges this runner may scan; ordinary databases have one range and no session setting. */
|
|
11
|
+
export declare const ShardMap: Context.Reference<readonly BucketRange[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Gives each targeted range its own session for the lifetime of the caller's
|
|
14
|
+
* scope. A shard setting cannot leak into the shared off-turn pool, and a
|
|
15
|
+
* failed setup closes the session instead of returning it to another user.
|
|
16
|
+
* Untargeted ranges reuse the existing client without a setting or a lease.
|
|
17
|
+
*/
|
|
18
|
+
export declare const shardClients: Effect.Effect<{
|
|
19
|
+
range: BucketRange;
|
|
20
|
+
sql: SqlClient.SqlClient;
|
|
21
|
+
}[], import("effect/sql/SqlError").SqlError, import("effect/Scope").Scope | SqlClient.SqlClient>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
|
+
import { BUCKETS } from "../turn/outbox.js";
|
|
5
|
+
/** The data ranges this runner may scan; ordinary databases have one range and no session setting. */
|
|
6
|
+
export const ShardMap = Context.Reference("akter/ShardMap", {
|
|
7
|
+
defaultValue: () => [BUCKETS],
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Gives each targeted range its own session for the lifetime of the caller's
|
|
11
|
+
* scope. A shard setting cannot leak into the shared off-turn pool, and a
|
|
12
|
+
* failed setup closes the session instead of returning it to another user.
|
|
13
|
+
* Untargeted ranges reuse the existing client without a setting or a lease.
|
|
14
|
+
*/
|
|
15
|
+
export const shardClients = Effect.gen(function* () {
|
|
16
|
+
const sql = yield* SqlClient.SqlClient;
|
|
17
|
+
const ranges = yield* ShardMap;
|
|
18
|
+
const sorted = [...ranges].sort((a, b) => a.first - b.first);
|
|
19
|
+
for (const [index, range] of sorted.entries()) {
|
|
20
|
+
if (!Number.isInteger(range.first) ||
|
|
21
|
+
!Number.isInteger(range.last) ||
|
|
22
|
+
range.first < BUCKETS.first ||
|
|
23
|
+
range.last > BUCKETS.last ||
|
|
24
|
+
range.first > range.last ||
|
|
25
|
+
(index > 0 && sorted[index - 1].last >= range.first) ||
|
|
26
|
+
range.shard === "")
|
|
27
|
+
return yield* Effect.die(new Error("Invalid or overlapping shard bucket ranges"));
|
|
28
|
+
}
|
|
29
|
+
return yield* Effect.forEach(ranges, (range) => Effect.gen(function* () {
|
|
30
|
+
if (range.shard === undefined)
|
|
31
|
+
return { range, sql };
|
|
32
|
+
const postgres = yield* PgClient.PgClient;
|
|
33
|
+
const client = Context.get(yield* Layer.build(PgClient.layerFrom(PgClient.makeClient(postgres.config))), SqlClient.SqlClient);
|
|
34
|
+
yield* client `SET __neki.shard = ${client.literal(`'${range.shard.replaceAll("'", "''")}'`)}`;
|
|
35
|
+
return { range, sql: client };
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
/**
|
|
4
|
+
* Runs `effect` in a read-only, repeatable-read transaction, so every read in
|
|
5
|
+
* it sees one snapshot and Postgres refuses any write it could attempt.
|
|
6
|
+
*/
|
|
7
|
+
export declare const inReadOnlySnapshot: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | import("effect/sql/SqlError").SqlError, R | SqlClient.SqlClient>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
/**
|
|
4
|
+
* Runs `effect` in a read-only, repeatable-read transaction, so every read in
|
|
5
|
+
* it sees one snapshot and Postgres refuses any write it could attempt.
|
|
6
|
+
*/
|
|
7
|
+
export const inReadOnlySnapshot = (effect) => Effect.flatMap(SqlClient.SqlClient, (sql) => sql.withTransaction(sql `SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY`.pipe(Effect.andThen(effect))));
|