@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,1018 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { PgClient } from "@effect/sql-pg";
|
|
3
|
+
import { Migrator, SqlClient } from "effect/sql";
|
|
4
|
+
import { NekiTurnSessions } from "./neki/session.js";
|
|
5
|
+
import { MigrationResuming, nekiMigrator, withMigrationCoordination } from "./neki/migrations.js";
|
|
6
|
+
/** Every framework migration by id, applied in order above the latest applied id. */
|
|
7
|
+
export const migrations = {
|
|
8
|
+
"0001_foundation": Effect.gen(function* () {
|
|
9
|
+
const sql = yield* SqlClient.SqlClient;
|
|
10
|
+
yield* sql `CREATE TABLE actor_deployment (
|
|
11
|
+
singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
|
|
12
|
+
protocol integer NOT NULL,
|
|
13
|
+
retry_window_ms bigint NOT NULL CHECK (retry_window_ms > 0)
|
|
14
|
+
)`;
|
|
15
|
+
yield* sql `CREATE TABLE actor_generations (
|
|
16
|
+
tenant_id text NOT NULL,
|
|
17
|
+
actor_type text NOT NULL,
|
|
18
|
+
actor_id text NOT NULL,
|
|
19
|
+
generation bigint NOT NULL DEFAULT 0,
|
|
20
|
+
PRIMARY KEY (tenant_id, actor_type, actor_id)
|
|
21
|
+
)`;
|
|
22
|
+
yield* sql `CREATE TABLE actor_state (
|
|
23
|
+
tenant_id text NOT NULL,
|
|
24
|
+
actor_type text NOT NULL,
|
|
25
|
+
actor_id text NOT NULL,
|
|
26
|
+
key text NOT NULL,
|
|
27
|
+
value jsonb NOT NULL,
|
|
28
|
+
PRIMARY KEY (tenant_id, actor_type, actor_id, key),
|
|
29
|
+
FOREIGN KEY (tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
30
|
+
)`;
|
|
31
|
+
yield* sql `CREATE TABLE actor_receipts (
|
|
32
|
+
tenant_id text NOT NULL,
|
|
33
|
+
actor_type text NOT NULL,
|
|
34
|
+
actor_id text NOT NULL,
|
|
35
|
+
command_id text NOT NULL,
|
|
36
|
+
command text NOT NULL,
|
|
37
|
+
payload_hash text NOT NULL,
|
|
38
|
+
caller_key text NOT NULL,
|
|
39
|
+
outcome text NOT NULL,
|
|
40
|
+
expires_at_ms bigint NOT NULL,
|
|
41
|
+
PRIMARY KEY (tenant_id, actor_type, actor_id, command_id),
|
|
42
|
+
FOREIGN KEY (tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
43
|
+
)`;
|
|
44
|
+
}),
|
|
45
|
+
"0002_creation": Effect.gen(function* () {
|
|
46
|
+
const sql = yield* SqlClient.SqlClient;
|
|
47
|
+
yield* sql `ALTER TABLE actor_generations ADD COLUMN created boolean NOT NULL DEFAULT false`;
|
|
48
|
+
}),
|
|
49
|
+
/**
|
|
50
|
+
* Every framework row carries its routing key, leading the primary key so a shard index can
|
|
51
|
+
* place it; state is opaque bytea.
|
|
52
|
+
*
|
|
53
|
+
* Placement and its encoding decide every row's routing key, so a change would fork each
|
|
54
|
+
* existing actor into a second identity.
|
|
55
|
+
*/
|
|
56
|
+
"0003_routing_state": Effect.gen(function* () {
|
|
57
|
+
const sql = yield* SqlClient.SqlClient;
|
|
58
|
+
if (!(yield* MigrationResuming) &&
|
|
59
|
+
(yield* sql `SELECT count(*)::int AS rows FROM actor_generations`)[0].rows > 0)
|
|
60
|
+
return yield* Effect.die(new Error("0003 requires an empty foundation database; M0 stored no production data"));
|
|
61
|
+
yield* sql `DROP TABLE actor_receipts`;
|
|
62
|
+
yield* sql `DROP TABLE actor_state`;
|
|
63
|
+
yield* sql `DROP TABLE actor_generations`;
|
|
64
|
+
yield* sql `CREATE TABLE actor_generations (
|
|
65
|
+
routing_key bigint NOT NULL,
|
|
66
|
+
tenant_id text NOT NULL,
|
|
67
|
+
actor_type text NOT NULL,
|
|
68
|
+
actor_id text NOT NULL,
|
|
69
|
+
generation bigint NOT NULL DEFAULT 0,
|
|
70
|
+
created boolean NOT NULL DEFAULT false,
|
|
71
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id)
|
|
72
|
+
) WITH (fillfactor = 80)`;
|
|
73
|
+
yield* sql `CREATE TABLE actor_state (
|
|
74
|
+
routing_key bigint NOT NULL,
|
|
75
|
+
tenant_id text NOT NULL,
|
|
76
|
+
actor_type text NOT NULL,
|
|
77
|
+
actor_id text NOT NULL,
|
|
78
|
+
key text NOT NULL,
|
|
79
|
+
value bytea NOT NULL,
|
|
80
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, key),
|
|
81
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
82
|
+
) WITH (fillfactor = 80)`;
|
|
83
|
+
yield* sql `CREATE TABLE actor_receipts (
|
|
84
|
+
routing_key bigint NOT NULL,
|
|
85
|
+
tenant_id text NOT NULL,
|
|
86
|
+
actor_type text NOT NULL,
|
|
87
|
+
actor_id text NOT NULL,
|
|
88
|
+
command_id text NOT NULL,
|
|
89
|
+
command text NOT NULL,
|
|
90
|
+
payload_hash text NOT NULL,
|
|
91
|
+
caller_key text NOT NULL,
|
|
92
|
+
outcome text NOT NULL,
|
|
93
|
+
expires_at_ms bigint NOT NULL,
|
|
94
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, command_id),
|
|
95
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
96
|
+
)`;
|
|
97
|
+
yield* sql `CREATE TABLE actor_placements (
|
|
98
|
+
actor_type text PRIMARY KEY,
|
|
99
|
+
placement text NOT NULL CHECK (placement IN ('tenant', 'actor')),
|
|
100
|
+
encoding integer NOT NULL
|
|
101
|
+
)`;
|
|
102
|
+
}),
|
|
103
|
+
/**
|
|
104
|
+
* Every intent and timer lives on its sender's shard. `bucket` is the top eight bits of
|
|
105
|
+
* `routing_key`, so the relay probes `(bucket, due_at_ms)` and never reads actors with
|
|
106
|
+
* nothing due.
|
|
107
|
+
*/
|
|
108
|
+
"0004_outbox": Effect.gen(function* () {
|
|
109
|
+
const sql = yield* SqlClient.SqlClient;
|
|
110
|
+
yield* sql `CREATE TABLE actor_outbox (
|
|
111
|
+
routing_key bigint NOT NULL,
|
|
112
|
+
intent_id text NOT NULL,
|
|
113
|
+
bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
|
|
114
|
+
due_at_ms bigint NOT NULL,
|
|
115
|
+
tenant_id text NOT NULL,
|
|
116
|
+
actor_type text NOT NULL,
|
|
117
|
+
actor_id text NOT NULL,
|
|
118
|
+
timer_key text,
|
|
119
|
+
target_type text NOT NULL,
|
|
120
|
+
target_id text NOT NULL,
|
|
121
|
+
command text NOT NULL,
|
|
122
|
+
payload text NOT NULL,
|
|
123
|
+
caller text NOT NULL,
|
|
124
|
+
attempts integer NOT NULL DEFAULT 0,
|
|
125
|
+
PRIMARY KEY (routing_key, intent_id),
|
|
126
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
127
|
+
)`;
|
|
128
|
+
yield* sql `CREATE INDEX actor_outbox_due ON actor_outbox (bucket, due_at_ms)`;
|
|
129
|
+
yield* sql `CREATE UNIQUE INDEX actor_outbox_timer
|
|
130
|
+
ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, timer_key)
|
|
131
|
+
WHERE timer_key IS NOT NULL`;
|
|
132
|
+
}),
|
|
133
|
+
/**
|
|
134
|
+
* Application tables come from drizzle-kit; the framework only records which actor type owns
|
|
135
|
+
* each one, so a second owner cannot read its rows.
|
|
136
|
+
*/
|
|
137
|
+
"0005_tables": Effect.gen(function* () {
|
|
138
|
+
const sql = yield* SqlClient.SqlClient;
|
|
139
|
+
yield* sql `CREATE TABLE actor_tables (
|
|
140
|
+
table_schema text NOT NULL,
|
|
141
|
+
table_name text NOT NULL,
|
|
142
|
+
actor_type text NOT NULL,
|
|
143
|
+
PRIMARY KEY (table_schema, table_name)
|
|
144
|
+
)`;
|
|
145
|
+
}),
|
|
146
|
+
/**
|
|
147
|
+
* Events share the actor's routing key and commit with its turn. The sequence counter lives
|
|
148
|
+
* on the fenced generation row, so a pruned stream never reissues a cursor.
|
|
149
|
+
*/
|
|
150
|
+
"0006_events": Effect.gen(function* () {
|
|
151
|
+
const sql = yield* SqlClient.SqlClient;
|
|
152
|
+
yield* sql `ALTER TABLE actor_generations ADD COLUMN event_sequence bigint NOT NULL DEFAULT 0`;
|
|
153
|
+
yield* sql `CREATE TABLE actor_events (
|
|
154
|
+
routing_key bigint NOT NULL,
|
|
155
|
+
tenant_id text NOT NULL,
|
|
156
|
+
actor_type text NOT NULL,
|
|
157
|
+
actor_id text NOT NULL,
|
|
158
|
+
sequence bigint NOT NULL CHECK (sequence > 0),
|
|
159
|
+
event text NOT NULL,
|
|
160
|
+
command_id text NOT NULL,
|
|
161
|
+
value bytea NOT NULL,
|
|
162
|
+
emitted_at_ms bigint NOT NULL,
|
|
163
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, sequence),
|
|
164
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
165
|
+
)`;
|
|
166
|
+
}),
|
|
167
|
+
/**
|
|
168
|
+
* An effect is an outbox row whose executor runs when it is due. On success or exhaustion it
|
|
169
|
+
* becomes an intent to its route, so the route is delivered like any intent, with the effect
|
|
170
|
+
* id as its command id. `ambiguous` records whether the last attempt's outcome is unknown.
|
|
171
|
+
*
|
|
172
|
+
* Exhausted effects stay visible to operators after their row settles.
|
|
173
|
+
*/
|
|
174
|
+
"0008_effects": Effect.gen(function* () {
|
|
175
|
+
const sql = yield* SqlClient.SqlClient;
|
|
176
|
+
yield* sql `ALTER TABLE actor_outbox
|
|
177
|
+
ADD COLUMN kind text NOT NULL DEFAULT 'intent' CHECK (kind IN ('intent', 'effect')),
|
|
178
|
+
ADD COLUMN last_error text,
|
|
179
|
+
ADD COLUMN ambiguous boolean NOT NULL DEFAULT false`;
|
|
180
|
+
yield* sql `CREATE TABLE actor_dead_letters (
|
|
181
|
+
routing_key bigint NOT NULL,
|
|
182
|
+
effect_id text NOT NULL,
|
|
183
|
+
tenant_id text NOT NULL,
|
|
184
|
+
actor_type text NOT NULL,
|
|
185
|
+
actor_id text NOT NULL,
|
|
186
|
+
effect text NOT NULL,
|
|
187
|
+
payload text NOT NULL,
|
|
188
|
+
attempts integer NOT NULL,
|
|
189
|
+
cause text NOT NULL,
|
|
190
|
+
ambiguous boolean NOT NULL,
|
|
191
|
+
dead_at_ms bigint NOT NULL,
|
|
192
|
+
PRIMARY KEY (routing_key, effect_id),
|
|
193
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
194
|
+
)`;
|
|
195
|
+
}),
|
|
196
|
+
/**
|
|
197
|
+
* Blob entries are chunked bytea beside the actor's other rows: `append` adds a chunk
|
|
198
|
+
* without rewriting earlier ones, and `compact` folds them into chunk 0.
|
|
199
|
+
*/
|
|
200
|
+
"0009_blobs": Effect.gen(function* () {
|
|
201
|
+
const sql = yield* SqlClient.SqlClient;
|
|
202
|
+
yield* sql `CREATE TABLE actor_blobs (
|
|
203
|
+
routing_key bigint NOT NULL,
|
|
204
|
+
tenant_id text NOT NULL,
|
|
205
|
+
actor_type text NOT NULL,
|
|
206
|
+
actor_id text NOT NULL,
|
|
207
|
+
blob text NOT NULL,
|
|
208
|
+
name text NOT NULL,
|
|
209
|
+
chunk integer NOT NULL CHECK (chunk >= 0),
|
|
210
|
+
bytes bytea NOT NULL,
|
|
211
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, blob, name, chunk),
|
|
212
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
213
|
+
)`;
|
|
214
|
+
}),
|
|
215
|
+
/**
|
|
216
|
+
* Cleanup reads each actor type's oldest receipts and events by age, and keeps a receipt
|
|
217
|
+
* while an outbox row with its id can still be redelivered; these indexes keep every cleanup
|
|
218
|
+
* batch a range read.
|
|
219
|
+
*/
|
|
220
|
+
"0010_retention": Effect.gen(function* () {
|
|
221
|
+
const sql = yield* SqlClient.SqlClient;
|
|
222
|
+
yield* sql `CREATE INDEX actor_receipts_expiry ON actor_receipts (actor_type, expires_at_ms)`;
|
|
223
|
+
yield* sql `CREATE INDEX actor_events_emitted ON actor_events (actor_type, emitted_at_ms)`;
|
|
224
|
+
yield* sql `CREATE INDEX actor_outbox_intent ON actor_outbox (intent_id)`;
|
|
225
|
+
}),
|
|
226
|
+
/**
|
|
227
|
+
* Claims and backoff move `due_at_ms`, so `scheduled_at_ms` keeps the time a row first
|
|
228
|
+
* became due. `kind` follows `bucket` in the due index so intent scans never read effect
|
|
229
|
+
* rows waiting for an executor, and the reverse.
|
|
230
|
+
*/
|
|
231
|
+
"0011_relay": Effect.gen(function* () {
|
|
232
|
+
const sql = yield* SqlClient.SqlClient;
|
|
233
|
+
yield* sql `ALTER TABLE actor_outbox ADD COLUMN scheduled_at_ms bigint NOT NULL`;
|
|
234
|
+
yield* sql `CREATE INDEX actor_outbox_due_kind ON actor_outbox (bucket, kind, due_at_ms)`;
|
|
235
|
+
yield* sql `DROP INDEX actor_outbox_due`;
|
|
236
|
+
}),
|
|
237
|
+
/**
|
|
238
|
+
* Workflow executions, their recorded steps, and the manifests deployments accepted. Every
|
|
239
|
+
* execution and step row lives on its owner's shard; a step row is written pending before
|
|
240
|
+
* its work starts and settled once.
|
|
241
|
+
*/
|
|
242
|
+
"0012_workflows": Effect.gen(function* () {
|
|
243
|
+
const sql = yield* SqlClient.SqlClient;
|
|
244
|
+
yield* sql `CREATE TABLE actor_workflow_executions (
|
|
245
|
+
routing_key bigint NOT NULL,
|
|
246
|
+
execution_id text NOT NULL,
|
|
247
|
+
bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
|
|
248
|
+
tenant_id text NOT NULL,
|
|
249
|
+
actor_type text NOT NULL,
|
|
250
|
+
actor_id text NOT NULL,
|
|
251
|
+
workflow text NOT NULL,
|
|
252
|
+
workflow_key text NOT NULL,
|
|
253
|
+
manifest_hash text NOT NULL,
|
|
254
|
+
payload bytea NOT NULL,
|
|
255
|
+
caller text NOT NULL,
|
|
256
|
+
event_cursor bigint NOT NULL,
|
|
257
|
+
status text NOT NULL CHECK (status IN ('running', 'suspended', 'finished')),
|
|
258
|
+
interrupt boolean NOT NULL DEFAULT false,
|
|
259
|
+
result bytea,
|
|
260
|
+
started_at_ms bigint NOT NULL,
|
|
261
|
+
finished_at_ms bigint,
|
|
262
|
+
PRIMARY KEY (routing_key, execution_id),
|
|
263
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations,
|
|
264
|
+
CHECK ((status = 'finished') = (result IS NOT NULL AND finished_at_ms IS NOT NULL))
|
|
265
|
+
)`;
|
|
266
|
+
yield* sql `CREATE INDEX actor_workflow_executions_open
|
|
267
|
+
ON actor_workflow_executions (routing_key, tenant_id, actor_type, actor_id)
|
|
268
|
+
WHERE status <> 'finished'`;
|
|
269
|
+
yield* sql `CREATE INDEX actor_workflow_executions_finished
|
|
270
|
+
ON actor_workflow_executions (bucket, finished_at_ms)
|
|
271
|
+
WHERE status = 'finished'`;
|
|
272
|
+
yield* sql `CREATE INDEX actor_workflow_executions_check
|
|
273
|
+
ON actor_workflow_executions (actor_type, workflow, manifest_hash)
|
|
274
|
+
WHERE status <> 'finished'`;
|
|
275
|
+
yield* sql `CREATE TABLE actor_workflow_step (
|
|
276
|
+
routing_key bigint NOT NULL,
|
|
277
|
+
execution_id text NOT NULL,
|
|
278
|
+
tenant_id text NOT NULL,
|
|
279
|
+
actor_type text NOT NULL,
|
|
280
|
+
actor_id text NOT NULL,
|
|
281
|
+
step text NOT NULL,
|
|
282
|
+
attempt integer NOT NULL,
|
|
283
|
+
kind text NOT NULL CHECK (kind IN ('activity', 'clock', 'deferred', 'wait', 'version')),
|
|
284
|
+
exit bytea,
|
|
285
|
+
due_at_ms bigint,
|
|
286
|
+
wait_event text,
|
|
287
|
+
wait_after bigint,
|
|
288
|
+
scanned bigint,
|
|
289
|
+
matched bigint,
|
|
290
|
+
version integer,
|
|
291
|
+
started_at_ms bigint NOT NULL,
|
|
292
|
+
settled_at_ms bigint,
|
|
293
|
+
PRIMARY KEY (routing_key, execution_id, step, attempt),
|
|
294
|
+
FOREIGN KEY (routing_key, execution_id) REFERENCES actor_workflow_executions ON DELETE CASCADE,
|
|
295
|
+
CHECK ((kind = 'wait') = (wait_event IS NOT NULL AND wait_after IS NOT NULL AND scanned IS NOT NULL)),
|
|
296
|
+
CHECK ((kind = 'clock') <= (due_at_ms IS NOT NULL)),
|
|
297
|
+
CHECK ((kind = 'version') = (version IS NOT NULL AND exit IS NULL))
|
|
298
|
+
)`;
|
|
299
|
+
yield* sql `CREATE INDEX actor_workflow_step_waits
|
|
300
|
+
ON actor_workflow_step (routing_key, tenant_id, actor_type, actor_id, wait_event)
|
|
301
|
+
WHERE kind = 'wait' AND exit IS NULL`;
|
|
302
|
+
yield* sql `CREATE TABLE actor_workflow_manifests (
|
|
303
|
+
actor_type text NOT NULL,
|
|
304
|
+
workflow text NOT NULL,
|
|
305
|
+
manifest_hash text NOT NULL,
|
|
306
|
+
manifest jsonb NOT NULL,
|
|
307
|
+
accepted_at_ms bigint NOT NULL,
|
|
308
|
+
PRIMARY KEY (actor_type, workflow, manifest_hash)
|
|
309
|
+
)`;
|
|
310
|
+
}),
|
|
311
|
+
/**
|
|
312
|
+
* The placement join keeps every view from being automatically updatable, so writes fail
|
|
313
|
+
* without triggers or rules to maintain.
|
|
314
|
+
*
|
|
315
|
+
* The catalog is how a tool checks which view versions a database has.
|
|
316
|
+
*/
|
|
317
|
+
"0013_inspection_views": Effect.gen(function* () {
|
|
318
|
+
const sql = yield* SqlClient.SqlClient;
|
|
319
|
+
yield* sql `CREATE SCHEMA durable`;
|
|
320
|
+
yield* sql `CREATE VIEW durable.actors AS
|
|
321
|
+
SELECT g.tenant_id, g.actor_type, g.actor_id, g.routing_key, p.placement,
|
|
322
|
+
g.generation, g.created, g.event_sequence AS last_event_sequence
|
|
323
|
+
FROM actor_generations g
|
|
324
|
+
LEFT JOIN actor_placements p ON p.actor_type = g.actor_type`;
|
|
325
|
+
yield* sql `CREATE VIEW durable.state AS
|
|
326
|
+
SELECT s.tenant_id, s.actor_type, s.actor_id, s.routing_key, p.placement,
|
|
327
|
+
s.key, s.value, octet_length(s.value) AS value_bytes
|
|
328
|
+
FROM actor_state s
|
|
329
|
+
LEFT JOIN actor_placements p ON p.actor_type = s.actor_type`;
|
|
330
|
+
yield* sql `CREATE VIEW durable.receipts AS
|
|
331
|
+
SELECT r.tenant_id, r.actor_type, r.actor_id, r.routing_key, p.placement,
|
|
332
|
+
r.command_id, r.command, r.caller_key,
|
|
333
|
+
r.outcome::jsonb ->> '_tag' AS outcome_tag, r.outcome,
|
|
334
|
+
r.expires_at_ms, to_timestamp(r.expires_at_ms::float8 / 1000) AS expires_at
|
|
335
|
+
FROM actor_receipts r
|
|
336
|
+
LEFT JOIN actor_placements p ON p.actor_type = r.actor_type`;
|
|
337
|
+
yield* sql `CREATE VIEW durable.events AS
|
|
338
|
+
SELECT e.tenant_id, e.actor_type, e.actor_id, e.routing_key, p.placement,
|
|
339
|
+
e.sequence, e.event, e.command_id, e.value, octet_length(e.value) AS value_bytes,
|
|
340
|
+
e.emitted_at_ms, to_timestamp(e.emitted_at_ms::float8 / 1000) AS emitted_at
|
|
341
|
+
FROM actor_events e
|
|
342
|
+
LEFT JOIN actor_placements p ON p.actor_type = e.actor_type`;
|
|
343
|
+
yield* sql `CREATE VIEW durable.outbox AS
|
|
344
|
+
SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
|
|
345
|
+
o.intent_id, o.timer_key, o.target_type, o.target_id, o.command, o.payload, o.caller,
|
|
346
|
+
o.attempts, o.last_error, o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
|
|
347
|
+
FROM actor_outbox o
|
|
348
|
+
LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
|
|
349
|
+
WHERE o.kind = 'intent'`;
|
|
350
|
+
yield* sql `CREATE VIEW durable.timers AS
|
|
351
|
+
SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
|
|
352
|
+
o.timer_key, o.intent_id, o.target_type, o.target_id, o.command, o.payload, o.caller,
|
|
353
|
+
o.attempts, o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
|
|
354
|
+
FROM actor_outbox o
|
|
355
|
+
LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
|
|
356
|
+
WHERE o.kind = 'intent' AND o.timer_key IS NOT NULL`;
|
|
357
|
+
yield* sql `CREATE VIEW durable.effects AS
|
|
358
|
+
SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
|
|
359
|
+
o.intent_id AS effect_id, o.command AS effect, o.payload, o.caller,
|
|
360
|
+
o.attempts, o.last_error, o.ambiguous,
|
|
361
|
+
o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at
|
|
362
|
+
FROM actor_outbox o
|
|
363
|
+
LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
|
|
364
|
+
WHERE o.kind = 'effect'`;
|
|
365
|
+
yield* sql `CREATE VIEW durable.dead_letters AS
|
|
366
|
+
SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
|
|
367
|
+
d.effect_id, d.effect, d.payload, d.attempts, d.cause, d.ambiguous,
|
|
368
|
+
d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at
|
|
369
|
+
FROM actor_dead_letters d
|
|
370
|
+
LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
|
|
371
|
+
yield* sql `CREATE VIEW durable.workflows AS
|
|
372
|
+
SELECT w.tenant_id, w.actor_type, w.actor_id, w.routing_key, p.placement,
|
|
373
|
+
w.execution_id, w.workflow, w.workflow_key, w.manifest_hash, w.status, w.interrupt,
|
|
374
|
+
w.caller, w.payload, octet_length(w.payload) AS payload_bytes,
|
|
375
|
+
w.result, octet_length(w.result) AS result_bytes,
|
|
376
|
+
w.started_at_ms, to_timestamp(w.started_at_ms::float8 / 1000) AS started_at,
|
|
377
|
+
w.finished_at_ms, to_timestamp(w.finished_at_ms::float8 / 1000) AS finished_at
|
|
378
|
+
FROM actor_workflow_executions w
|
|
379
|
+
LEFT JOIN actor_placements p ON p.actor_type = w.actor_type`;
|
|
380
|
+
yield* sql `CREATE VIEW durable.workflow_steps AS
|
|
381
|
+
SELECT s.tenant_id, s.actor_type, s.actor_id, s.routing_key, p.placement,
|
|
382
|
+
s.execution_id, s.step, s.attempt, s.kind, s.exit, s.wait_event, s.version,
|
|
383
|
+
s.due_at_ms, to_timestamp(s.due_at_ms::float8 / 1000) AS due_at,
|
|
384
|
+
s.started_at_ms, to_timestamp(s.started_at_ms::float8 / 1000) AS started_at,
|
|
385
|
+
s.settled_at_ms, to_timestamp(s.settled_at_ms::float8 / 1000) AS settled_at
|
|
386
|
+
FROM actor_workflow_step s
|
|
387
|
+
LEFT JOIN actor_placements p ON p.actor_type = s.actor_type`;
|
|
388
|
+
yield* sql `CREATE VIEW durable.views AS
|
|
389
|
+
SELECT view_name, version FROM (VALUES
|
|
390
|
+
('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
|
|
391
|
+
('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
|
|
392
|
+
('workflow_steps', 1), ('views', 1)
|
|
393
|
+
) AS v(view_name, version)`;
|
|
394
|
+
}),
|
|
395
|
+
/**
|
|
396
|
+
* A connection's session lives beside the actor's rows; its socket and buffers live at the
|
|
397
|
+
* holder runner named by `holder` and `holder_epoch`.
|
|
398
|
+
*/
|
|
399
|
+
"0014_connections": Effect.gen(function* () {
|
|
400
|
+
const sql = yield* SqlClient.SqlClient;
|
|
401
|
+
yield* sql `CREATE TABLE actor_connections (
|
|
402
|
+
routing_key bigint NOT NULL,
|
|
403
|
+
connection_id text NOT NULL,
|
|
404
|
+
bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
|
|
405
|
+
tenant_id text NOT NULL,
|
|
406
|
+
actor_type text NOT NULL,
|
|
407
|
+
actor_id text NOT NULL,
|
|
408
|
+
member text NOT NULL,
|
|
409
|
+
holder text NOT NULL,
|
|
410
|
+
holder_epoch text NOT NULL,
|
|
411
|
+
caller text NOT NULL,
|
|
412
|
+
session bytea,
|
|
413
|
+
frame_seq bigint NOT NULL DEFAULT 0,
|
|
414
|
+
opened_at_ms bigint NOT NULL,
|
|
415
|
+
opened_through bigint NOT NULL,
|
|
416
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, connection_id),
|
|
417
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
418
|
+
)`;
|
|
419
|
+
yield* sql `CREATE INDEX actor_connections_holder ON actor_connections (bucket, holder, holder_epoch)`;
|
|
420
|
+
}),
|
|
421
|
+
"0015_effect_control": Effect.gen(function* () {
|
|
422
|
+
const sql = yield* SqlClient.SqlClient;
|
|
423
|
+
yield* sql `ALTER TABLE actor_outbox
|
|
424
|
+
ADD COLUMN running boolean NOT NULL DEFAULT false,
|
|
425
|
+
ADD COLUMN cancelled_at_ms bigint,
|
|
426
|
+
ADD COLUMN maybe_applied boolean NOT NULL DEFAULT false,
|
|
427
|
+
ADD COLUMN ready_at_ms bigint,
|
|
428
|
+
ADD COLUMN waiting boolean NOT NULL DEFAULT false,
|
|
429
|
+
ADD CONSTRAINT actor_outbox_effect_ready CHECK (kind <> 'effect' OR ready_at_ms IS NOT NULL)`;
|
|
430
|
+
yield* sql `CREATE INDEX actor_outbox_running
|
|
431
|
+
ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command)
|
|
432
|
+
WHERE kind = 'effect' AND running`;
|
|
433
|
+
yield* sql `CREATE INDEX actor_outbox_effect_queue
|
|
434
|
+
ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command, ready_at_ms, intent_id)
|
|
435
|
+
WHERE kind = 'effect' AND NOT running`;
|
|
436
|
+
}),
|
|
437
|
+
/**
|
|
438
|
+
* An attempt can end an effect before its retries run out, as when its route rejects the
|
|
439
|
+
* result. `final_failure` records that, with the outcome, so a dead letter that fails to
|
|
440
|
+
* commit is retried without another provider call.
|
|
441
|
+
*/
|
|
442
|
+
"0016_final_effect_failures": Effect.gen(function* () {
|
|
443
|
+
const sql = yield* SqlClient.SqlClient;
|
|
444
|
+
yield* sql `ALTER TABLE actor_outbox ADD COLUMN final_failure boolean NOT NULL DEFAULT false`;
|
|
445
|
+
}),
|
|
446
|
+
/**
|
|
447
|
+
* Subscriptions fan out on the source's shard after commit: one row per (source,
|
|
448
|
+
* subscription, subscriber), routed rows with subscriber_id = ''. The tag summary makes the
|
|
449
|
+
* publisher's probe a key lookup per emitted tag, and the subscriber's cursor, on its own
|
|
450
|
+
* shard, deduplicates every delivery after its receipt is pruned. The due index leads with
|
|
451
|
+
* `subscriber_type` so a runner never scans types it doesn't register.
|
|
452
|
+
*
|
|
453
|
+
* The routed declarations of the deployment, by source type: a runner that serves a source
|
|
454
|
+
* must register every subscriber type routing from it, because the publishing turn creates
|
|
455
|
+
* the routed rows.
|
|
456
|
+
*/
|
|
457
|
+
"0017_subscriptions": Effect.gen(function* () {
|
|
458
|
+
const sql = yield* SqlClient.SqlClient;
|
|
459
|
+
yield* sql `CREATE TABLE actor_subscriptions (
|
|
460
|
+
routing_key bigint NOT NULL,
|
|
461
|
+
tenant_id text NOT NULL,
|
|
462
|
+
source_type text NOT NULL,
|
|
463
|
+
source_id text NOT NULL,
|
|
464
|
+
subscriber_type text NOT NULL,
|
|
465
|
+
subscription text NOT NULL,
|
|
466
|
+
subscriber_id text NOT NULL,
|
|
467
|
+
events text[] NOT NULL,
|
|
468
|
+
epoch bigint NOT NULL DEFAULT 0,
|
|
469
|
+
active boolean NOT NULL DEFAULT true,
|
|
470
|
+
delivered bigint NOT NULL,
|
|
471
|
+
marked bigint NOT NULL DEFAULT 0,
|
|
472
|
+
bucket integer NOT NULL CHECK (bucket = routing_key >> 56),
|
|
473
|
+
due_at_ms bigint,
|
|
474
|
+
attempts integer NOT NULL DEFAULT 0,
|
|
475
|
+
last_error text,
|
|
476
|
+
gaps bigint NOT NULL DEFAULT 0,
|
|
477
|
+
gap_at_ms bigint,
|
|
478
|
+
gap_through bigint,
|
|
479
|
+
PRIMARY KEY (routing_key, tenant_id, source_type, source_id, subscriber_type, subscription, subscriber_id),
|
|
480
|
+
FOREIGN KEY (routing_key, tenant_id, source_type, source_id) REFERENCES actor_generations
|
|
481
|
+
) WITH (fillfactor = 80)`;
|
|
482
|
+
yield* sql `CREATE INDEX actor_subscriptions_due ON actor_subscriptions (bucket, subscriber_type, due_at_ms)
|
|
483
|
+
WHERE due_at_ms IS NOT NULL`;
|
|
484
|
+
yield* sql `CREATE TABLE actor_subscription_tags (
|
|
485
|
+
routing_key bigint NOT NULL,
|
|
486
|
+
tenant_id text NOT NULL,
|
|
487
|
+
source_type text NOT NULL,
|
|
488
|
+
source_id text NOT NULL,
|
|
489
|
+
event text NOT NULL,
|
|
490
|
+
rows integer NOT NULL CHECK (rows > 0),
|
|
491
|
+
PRIMARY KEY (routing_key, tenant_id, source_type, source_id, event),
|
|
492
|
+
FOREIGN KEY (routing_key, tenant_id, source_type, source_id) REFERENCES actor_generations
|
|
493
|
+
)`;
|
|
494
|
+
yield* sql `CREATE TABLE actor_subscription_cursors (
|
|
495
|
+
routing_key bigint NOT NULL,
|
|
496
|
+
tenant_id text NOT NULL,
|
|
497
|
+
actor_type text NOT NULL,
|
|
498
|
+
actor_id text NOT NULL,
|
|
499
|
+
subscription text NOT NULL,
|
|
500
|
+
source_type text NOT NULL,
|
|
501
|
+
source_id text NOT NULL,
|
|
502
|
+
epoch bigint NOT NULL DEFAULT 0,
|
|
503
|
+
active boolean NOT NULL DEFAULT true,
|
|
504
|
+
applied bigint NOT NULL,
|
|
505
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, subscription, source_type, source_id),
|
|
506
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
507
|
+
)`;
|
|
508
|
+
yield* sql `CREATE TABLE actor_routed_subscriptions (
|
|
509
|
+
source_type text NOT NULL,
|
|
510
|
+
subscriber_type text NOT NULL,
|
|
511
|
+
subscription text NOT NULL,
|
|
512
|
+
PRIMARY KEY (source_type, subscriber_type, subscription)
|
|
513
|
+
)`;
|
|
514
|
+
yield* sql `ALTER TABLE actor_outbox DROP CONSTRAINT actor_outbox_kind_check,
|
|
515
|
+
ADD CONSTRAINT actor_outbox_kind_check CHECK (kind IN ('intent', 'effect', 'feed', 'control'))`;
|
|
516
|
+
}),
|
|
517
|
+
/**
|
|
518
|
+
* Every tenant row admits only the tenant its transaction names. The table owner and
|
|
519
|
+
* superusers are exempt, so nothing changes until a deployment runs its turns and views as a
|
|
520
|
+
* role that is neither.
|
|
521
|
+
*/
|
|
522
|
+
"0018_rls": Effect.gen(function* () {
|
|
523
|
+
const sql = yield* SqlClient.SqlClient;
|
|
524
|
+
for (const table of [
|
|
525
|
+
"actor_generations",
|
|
526
|
+
"actor_state",
|
|
527
|
+
"actor_receipts",
|
|
528
|
+
"actor_outbox",
|
|
529
|
+
"actor_events",
|
|
530
|
+
"actor_dead_letters",
|
|
531
|
+
"actor_blobs",
|
|
532
|
+
"actor_workflow_executions",
|
|
533
|
+
"actor_workflow_step",
|
|
534
|
+
"actor_connections",
|
|
535
|
+
"actor_subscriptions",
|
|
536
|
+
"actor_subscription_tags",
|
|
537
|
+
"actor_subscription_cursors",
|
|
538
|
+
]) {
|
|
539
|
+
yield* sql `ALTER TABLE ${sql(table)} ENABLE ROW LEVEL SECURITY`;
|
|
540
|
+
yield* sql `CREATE POLICY durable_tenant ON ${sql(table)}
|
|
541
|
+
USING (tenant_id = current_setting('durable.tenant', true))
|
|
542
|
+
WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
|
|
543
|
+
}
|
|
544
|
+
}),
|
|
545
|
+
/**
|
|
546
|
+
* Content is stored once per tenant on the tenant's routing key; actors hold references on
|
|
547
|
+
* their own shard. Nothing counts references and no key points from a reference or a chunk
|
|
548
|
+
* to a content row: the sweep finds unreferenced content by scanning references, and grants
|
|
549
|
+
* gate it.
|
|
550
|
+
*
|
|
551
|
+
* Grants name the database they were issued by, so a key shared with another deployment
|
|
552
|
+
* never makes its grants valid here.
|
|
553
|
+
*
|
|
554
|
+
* Every tenant that ever uploaded, and when its content was last swept.
|
|
555
|
+
*
|
|
556
|
+
* A query reads references and chunks as the tenant role when row-level security is on;
|
|
557
|
+
* uploads, grants, and the sweep keep the exempt connecting role.
|
|
558
|
+
*
|
|
559
|
+
* The longest turn of each actor type that declares content, which the sweep waits out; it
|
|
560
|
+
* only grows, so a runner still on an earlier deploy with longer turns stays covered.
|
|
561
|
+
*
|
|
562
|
+
* The sweep join shows how far each tenant's collection lags, and keeps the view from being
|
|
563
|
+
* automatically updatable.
|
|
564
|
+
*/
|
|
565
|
+
"0020_content_blobs": Effect.gen(function* () {
|
|
566
|
+
const sql = yield* SqlClient.SqlClient;
|
|
567
|
+
yield* sql `ALTER TABLE actor_deployment
|
|
568
|
+
ADD COLUMN deployment_id text NOT NULL DEFAULT gen_random_uuid()::text`;
|
|
569
|
+
yield* sql `CREATE TABLE tenant_contents (
|
|
570
|
+
routing_key bigint NOT NULL,
|
|
571
|
+
tenant_id text NOT NULL,
|
|
572
|
+
hash text NOT NULL,
|
|
573
|
+
size bigint NOT NULL CHECK (size >= 0),
|
|
574
|
+
granted_until_ms bigint NOT NULL,
|
|
575
|
+
PRIMARY KEY (routing_key, tenant_id, hash)
|
|
576
|
+
)`;
|
|
577
|
+
yield* sql `CREATE INDEX tenant_contents_granted
|
|
578
|
+
ON tenant_contents (routing_key, tenant_id, granted_until_ms, hash)`;
|
|
579
|
+
yield* sql `CREATE TABLE tenant_content_chunks (
|
|
580
|
+
routing_key bigint NOT NULL,
|
|
581
|
+
tenant_id text NOT NULL,
|
|
582
|
+
hash text NOT NULL,
|
|
583
|
+
chunk integer NOT NULL CHECK (chunk >= 0),
|
|
584
|
+
bytes bytea NOT NULL,
|
|
585
|
+
PRIMARY KEY (routing_key, tenant_id, hash, chunk)
|
|
586
|
+
)`;
|
|
587
|
+
yield* sql `CREATE TABLE actor_content_refs (
|
|
588
|
+
routing_key bigint NOT NULL,
|
|
589
|
+
tenant_id text NOT NULL,
|
|
590
|
+
actor_type text NOT NULL,
|
|
591
|
+
actor_id text NOT NULL,
|
|
592
|
+
blob text NOT NULL,
|
|
593
|
+
name text NOT NULL,
|
|
594
|
+
hash text NOT NULL,
|
|
595
|
+
size bigint NOT NULL CHECK (size >= 0),
|
|
596
|
+
PRIMARY KEY (routing_key, tenant_id, actor_type, actor_id, blob, name),
|
|
597
|
+
FOREIGN KEY (routing_key, tenant_id, actor_type, actor_id) REFERENCES actor_generations
|
|
598
|
+
)`;
|
|
599
|
+
yield* sql `CREATE INDEX actor_content_refs_hash ON actor_content_refs (tenant_id, hash)`;
|
|
600
|
+
yield* sql `CREATE TABLE tenant_content_sweeps (
|
|
601
|
+
routing_key bigint NOT NULL,
|
|
602
|
+
tenant_id text NOT NULL,
|
|
603
|
+
swept_at_ms bigint NOT NULL,
|
|
604
|
+
PRIMARY KEY (routing_key, tenant_id)
|
|
605
|
+
)`;
|
|
606
|
+
for (const table of [
|
|
607
|
+
"actor_content_refs",
|
|
608
|
+
"tenant_contents",
|
|
609
|
+
"tenant_content_chunks",
|
|
610
|
+
"tenant_content_sweeps",
|
|
611
|
+
]) {
|
|
612
|
+
yield* sql `ALTER TABLE ${sql(table)} ENABLE ROW LEVEL SECURITY`;
|
|
613
|
+
yield* sql `CREATE POLICY durable_tenant ON ${sql(table)}
|
|
614
|
+
USING (tenant_id = current_setting('durable.tenant', true))
|
|
615
|
+
WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
|
|
616
|
+
}
|
|
617
|
+
yield* sql `CREATE TABLE actor_content_types (
|
|
618
|
+
actor_type text PRIMARY KEY,
|
|
619
|
+
turn_ms bigint NOT NULL CHECK (turn_ms > 0)
|
|
620
|
+
)`;
|
|
621
|
+
yield* sql `CREATE VIEW durable.contents AS
|
|
622
|
+
SELECT c.tenant_id, c.routing_key, c.hash, c.size,
|
|
623
|
+
c.granted_until_ms, to_timestamp(c.granted_until_ms::float8 / 1000) AS granted_until,
|
|
624
|
+
s.swept_at_ms, to_timestamp(s.swept_at_ms::float8 / 1000) AS swept_at
|
|
625
|
+
FROM tenant_contents c
|
|
626
|
+
LEFT JOIN tenant_content_sweeps s ON s.routing_key = c.routing_key AND s.tenant_id = c.tenant_id`;
|
|
627
|
+
yield* sql `CREATE VIEW durable.content_refs AS
|
|
628
|
+
SELECT r.tenant_id, r.actor_type, r.actor_id, r.routing_key, p.placement,
|
|
629
|
+
r.blob, r.name, r.hash, r.size
|
|
630
|
+
FROM actor_content_refs r
|
|
631
|
+
LEFT JOIN actor_placements p ON p.actor_type = r.actor_type`;
|
|
632
|
+
yield* sql `CREATE OR REPLACE VIEW durable.views AS
|
|
633
|
+
SELECT view_name, version FROM (VALUES
|
|
634
|
+
('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
|
|
635
|
+
('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
|
|
636
|
+
('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1)
|
|
637
|
+
) AS v(view_name, version)`;
|
|
638
|
+
}),
|
|
639
|
+
/**
|
|
640
|
+
* Stored events and effect payloads carry the version of their class's migration chain they
|
|
641
|
+
* were written at; readers upcast from it. A settled effect row becomes its route intent and
|
|
642
|
+
* goes back to 0. The two tables let startup refuse a deploy that would strand a stored
|
|
643
|
+
* version: `actor_payload_versions` records each version some runtime has written, and
|
|
644
|
+
* `actor_payload_writers` is each runtime's heartbeat per version it writes, which a clear
|
|
645
|
+
* waits out before it scans. Nothing written before it has a recorded version, so a database
|
|
646
|
+
* that already holds events or effect rows is refused rather than guessed at.
|
|
647
|
+
*
|
|
648
|
+
* Columns are added at the end, which a view version allows.
|
|
649
|
+
*/
|
|
650
|
+
"0021_payload_versions": Effect.gen(function* () {
|
|
651
|
+
const sql = yield* SqlClient.SqlClient;
|
|
652
|
+
const [held] = yield* sql `SELECT
|
|
653
|
+
EXISTS (SELECT 1 FROM actor_events) OR EXISTS (SELECT 1 FROM actor_outbox)
|
|
654
|
+
OR EXISTS (SELECT 1 FROM actor_dead_letters) AS held`;
|
|
655
|
+
if (held?.held === true)
|
|
656
|
+
return yield* new Migrator.MigrationError({
|
|
657
|
+
kind: "BadState",
|
|
658
|
+
message: "Migration 0021_payload_versions needs a database without events, outbox rows, or dead letters; recreate this development database",
|
|
659
|
+
});
|
|
660
|
+
yield* sql `ALTER TABLE actor_events ADD COLUMN payload_version integer NOT NULL DEFAULT 0
|
|
661
|
+
CHECK (payload_version >= 0)`;
|
|
662
|
+
yield* sql `ALTER TABLE actor_outbox ADD COLUMN payload_version integer NOT NULL DEFAULT 0
|
|
663
|
+
CHECK (payload_version >= 0)`;
|
|
664
|
+
yield* sql `ALTER TABLE actor_dead_letters ADD COLUMN payload_version integer NOT NULL DEFAULT 0
|
|
665
|
+
CHECK (payload_version >= 0)`;
|
|
666
|
+
yield* sql `CREATE TABLE actor_payload_versions (
|
|
667
|
+
actor_type text NOT NULL,
|
|
668
|
+
kind text NOT NULL CHECK (kind IN ('event', 'effect')),
|
|
669
|
+
tag text NOT NULL,
|
|
670
|
+
version integer NOT NULL CHECK (version >= 0),
|
|
671
|
+
first_written_at_ms bigint NOT NULL,
|
|
672
|
+
superseded_at_ms bigint,
|
|
673
|
+
cleared_at_ms bigint,
|
|
674
|
+
PRIMARY KEY (actor_type, kind, tag, version)
|
|
675
|
+
)`;
|
|
676
|
+
yield* sql `CREATE TABLE actor_payload_writers (
|
|
677
|
+
runtime_id text NOT NULL,
|
|
678
|
+
actor_type text NOT NULL,
|
|
679
|
+
kind text NOT NULL CHECK (kind IN ('event', 'effect')),
|
|
680
|
+
tag text NOT NULL,
|
|
681
|
+
version integer NOT NULL CHECK (version >= 0),
|
|
682
|
+
window_ms bigint NOT NULL CHECK (window_ms > 0),
|
|
683
|
+
refreshed_at_ms bigint NOT NULL,
|
|
684
|
+
PRIMARY KEY (runtime_id, actor_type, kind, tag, version)
|
|
685
|
+
)`;
|
|
686
|
+
yield* sql `CREATE INDEX actor_payload_writers_version
|
|
687
|
+
ON actor_payload_writers (actor_type, kind, tag, version, refreshed_at_ms)`;
|
|
688
|
+
yield* sql `CREATE OR REPLACE VIEW durable.events AS
|
|
689
|
+
SELECT e.tenant_id, e.actor_type, e.actor_id, e.routing_key, p.placement,
|
|
690
|
+
e.sequence, e.event, e.command_id, e.value, octet_length(e.value) AS value_bytes,
|
|
691
|
+
e.emitted_at_ms, to_timestamp(e.emitted_at_ms::float8 / 1000) AS emitted_at,
|
|
692
|
+
e.payload_version
|
|
693
|
+
FROM actor_events e
|
|
694
|
+
LEFT JOIN actor_placements p ON p.actor_type = e.actor_type`;
|
|
695
|
+
yield* sql `CREATE OR REPLACE VIEW durable.effects AS
|
|
696
|
+
SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
|
|
697
|
+
o.intent_id AS effect_id, o.command AS effect, o.payload, o.caller,
|
|
698
|
+
o.attempts, o.last_error, o.ambiguous,
|
|
699
|
+
o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at,
|
|
700
|
+
o.payload_version
|
|
701
|
+
FROM actor_outbox o
|
|
702
|
+
LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
|
|
703
|
+
WHERE o.kind = 'effect'`;
|
|
704
|
+
yield* sql `CREATE OR REPLACE VIEW durable.dead_letters AS
|
|
705
|
+
SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
|
|
706
|
+
d.effect_id, d.effect, d.payload, d.attempts, d.cause, d.ambiguous,
|
|
707
|
+
d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at,
|
|
708
|
+
d.payload_version
|
|
709
|
+
FROM actor_dead_letters d
|
|
710
|
+
LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
|
|
711
|
+
}),
|
|
712
|
+
/**
|
|
713
|
+
* A parent-placed type routes through its parent type's placement, so the parent is part of
|
|
714
|
+
* the record a later build must match.
|
|
715
|
+
*/
|
|
716
|
+
"0022_parent_placement": Effect.gen(function* () {
|
|
717
|
+
const sql = yield* SqlClient.SqlClient;
|
|
718
|
+
yield* sql `ALTER TABLE actor_placements ADD COLUMN parent_type text,
|
|
719
|
+
DROP CONSTRAINT actor_placements_placement_check,
|
|
720
|
+
ADD CONSTRAINT actor_placements_placement_check
|
|
721
|
+
CHECK (placement IN ('tenant', 'actor', 'parent')),
|
|
722
|
+
ADD CONSTRAINT actor_placements_parent_type_check
|
|
723
|
+
CHECK ((placement = 'parent') = (parent_type IS NOT NULL))`;
|
|
724
|
+
}),
|
|
725
|
+
/**
|
|
726
|
+
* Every authorized operator action, and every authenticated one refused by scope. A repair
|
|
727
|
+
* writes its row in its own transaction, on the target actor's routing key, so the two
|
|
728
|
+
* commit together on one shard; a tenant-wide action uses the tenant's routing key. Rows are
|
|
729
|
+
* never pruned.
|
|
730
|
+
*
|
|
731
|
+
* The join keeps the view read-only, as every other `durable` view is.
|
|
732
|
+
*/
|
|
733
|
+
"0023_operator_audit": Effect.gen(function* () {
|
|
734
|
+
const sql = yield* SqlClient.SqlClient;
|
|
735
|
+
yield* sql `CREATE TABLE actor_operator_audit (
|
|
736
|
+
routing_key bigint NOT NULL,
|
|
737
|
+
audit_id text NOT NULL,
|
|
738
|
+
at_ms bigint NOT NULL,
|
|
739
|
+
operator text NOT NULL,
|
|
740
|
+
action text NOT NULL,
|
|
741
|
+
tenant_id text NOT NULL,
|
|
742
|
+
actor_type text,
|
|
743
|
+
actor_id text,
|
|
744
|
+
target text,
|
|
745
|
+
capability text,
|
|
746
|
+
reason text,
|
|
747
|
+
outcome text NOT NULL,
|
|
748
|
+
PRIMARY KEY (routing_key, audit_id)
|
|
749
|
+
)`;
|
|
750
|
+
yield* sql `CREATE INDEX actor_operator_audit_tenant ON actor_operator_audit (tenant_id, at_ms)`;
|
|
751
|
+
yield* sql `ALTER TABLE actor_operator_audit ENABLE ROW LEVEL SECURITY`;
|
|
752
|
+
yield* sql `CREATE POLICY durable_tenant ON actor_operator_audit
|
|
753
|
+
USING (tenant_id = current_setting('durable.tenant', true))
|
|
754
|
+
WITH CHECK (tenant_id = current_setting('durable.tenant', true))`;
|
|
755
|
+
yield* sql `CREATE VIEW durable.operator_audit AS
|
|
756
|
+
SELECT a.tenant_id, a.actor_type, a.actor_id, a.routing_key, p.placement, a.audit_id,
|
|
757
|
+
a.operator, a.action, a.target, a.capability, a.reason, a.outcome,
|
|
758
|
+
a.at_ms, to_timestamp(a.at_ms::float8 / 1000) AS at
|
|
759
|
+
FROM actor_operator_audit a
|
|
760
|
+
LEFT JOIN actor_placements p ON p.actor_type = a.actor_type`;
|
|
761
|
+
yield* sql `CREATE OR REPLACE VIEW durable.views AS
|
|
762
|
+
SELECT view_name, version FROM (VALUES
|
|
763
|
+
('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
|
|
764
|
+
('timers', 1), ('effects', 1), ('dead_letters', 1), ('workflows', 1),
|
|
765
|
+
('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1),
|
|
766
|
+
('operator_audit', 1)
|
|
767
|
+
) AS v(view_name, version)`;
|
|
768
|
+
}),
|
|
769
|
+
"0024_adoption": Effect.gen(function* () {
|
|
770
|
+
const sql = yield* SqlClient.SqlClient;
|
|
771
|
+
const schema = (yield* sql `SELECT quote_ident(current_schema()) AS schema`)[0].schema;
|
|
772
|
+
yield* sql `CREATE TABLE actor_adoptions (
|
|
773
|
+
table_schema text NOT NULL,
|
|
774
|
+
table_name text NOT NULL,
|
|
775
|
+
actor_type text NOT NULL,
|
|
776
|
+
tenant_column text NOT NULL,
|
|
777
|
+
actor_column text NOT NULL,
|
|
778
|
+
mode text NOT NULL CHECK (mode IN ('observe', 'enforce')),
|
|
779
|
+
writer_role text,
|
|
780
|
+
allowed_roles text[] NOT NULL DEFAULT '{}',
|
|
781
|
+
revoked jsonb NOT NULL DEFAULT '[]',
|
|
782
|
+
changed_at_ms bigint NOT NULL,
|
|
783
|
+
changed_by text NOT NULL,
|
|
784
|
+
PRIMARY KEY (table_schema, table_name),
|
|
785
|
+
CHECK ((mode = 'enforce') = (writer_role IS NOT NULL)),
|
|
786
|
+
CHECK (mode = 'enforce' OR cardinality(allowed_roles) = 0)
|
|
787
|
+
)`;
|
|
788
|
+
yield* sql `CREATE TABLE actor_adoption_writes (
|
|
789
|
+
observed_at_ms bigint NOT NULL,
|
|
790
|
+
table_schema text NOT NULL,
|
|
791
|
+
table_name text NOT NULL,
|
|
792
|
+
operation text NOT NULL,
|
|
793
|
+
session_user_name text NOT NULL,
|
|
794
|
+
application_name text NOT NULL,
|
|
795
|
+
in_turn boolean NOT NULL,
|
|
796
|
+
allowed boolean NOT NULL,
|
|
797
|
+
rows bigint NOT NULL
|
|
798
|
+
)`;
|
|
799
|
+
yield* sql `CREATE INDEX actor_adoption_writes_table
|
|
800
|
+
ON actor_adoption_writes (table_schema, table_name, observed_at_ms)`;
|
|
801
|
+
yield* sql.unsafe(`CREATE FUNCTION actor_adoption_observe() RETURNS trigger
|
|
802
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $$
|
|
803
|
+
DECLARE
|
|
804
|
+
caller text := CASE WHEN current_setting('role') = 'none'
|
|
805
|
+
THEN session_user::text ELSE current_setting('role') END;
|
|
806
|
+
changed bigint := 0;
|
|
807
|
+
BEGIN
|
|
808
|
+
IF coalesce(current_setting('durable.backfill', true), '') = 'on' AND (
|
|
809
|
+
pg_has_role(caller, (SELECT relowner FROM pg_class WHERE oid = TG_RELID), 'MEMBER')
|
|
810
|
+
OR EXISTS (SELECT 1 FROM ${schema}.actor_adoptions a
|
|
811
|
+
WHERE a.table_schema = TG_TABLE_SCHEMA AND a.table_name = TG_TABLE_NAME
|
|
812
|
+
AND a.changed_by = session_user::text)) THEN
|
|
813
|
+
RETURN NULL;
|
|
814
|
+
END IF;
|
|
815
|
+
IF TG_NARGS > 0 AND caller = TG_ARGV[0] THEN
|
|
816
|
+
RETURN NULL;
|
|
817
|
+
END IF;
|
|
818
|
+
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
|
|
819
|
+
SELECT count(*) INTO changed FROM new_rows;
|
|
820
|
+
ELSIF TG_OP = 'DELETE' THEN
|
|
821
|
+
SELECT count(*) INTO changed FROM old_rows;
|
|
822
|
+
END IF;
|
|
823
|
+
INSERT INTO ${schema}.actor_adoption_writes (observed_at_ms, table_schema, table_name,
|
|
824
|
+
operation, session_user_name, application_name, in_turn, allowed, rows)
|
|
825
|
+
VALUES (floor(extract(epoch FROM clock_timestamp()) * 1000), TG_TABLE_SCHEMA, TG_TABLE_NAME,
|
|
826
|
+
TG_OP, session_user, current_setting('application_name'),
|
|
827
|
+
coalesce(current_setting('durable.turn', true), '') = 'on',
|
|
828
|
+
TG_NARGS > 1 AND caller = ANY(TG_ARGV[1:TG_NARGS - 1]), changed);
|
|
829
|
+
RETURN NULL;
|
|
830
|
+
END
|
|
831
|
+
$$`);
|
|
832
|
+
yield* sql.unsafe(`CREATE FUNCTION actor_adoption_guard() RETURNS trigger
|
|
833
|
+
LANGUAGE plpgsql AS $$
|
|
834
|
+
DECLARE
|
|
835
|
+
writer text := TG_ARGV[0];
|
|
836
|
+
allowed text[] := string_to_array(TG_ARGV[1], ',');
|
|
837
|
+
tenant_column text := TG_ARGV[2];
|
|
838
|
+
actor_column text := TG_ARGV[3];
|
|
839
|
+
owner text := TG_ARGV[4];
|
|
840
|
+
incoming jsonb;
|
|
841
|
+
BEGIN
|
|
842
|
+
IF TG_OP = 'TRUNCATE' THEN
|
|
843
|
+
IF current_user = ANY(allowed) THEN
|
|
844
|
+
RETURN NULL;
|
|
845
|
+
END IF;
|
|
846
|
+
ELSE
|
|
847
|
+
IF TG_OP = 'UPDATE' THEN
|
|
848
|
+
IF to_jsonb(OLD) -> tenant_column IS DISTINCT FROM to_jsonb(NEW) -> tenant_column
|
|
849
|
+
OR to_jsonb(OLD) -> actor_column IS DISTINCT FROM to_jsonb(NEW) -> actor_column THEN
|
|
850
|
+
RAISE EXCEPTION 'a row of %.% cannot move to another tenant or actor', TG_TABLE_SCHEMA, TG_TABLE_NAME
|
|
851
|
+
USING ERRCODE = '42501';
|
|
852
|
+
END IF;
|
|
853
|
+
END IF;
|
|
854
|
+
IF current_user = writer OR current_user = ANY(allowed) THEN
|
|
855
|
+
IF TG_OP = 'DELETE' THEN
|
|
856
|
+
RETURN OLD;
|
|
857
|
+
END IF;
|
|
858
|
+
incoming := to_jsonb(NEW);
|
|
859
|
+
IF incoming -> 'routing_key' = 'null'::jsonb
|
|
860
|
+
OR incoming -> tenant_column = 'null'::jsonb
|
|
861
|
+
OR incoming -> actor_column = 'null'::jsonb THEN
|
|
862
|
+
RAISE EXCEPTION 'a row of %.% needs routing_key, %, and %', TG_TABLE_SCHEMA, TG_TABLE_NAME,
|
|
863
|
+
tenant_column, actor_column USING ERRCODE = '23514';
|
|
864
|
+
END IF;
|
|
865
|
+
RETURN NEW;
|
|
866
|
+
END IF;
|
|
867
|
+
END IF;
|
|
868
|
+
RAISE EXCEPTION '%.% belongs to actor %; % by % is rejected', TG_TABLE_SCHEMA, TG_TABLE_NAME, owner, TG_OP,
|
|
869
|
+
current_user USING ERRCODE = '42501';
|
|
870
|
+
END
|
|
871
|
+
$$`);
|
|
872
|
+
}),
|
|
873
|
+
/**
|
|
874
|
+
* Per-view state of the fleet maintainer: its source, the hash of the
|
|
875
|
+
* definition that built its rows, its status, and how far it has applied
|
|
876
|
+
* the change feed. Framework state without a tenant, like `actor_tables`,
|
|
877
|
+
* so it carries no policy and no `durable` view.
|
|
878
|
+
*/
|
|
879
|
+
"0025_fleet": Effect.gen(function* () {
|
|
880
|
+
const sql = yield* SqlClient.SqlClient;
|
|
881
|
+
yield* sql `CREATE TABLE actor_fleet_views (
|
|
882
|
+
view_name text PRIMARY KEY,
|
|
883
|
+
source_schema text NOT NULL,
|
|
884
|
+
source_table text NOT NULL,
|
|
885
|
+
definition_hash text NOT NULL,
|
|
886
|
+
status text NOT NULL CHECK (status IN ('building', 'ready', 'stale')),
|
|
887
|
+
applied_lsn numeric,
|
|
888
|
+
updated_at_ms bigint NOT NULL,
|
|
889
|
+
last_error text
|
|
890
|
+
)`;
|
|
891
|
+
}),
|
|
892
|
+
/**
|
|
893
|
+
* Staged external work is named a job everywhere it is stored: the outbox kind, keyed-job
|
|
894
|
+
* timer keys, dead-letter columns, payload-version kinds, and the inspection views. Rows keep
|
|
895
|
+
* their ids, attempts, ambiguity, and cancellation marks, so a job in flight settles under the
|
|
896
|
+
* same id. Dropping the renamed views drops any grants on them.
|
|
897
|
+
*
|
|
898
|
+
* A subscription change lowers a tag count in place and deletes a count it
|
|
899
|
+
* lowered to 0 before its transaction commits, so a summary row may hold 0
|
|
900
|
+
* only inside that transaction.
|
|
901
|
+
*/
|
|
902
|
+
"0026_jobs": Effect.gen(function* () {
|
|
903
|
+
const sql = yield* SqlClient.SqlClient;
|
|
904
|
+
yield* sql `ALTER TABLE actor_outbox
|
|
905
|
+
DROP CONSTRAINT actor_outbox_kind_check,
|
|
906
|
+
DROP CONSTRAINT actor_outbox_effect_ready`;
|
|
907
|
+
yield* sql `DROP INDEX actor_outbox_running`;
|
|
908
|
+
yield* sql `DROP INDEX actor_outbox_effect_queue`;
|
|
909
|
+
yield* sql `UPDATE actor_outbox SET kind = 'job',
|
|
910
|
+
timer_key = CASE WHEN left(timer_key, 8) = '$effect:' THEN '$job:' || substr(timer_key, 9)
|
|
911
|
+
ELSE timer_key END
|
|
912
|
+
WHERE kind = 'effect'`;
|
|
913
|
+
yield* sql `ALTER TABLE actor_outbox
|
|
914
|
+
ADD CONSTRAINT actor_outbox_kind_check CHECK (kind IN ('intent', 'job', 'feed', 'control')),
|
|
915
|
+
ADD CONSTRAINT actor_outbox_job_ready CHECK (kind <> 'job' OR ready_at_ms IS NOT NULL)`;
|
|
916
|
+
yield* sql `CREATE INDEX actor_outbox_running
|
|
917
|
+
ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command)
|
|
918
|
+
WHERE kind = 'job' AND running`;
|
|
919
|
+
yield* sql `CREATE INDEX actor_outbox_job_queue
|
|
920
|
+
ON actor_outbox (routing_key, tenant_id, actor_type, actor_id, command, ready_at_ms, intent_id)
|
|
921
|
+
WHERE kind = 'job' AND NOT running`;
|
|
922
|
+
yield* sql `ALTER TABLE actor_dead_letters RENAME COLUMN effect_id TO job_id`;
|
|
923
|
+
yield* sql `ALTER TABLE actor_dead_letters RENAME COLUMN effect TO job`;
|
|
924
|
+
for (const table of ["actor_payload_versions", "actor_payload_writers"]) {
|
|
925
|
+
yield* sql `ALTER TABLE ${sql(table)} DROP CONSTRAINT ${sql(`${table}_kind_check`)}`;
|
|
926
|
+
yield* sql `UPDATE ${sql(table)} SET kind = 'job' WHERE kind = 'effect'`;
|
|
927
|
+
yield* sql `ALTER TABLE ${sql(table)} ADD CONSTRAINT ${sql(`${table}_kind_check`)}
|
|
928
|
+
CHECK (kind IN ('event', 'job'))`;
|
|
929
|
+
}
|
|
930
|
+
yield* sql `ALTER TABLE actor_subscription_tags DROP CONSTRAINT actor_subscription_tags_rows_check,
|
|
931
|
+
ADD CONSTRAINT actor_subscription_tags_rows_check CHECK (rows >= 0)`;
|
|
932
|
+
yield* sql `DROP VIEW durable.effects`;
|
|
933
|
+
yield* sql `DROP VIEW durable.dead_letters`;
|
|
934
|
+
yield* sql `CREATE VIEW durable.jobs AS
|
|
935
|
+
SELECT o.tenant_id, o.actor_type, o.actor_id, o.routing_key, p.placement,
|
|
936
|
+
o.intent_id AS job_id, o.command AS job, o.payload, o.caller,
|
|
937
|
+
o.attempts, o.last_error, o.ambiguous,
|
|
938
|
+
o.due_at_ms, to_timestamp(o.due_at_ms::float8 / 1000) AS due_at,
|
|
939
|
+
o.payload_version
|
|
940
|
+
FROM actor_outbox o
|
|
941
|
+
LEFT JOIN actor_placements p ON p.actor_type = o.actor_type
|
|
942
|
+
WHERE o.kind = 'job'`;
|
|
943
|
+
yield* sql `CREATE VIEW durable.dead_letters AS
|
|
944
|
+
SELECT d.tenant_id, d.actor_type, d.actor_id, d.routing_key, p.placement,
|
|
945
|
+
d.job_id, d.job, d.payload, d.attempts, d.cause, d.ambiguous,
|
|
946
|
+
d.dead_at_ms, to_timestamp(d.dead_at_ms::float8 / 1000) AS dead_at,
|
|
947
|
+
d.payload_version
|
|
948
|
+
FROM actor_dead_letters d
|
|
949
|
+
LEFT JOIN actor_placements p ON p.actor_type = d.actor_type`;
|
|
950
|
+
yield* sql `CREATE OR REPLACE VIEW durable.views AS
|
|
951
|
+
SELECT view_name, version FROM (VALUES
|
|
952
|
+
('actors', 1), ('state', 1), ('receipts', 1), ('events', 1), ('outbox', 1),
|
|
953
|
+
('timers', 1), ('jobs', 1), ('dead_letters', 2), ('workflows', 1),
|
|
954
|
+
('workflow_steps', 1), ('views', 1), ('contents', 1), ('content_refs', 1),
|
|
955
|
+
('operator_audit', 1)
|
|
956
|
+
) AS v(view_name, version)`;
|
|
957
|
+
}),
|
|
958
|
+
"0027_coordination": Effect.gen(function* () {
|
|
959
|
+
const sql = yield* SqlClient.SqlClient;
|
|
960
|
+
yield* sql `CREATE TABLE IF NOT EXISTS actor_coordination (resource text PRIMARY KEY)`;
|
|
961
|
+
}),
|
|
962
|
+
"0029_runner_configuration": Effect.gen(function* () {
|
|
963
|
+
const sql = yield* SqlClient.SqlClient;
|
|
964
|
+
yield* sql `ALTER TABLE actor_deployment ADD COLUMN runner_shards integer,
|
|
965
|
+
ADD COLUMN runner_lock_expiration_ms bigint,
|
|
966
|
+
ADD CONSTRAINT actor_deployment_runner_configuration CHECK (
|
|
967
|
+
(runner_shards IS NULL AND runner_lock_expiration_ms IS NULL) OR
|
|
968
|
+
(runner_shards IS NOT NULL AND
|
|
969
|
+
runner_lock_expiration_ms IS NOT NULL AND runner_lock_expiration_ms >= 3000 AND
|
|
970
|
+
runner_shards BETWEEN 1 AND 65536)
|
|
971
|
+
)`;
|
|
972
|
+
}),
|
|
973
|
+
};
|
|
974
|
+
/**
|
|
975
|
+
* Runs `record` like `Migrator`, but first refuses a database where a
|
|
976
|
+
* registered id below the latest applied one was never applied: `Migrator`
|
|
977
|
+
* would skip it forever, leaving its tables missing. The check runs again
|
|
978
|
+
* after the migrations because a concurrent runner with fewer migrations can
|
|
979
|
+
* commit a higher id between the first check and the migration lock.
|
|
980
|
+
*/
|
|
981
|
+
export const migrator = (record) => {
|
|
982
|
+
const run = Migrator.make({})({
|
|
983
|
+
table: "actor_migrations",
|
|
984
|
+
loader: Migrator.fromRecord(record),
|
|
985
|
+
});
|
|
986
|
+
const registered = Object.keys(record).map((key) => Number(key.split("_")[0]));
|
|
987
|
+
const refuseSkipped = Effect.gen(function* () {
|
|
988
|
+
const sql = yield* SqlClient.SqlClient;
|
|
989
|
+
const [table] = yield* sql `SELECT to_regclass('actor_migrations')::text AS name`;
|
|
990
|
+
const rows = table?.name == null
|
|
991
|
+
? []
|
|
992
|
+
: yield* sql `SELECT migration_id::int AS id FROM actor_migrations`;
|
|
993
|
+
const applied = new Set(rows.map(({ id }) => id));
|
|
994
|
+
const latest = Math.max(0, ...applied);
|
|
995
|
+
const skipped = registered.filter((id) => id < latest && !applied.has(id)).sort((a, b) => a - b);
|
|
996
|
+
if (skipped.length > 0) {
|
|
997
|
+
return yield* new Migrator.MigrationError({
|
|
998
|
+
kind: "BadState",
|
|
999
|
+
message: `Migrations ${skipped.join(", ")} were never applied but migration ${latest} was; they would be skipped. Restore this database from before migration ${latest} or recreate it.`,
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
const transactional = refuseSkipped.pipe(Effect.andThen(run), Effect.tap(() => refuseSkipped));
|
|
1004
|
+
return Effect.gen(function* () {
|
|
1005
|
+
const neki = yield* NekiTurnSessions;
|
|
1006
|
+
const selected = neki
|
|
1007
|
+
? nekiMigrator({
|
|
1008
|
+
record: Object.fromEntries(Object.entries(record).map(([key, effect]) => [key, Effect.orDie(effect)])),
|
|
1009
|
+
refuseSkipped,
|
|
1010
|
+
})
|
|
1011
|
+
: transactional;
|
|
1012
|
+
return yield* neki || Option.isSome(yield* Effect.serviceOption(PgClient.PgClient))
|
|
1013
|
+
? withMigrationCoordination(selected)
|
|
1014
|
+
: selected;
|
|
1015
|
+
});
|
|
1016
|
+
};
|
|
1017
|
+
/** Applies every framework migration not yet recorded in `actor_migrations`, refusing a database that skipped one. */
|
|
1018
|
+
export const migrate = migrator(migrations);
|