@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,972 @@
|
|
|
1
|
+
import { Cause, Clock, Crypto, Effect, Exit, Option, Predicate, Result, Schema, Scope, } from "effect";
|
|
2
|
+
import { SqlClient, SqlError } from "effect/sql";
|
|
3
|
+
import { ActorError, CommandExpired, NotCreated, Unauthorized } from "../../errors/actor.js";
|
|
4
|
+
import {} from "../members.js";
|
|
5
|
+
import { Outcome } from "../request.js";
|
|
6
|
+
import { callerKey, System } from "../../identity/caller.js";
|
|
7
|
+
import { commandTimes } from "../../identity/command.js";
|
|
8
|
+
import { isMintedId, provesMint } from "../../identity/mint.js";
|
|
9
|
+
import { parseChildId } from "../../identity/child.js";
|
|
10
|
+
import { writeTicks } from "../cron/schedule.js";
|
|
11
|
+
import { eventsStatement, notifyEvents } from "../events/append.js";
|
|
12
|
+
import { COMMIT_VERSION } from "../database/replica.js";
|
|
13
|
+
import { isPoolRefusal } from "../database/bounded.js";
|
|
14
|
+
import { NekiTurnSessions } from "../database/neki/session.js";
|
|
15
|
+
import { compress, decompress } from "../storage/codec.js";
|
|
16
|
+
import { Metrics, record } from "../telemetry/metrics.js";
|
|
17
|
+
import { SpanNames } from "../telemetry/spans.js";
|
|
18
|
+
import { receiptMarginMs } from "../storage/retention.js";
|
|
19
|
+
import { hashedPayload } from "../subscriptions/identity.js";
|
|
20
|
+
import { tenantSettings, TenantScope } from "../database/tenancy.js";
|
|
21
|
+
import { actorRow as rowOf, forget } from "../storage/generation.js";
|
|
22
|
+
import { checkIdentity, databaseTime, FrameworkClock } from "./admission.js";
|
|
23
|
+
import { abortedBefore, errorOf, Shared, TurnGroups, Unseated } from "./group.js";
|
|
24
|
+
import { RetryTurn, TurnHooks } from "./hooks.js";
|
|
25
|
+
import { OutboxRuntime, outboxStatements } from "./outbox.js";
|
|
26
|
+
import { asSqlConnection, awaitReplies, queueStatements, sendPipelined, sendSequentially, TurnConnections, } from "./pipeline.js";
|
|
27
|
+
import { MERGE_CAP, merges } from "../entity/mailbox.js";
|
|
28
|
+
import { checkReceipt, encodeOutcome, hashCanonical } from "./receipt.js";
|
|
29
|
+
const isSystem = Schema.is(System);
|
|
30
|
+
/**
|
|
31
|
+
* The cursor row once `delivery` applied, as its commit writes it: a routed
|
|
32
|
+
* delivery creates the epoch-0 row or raises its position, a dynamic one moves
|
|
33
|
+
* the row of its own epoch, and a rejection deactivates that row.
|
|
34
|
+
*/
|
|
35
|
+
const applied = (delivery, cursor) => {
|
|
36
|
+
const epoch = cursor.sub_epoch ?? null;
|
|
37
|
+
if (delivery.kind === "rejected")
|
|
38
|
+
return epoch === delivery.epoch ? { ...cursor, sub_active: false } : cursor;
|
|
39
|
+
if (delivery.epoch === "0") {
|
|
40
|
+
if (epoch === null)
|
|
41
|
+
return { sub_epoch: "0", sub_active: true, sub_applied: delivery.position };
|
|
42
|
+
if (epoch !== "0")
|
|
43
|
+
return cursor;
|
|
44
|
+
const position = BigInt(cursor.sub_applied) >= BigInt(delivery.position)
|
|
45
|
+
? cursor.sub_applied
|
|
46
|
+
: delivery.position;
|
|
47
|
+
return { ...cursor, sub_applied: position };
|
|
48
|
+
}
|
|
49
|
+
return epoch === delivery.epoch ? { ...cursor, sub_applied: delivery.position } : cursor;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* How the subscriber's cursor row settles a subscription delivery before its
|
|
53
|
+
* handler runs: undefined runs the handler. A row of a newer epoch makes the
|
|
54
|
+
* delivery stale, an inactive row at its epoch means it was unsubscribed, and
|
|
55
|
+
* a position at or below `applied` was already applied. A dynamic
|
|
56
|
+
* subscription always has a row, so only a routed one (epoch 0) creates it.
|
|
57
|
+
*/
|
|
58
|
+
const acknowledgement = (delivery, admission) => {
|
|
59
|
+
const epoch = BigInt(delivery.epoch);
|
|
60
|
+
if (admission.sub_epoch == null)
|
|
61
|
+
return epoch > 0n ? "Stale" : undefined;
|
|
62
|
+
const stored = BigInt(admission.sub_epoch);
|
|
63
|
+
if (stored !== epoch)
|
|
64
|
+
return "Stale";
|
|
65
|
+
if (admission.sub_active !== true)
|
|
66
|
+
return "Unsubscribed";
|
|
67
|
+
if (delivery.kind === "rejected")
|
|
68
|
+
return undefined;
|
|
69
|
+
return BigInt(admission.sub_applied) >= BigInt(delivery.position) ? "AlreadyApplied" : undefined;
|
|
70
|
+
};
|
|
71
|
+
/** The first cron ticks a newly created actor stages, read against the database clock. */
|
|
72
|
+
const cronTicks = Effect.fnUntraced(function* (routingKey, ref, cron) {
|
|
73
|
+
const now = yield* databaseTime;
|
|
74
|
+
const services = yield* Effect.context();
|
|
75
|
+
return [writeTicks(routingKey, ref, cron, now).pipe(Effect.provideContext(services))];
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* True when `request` is a minted actor's creating intent: its caller carries
|
|
79
|
+
* the parent's mint proof for the actor's id, and its delivery carries the
|
|
80
|
+
* sender of the committed outbox row the relay claimed. The trusted relay
|
|
81
|
+
* copies the request from that row, and external admission refuses this
|
|
82
|
+
* provenance. The child's transaction needs no read on the parent's shard.
|
|
83
|
+
*/
|
|
84
|
+
const committedMintIntent = Effect.fnUntraced(function* (request, parent) {
|
|
85
|
+
const { caller, ref, intent } = request;
|
|
86
|
+
if (request.external === true ||
|
|
87
|
+
intent === undefined ||
|
|
88
|
+
!isSystem(caller) ||
|
|
89
|
+
caller.ref === undefined ||
|
|
90
|
+
intent.tenant !== ref.tenant ||
|
|
91
|
+
intent.tenant !== caller.ref.tenant ||
|
|
92
|
+
intent.actor !== caller.ref.actor ||
|
|
93
|
+
intent.id !== caller.ref.id)
|
|
94
|
+
return false;
|
|
95
|
+
return yield* provesMint(caller, ref, parent);
|
|
96
|
+
});
|
|
97
|
+
const nothingWritten = { receipts: 0, events: 0, intents: 0, jobs: 0 };
|
|
98
|
+
const nothingWrote = { state: false, events: [], tables: [], blobs: [] };
|
|
99
|
+
class RolledBack {
|
|
100
|
+
plan;
|
|
101
|
+
constructor(plan) {
|
|
102
|
+
this.plan = plan;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const HANDLER_SAVEPOINT = "durable_handler";
|
|
106
|
+
/**
|
|
107
|
+
* Commands already waiting for one actor, run in delivery order in one
|
|
108
|
+
* framework transaction sent as two groups. The admission group opens the
|
|
109
|
+
* transaction, takes the generation fence, resolves every command's receipt
|
|
110
|
+
* under it, and on a cold activation acquires the next generation and reads
|
|
111
|
+
* state. The handlers run in memory once those replies arrive, each on the
|
|
112
|
+
* state the previous one left. The commit group writes the dirty state, each
|
|
113
|
+
* command's events and outbox rows, the creation marker, and every receipt,
|
|
114
|
+
* then commits. A lone command is a batch of one.
|
|
115
|
+
*
|
|
116
|
+
* Each command keeps its own outcome: a declared failure discards only its
|
|
117
|
+
* own staged work, and an admission error (expiry, `NotCreated`, a receipt
|
|
118
|
+
* conflict) answers only that command. A stale fence or any defect fails the
|
|
119
|
+
* whole batch and writes nothing. A batch with nothing to commit rolls back.
|
|
120
|
+
* Every command id in a batch must be distinct.
|
|
121
|
+
*
|
|
122
|
+
* `statements` marks an actor whose handler can issue SQL; only its handlers
|
|
123
|
+
* run under a savepoint, so a declared failure discards the handler's rows.
|
|
124
|
+
* The first handler's savepoint goes out with admission and each later one's
|
|
125
|
+
* with that handler's first statement. A handler that issued no statement has
|
|
126
|
+
* nothing to roll back, so its unsent savepoint is dropped. Rolling back to a
|
|
127
|
+
* savepoint keeps it, so each failed handler leaves one open until commit; a
|
|
128
|
+
* batch never holds more savepoints than its command cap. `parent` is a
|
|
129
|
+
* parent-placed actor's parent type, whose turns alone mint it, and `waited`
|
|
130
|
+
* holds the event tags workflows wait on.
|
|
131
|
+
*
|
|
132
|
+
* Admission statements take no parameter from another's reply, so they share
|
|
133
|
+
* one flight. The generation insert precedes the fenced read, so a brand-new
|
|
134
|
+
* actor's receipts resolve under the generation row lock. The timeout
|
|
135
|
+
* `set_config` runs before that insert, so `lock_timeout` bounds the insert's
|
|
136
|
+
* row waits but not the table lock taken as the statement starts;
|
|
137
|
+
* `statement_timeout` applies from the next statement. With row-level
|
|
138
|
+
* security the same statement takes the tenant role, which binds every later
|
|
139
|
+
* statement of the turn, the handler's included, to the actor's tenant at no
|
|
140
|
+
* extra round trip. A subscription delivery's cursor row is read in the
|
|
141
|
+
* fenced statement, and the delivery's canonical payload, not the event's
|
|
142
|
+
* bytes, binds its identity. When another runner advanced the generation
|
|
143
|
+
* since this activation acquired it, its cached state may be stale, so
|
|
144
|
+
* nothing runs, the activation drops its cache, and the retry reloads under a
|
|
145
|
+
* new generation.
|
|
146
|
+
*
|
|
147
|
+
* Admission checks. The generation row is locked in a materialized input
|
|
148
|
+
* before the read evaluates its clock, so time spent waiting for the fence
|
|
149
|
+
* cannot admit an identity that expired meanwhile. External ids are validated
|
|
150
|
+
* against the fenced read's clock before their receipt is released or a handler
|
|
151
|
+
* runs. An owner reports
|
|
152
|
+
* that admission on a retryable failure, so only a previously admitted
|
|
153
|
+
* redelivery can run past expiry, and not once retention could have pruned
|
|
154
|
+
* its receipt. No pre-delivery receipt read is needed. Only the relay's subscription
|
|
155
|
+
* deliveries reach a handler, and only a handler takes one, so no caller
|
|
156
|
+
* reaches it around the cursor or route. A routed event for a subscriber its
|
|
157
|
+
* creating command has not created is skipped, and its cursor keeps a stale
|
|
158
|
+
* redelivery from running after another command creates the subscriber. A
|
|
159
|
+
* minted actor is created only by the relay delivering the creating intent
|
|
160
|
+
* its parent's turn staged and committed: the mint proof binds the id to the
|
|
161
|
+
* parent's command, and the relay's claimed-row provenance proves that
|
|
162
|
+
* command committed the intent without reading another actor's shard.
|
|
163
|
+
*
|
|
164
|
+
* Handlers and commit. Calls of a commutative reducer already waiting right
|
|
165
|
+
* behind a command merge into its turn: their inputs are combined and reduced
|
|
166
|
+
* once. A complete result lists every key it keeps, and any other key it was
|
|
167
|
+
* given is deleted. A delivery's cursor position is applied with its receipt,
|
|
168
|
+
* declared failures included. Re-arming waiting workflows reads their steps,
|
|
169
|
+
* so it runs before the commit group and only an actor with a workflow
|
|
170
|
+
* waiting on an emitted class pays for it. A cold activation that replays or
|
|
171
|
+
* acknowledges still commits and keeps the generation it acquired, so work
|
|
172
|
+
* the replay wakes runs under it. The first batch a generation commits
|
|
173
|
+
* schedules every cron entry not yet ticking from the database clock after
|
|
174
|
+
* its handlers ran, so a first tick is never due before the batch that
|
|
175
|
+
* writes it. A batch's work is bounded like a lone turn's: interruption and
|
|
176
|
+
* the command timeout roll it back, and SQL failures are defects. A failed or
|
|
177
|
+
* unknown commit leaves nothing the cached state can be trusted for, so it
|
|
178
|
+
* clears the cache.
|
|
179
|
+
*
|
|
180
|
+
* On Postgres a run uses one leased session. Each admission group opens with
|
|
181
|
+
* `BEGIN`, and each commit group ends with `COMMIT`, whose command tag must
|
|
182
|
+
* be `COMMIT`, since Postgres answers `COMMIT` in an aborted transaction with
|
|
183
|
+
* `ROLLBACK`. While batch N commits, the next batch already waiting sends its
|
|
184
|
+
* `BEGIN` and admission group in the same flight, right behind N's `COMMIT`:
|
|
185
|
+
* the server runs them strictly after it, as a new transaction on the same
|
|
186
|
+
* session. That admission is built as if N commits, which its fence proves. A
|
|
187
|
+
* batch that leaves the cache cold is committed alone, so the next one
|
|
188
|
+
* prepares, which may acquire the generation itself, before its admission
|
|
189
|
+
* locks the generation row. So is a batch whose publication may wait for that
|
|
190
|
+
* lock in another transaction: an admission already behind its `COMMIT` would
|
|
191
|
+
* hold the lock until the publication it waits on ends, and neither would
|
|
192
|
+
* ever finish. N's callers are answered once its `COMMIT` reply
|
|
193
|
+
* arrives, and the next batch's handlers run only once its own fence and
|
|
194
|
+
* receipt replies arrive. If N's commit fails, the next batch's transaction
|
|
195
|
+
* is rolled back unseen with it. The commit version is read on the same
|
|
196
|
+
* session after the transaction ends, in the same flight, so it covers the
|
|
197
|
+
* batch's commit record and any receipt it replayed. The same statement reads
|
|
198
|
+
* a fresh clock after the transaction ended for the external expiry recheck,
|
|
199
|
+
* never reusing admission time.
|
|
200
|
+
*
|
|
201
|
+
* The session is unsafe from the moment a `BEGIN` may be queued until a
|
|
202
|
+
* transaction-ending reply with nothing queued behind it. Any other exit
|
|
203
|
+
* rolls back, and a session whose transaction state is unknown never goes
|
|
204
|
+
* back to the pool: an interrupted batch cancels its backend's statement and
|
|
205
|
+
* discards the session, so an unsent `COMMIT` rolls back with it and one
|
|
206
|
+
* already sent resolves through the receipt on retry. Deferred statements go
|
|
207
|
+
* out in the same flight as the next statement a handler sends, or at the
|
|
208
|
+
* head of the commit group.
|
|
209
|
+
*/
|
|
210
|
+
export const executeBatches = Effect.fnUntraced(function* (run, cache, routingKey, policy, mintable, parent, statements, waited = new Set(), connections, cron = [], accounting) {
|
|
211
|
+
const sql = yield* SqlClient.SqlClient;
|
|
212
|
+
const hooks = yield* TurnHooks;
|
|
213
|
+
const clock = yield* FrameworkClock;
|
|
214
|
+
const scope = yield* TenantScope;
|
|
215
|
+
const { ref } = run.first[0].request;
|
|
216
|
+
const { tenant, actor, id } = ref;
|
|
217
|
+
const role = scope.role ?? (scope.adoption?.enforced.has(actor) === true ? scope.adoption.role : undefined);
|
|
218
|
+
const actorRow = rowOf({ sql, actor: { key: routingKey, ref } });
|
|
219
|
+
const canonicalsOf = (batch) => Effect.forEach(batch, ({ request }) => hashedPayload(request));
|
|
220
|
+
const cursorOf = (delivery) => sql `${actorRow} AND subscription = ${delivery.subscription}
|
|
221
|
+
AND source_type = ${delivery.sourceType} AND source_id = ${delivery.sourceId}`;
|
|
222
|
+
const applyCursor = (delivery) => Effect.asVoid(sql `INSERT INTO actor_subscription_cursors (routing_key, tenant_id, actor_type, actor_id,
|
|
223
|
+
subscription, source_type, source_id, epoch, active, applied)
|
|
224
|
+
VALUES (${routingKey}, ${tenant}, ${actor}, ${id}, ${delivery.subscription},
|
|
225
|
+
${delivery.sourceType}, ${delivery.sourceId}, 0, true, ${delivery.position})
|
|
226
|
+
ON CONFLICT (routing_key, tenant_id, actor_type, actor_id, subscription, source_type, source_id)
|
|
227
|
+
DO UPDATE SET applied = greatest(actor_subscription_cursors.applied, EXCLUDED.applied)
|
|
228
|
+
WHERE actor_subscription_cursors.epoch = 0`);
|
|
229
|
+
const timeouts = sql `set_config('lock_timeout', ${`${policy.lockWaitMs}ms`}, true),
|
|
230
|
+
set_config('statement_timeout', ${`${policy.executionMs}ms`}, true),
|
|
231
|
+
set_config('durable.turn', 'on', true)
|
|
232
|
+
${role === undefined ? sql.literal("") : sql `, ${tenantSettings({ sql, role, tenant })}`}`;
|
|
233
|
+
/**
|
|
234
|
+
* Turns that set the same transaction settings, which a group sets once for
|
|
235
|
+
* all of them. Each part is encoded so no two settings share a key.
|
|
236
|
+
*/
|
|
237
|
+
const groupKey = [
|
|
238
|
+
policy.lockWaitMs,
|
|
239
|
+
policy.executionMs,
|
|
240
|
+
role === undefined ? "-" : `+${encodeURIComponent(role)}`,
|
|
241
|
+
role === undefined ? "-" : `+${encodeURIComponent(tenant)}`,
|
|
242
|
+
].join(":");
|
|
243
|
+
const admit = (batch, canonicals, view, session, begin, grouped = false) => {
|
|
244
|
+
const cold = view.generation === undefined;
|
|
245
|
+
const readsState = cold || view.state === undefined;
|
|
246
|
+
let admissions = [];
|
|
247
|
+
let bumped;
|
|
248
|
+
let stored = [];
|
|
249
|
+
const subscribed = batch.some(({ request }) => request.delivery !== undefined);
|
|
250
|
+
const commands = sql.csv(batch.map(({ request: { commandId, delivery } }, index) => subscribed
|
|
251
|
+
? sql `(${index}::integer, ${commandId}::text, ${canonicals[index]}::text,
|
|
252
|
+
${delivery?.subscription ?? null}::text, ${delivery?.sourceType ?? null}::text,
|
|
253
|
+
${delivery?.sourceId ?? null}::text)`
|
|
254
|
+
: sql `(${index}::integer, ${commandId}::text, ${canonicals[index]}::text)`));
|
|
255
|
+
const cursorColumns = subscribed
|
|
256
|
+
? sql `, s.epoch::text AS sub_epoch, s.active AS sub_active, s.applied::text AS sub_applied`
|
|
257
|
+
: sql.literal("");
|
|
258
|
+
const values = subscribed
|
|
259
|
+
? sql `(VALUES ${commands}) AS c (ordinal, command_id, payload, subscription, source_type, source_id)`
|
|
260
|
+
: sql `(VALUES ${commands}) AS c (ordinal, command_id, payload)`;
|
|
261
|
+
const cursorJoin = subscribed
|
|
262
|
+
? sql `LEFT JOIN actor_subscription_cursors s ON s.routing_key = g.routing_key
|
|
263
|
+
AND s.tenant_id = g.tenant_id AND s.actor_type = g.actor_type AND s.actor_id = g.actor_id
|
|
264
|
+
AND s.subscription = c.subscription AND s.source_type = c.source_type
|
|
265
|
+
AND s.source_id = c.source_id`
|
|
266
|
+
: sql.literal("");
|
|
267
|
+
const group = [
|
|
268
|
+
...begin,
|
|
269
|
+
...(grouped
|
|
270
|
+
? []
|
|
271
|
+
: [
|
|
272
|
+
cold
|
|
273
|
+
? Effect.asVoid(sql `INSERT INTO actor_generations (routing_key, tenant_id, actor_type, actor_id)
|
|
274
|
+
SELECT ${routingKey}, ${tenant}, ${actor}, ${id}
|
|
275
|
+
FROM (SELECT ${timeouts}) AS timeouts
|
|
276
|
+
ON CONFLICT DO NOTHING`)
|
|
277
|
+
: Effect.asVoid(sql `SELECT ${timeouts}`),
|
|
278
|
+
]),
|
|
279
|
+
Effect.map(sql `
|
|
280
|
+
WITH locked AS MATERIALIZED (
|
|
281
|
+
SELECT g.routing_key, g.tenant_id, g.actor_type, g.actor_id,
|
|
282
|
+
g.generation, g.created, g.event_sequence
|
|
283
|
+
FROM actor_generations g
|
|
284
|
+
WHERE ${rowOf({ sql, actor: { key: routingKey, ref }, alias: "g" })}
|
|
285
|
+
FOR UPDATE OF g ${grouped ? sql.literal("SKIP LOCKED") : sql.literal("")}
|
|
286
|
+
)
|
|
287
|
+
SELECT floor(extract(epoch FROM clock_timestamp()) * 1000)::text AS now,
|
|
288
|
+
g.generation::text AS generation, g.created,
|
|
289
|
+
c.payload::jsonb::text AS canonical,
|
|
290
|
+
r.caller_key, r.command, r.payload_hash, r.outcome, g.event_sequence::text AS head
|
|
291
|
+
${cursorColumns}
|
|
292
|
+
FROM locked g
|
|
293
|
+
CROSS JOIN ${values}
|
|
294
|
+
LEFT JOIN actor_receipts r ON r.routing_key = g.routing_key AND r.tenant_id = g.tenant_id
|
|
295
|
+
AND r.actor_type = g.actor_type AND r.actor_id = g.actor_id AND r.command_id = c.command_id
|
|
296
|
+
${cursorJoin}
|
|
297
|
+
ORDER BY c.ordinal`, (rows) => {
|
|
298
|
+
admissions = rows;
|
|
299
|
+
}),
|
|
300
|
+
...(cold
|
|
301
|
+
? [
|
|
302
|
+
Effect.map(sql `
|
|
303
|
+
UPDATE actor_generations SET generation = generation + 1 WHERE ${actorRow}
|
|
304
|
+
RETURNING generation::text AS generation`, (rows) => {
|
|
305
|
+
bumped = rows[0]?.generation;
|
|
306
|
+
}),
|
|
307
|
+
]
|
|
308
|
+
: []),
|
|
309
|
+
...(readsState
|
|
310
|
+
? [
|
|
311
|
+
Effect.map(sql `
|
|
312
|
+
SELECT key, value FROM actor_state WHERE ${actorRow}`, (rows) => {
|
|
313
|
+
stored = rows;
|
|
314
|
+
}),
|
|
315
|
+
]
|
|
316
|
+
: []),
|
|
317
|
+
...(statements ? [session.control(`SAVEPOINT ${HANDLER_SAVEPOINT}`)] : []),
|
|
318
|
+
];
|
|
319
|
+
const resume = Effect.fnUntraced(function* () {
|
|
320
|
+
const first = admissions[0];
|
|
321
|
+
if (first === undefined && grouped)
|
|
322
|
+
return yield* Effect.die(new Unseated());
|
|
323
|
+
if (first === undefined || (!cold && view.generation !== first.generation)) {
|
|
324
|
+
forget(cache);
|
|
325
|
+
return yield* Effect.die(RetryTurn.make({ message: "Stale actor generation" }));
|
|
326
|
+
}
|
|
327
|
+
const current = cold ? bumped : first.generation;
|
|
328
|
+
const now = Number(first.now) + clock.offsetMillis();
|
|
329
|
+
const { retryWindowMs } = yield* OutboxRuntime;
|
|
330
|
+
const expiryMarginMs = receiptMarginMs({
|
|
331
|
+
keepReceiptsMs: policy.keepReceiptsMs,
|
|
332
|
+
deliveryMs: policy.deliveryMs,
|
|
333
|
+
retryWindowMs,
|
|
334
|
+
});
|
|
335
|
+
const ticks = !cold || cron.length === 0 ? Effect.succeed([]) : cronTicks(routingKey, ref, cron);
|
|
336
|
+
const settled = Array.from({ length: batch.length });
|
|
337
|
+
let next;
|
|
338
|
+
const dirty = new Map();
|
|
339
|
+
const removed = new Set();
|
|
340
|
+
const staged = [];
|
|
341
|
+
const receipts = [];
|
|
342
|
+
let created = first.created;
|
|
343
|
+
let creates = false;
|
|
344
|
+
let replayed = false;
|
|
345
|
+
let wake = false;
|
|
346
|
+
const broadcasts = [];
|
|
347
|
+
let events = 0;
|
|
348
|
+
let intents = 0;
|
|
349
|
+
let jobs = 0;
|
|
350
|
+
const eventTags = new Set();
|
|
351
|
+
const tables = new Set();
|
|
352
|
+
const blobs = new Set();
|
|
353
|
+
const replays = new Set();
|
|
354
|
+
const committed = [];
|
|
355
|
+
const emitted = [];
|
|
356
|
+
const cursors = new Map();
|
|
357
|
+
const cursorKey = (delivery) => JSON.stringify([delivery.subscription, delivery.sourceType, delivery.sourceId]);
|
|
358
|
+
const cursorAt = (index, delivery) => cursors.get(cursorKey(delivery)) ?? admissions[index];
|
|
359
|
+
const apply = (index, delivery) => cursors.set(cursorKey(delivery), applied(delivery, cursorAt(index, delivery)));
|
|
360
|
+
const outboxes = [];
|
|
361
|
+
/**
|
|
362
|
+
* Admits one command of the batch once its payload hash is known: settles
|
|
363
|
+
* a replay, an expiry, or a refusal and yields nothing, or yields the
|
|
364
|
+
* hash its receipt records. A plain function rather than a generator,
|
|
365
|
+
* because a generator built for every turn is compiled again and again as
|
|
366
|
+
* the engine sees new copies of it.
|
|
367
|
+
*/
|
|
368
|
+
const admitHashed = (index, hash) => {
|
|
369
|
+
const { request, command } = batch[index];
|
|
370
|
+
const admitted = admissions[index];
|
|
371
|
+
if (admitted.outcome !== null)
|
|
372
|
+
return Effect.map(checkReceipt(request, hash, admitted).pipe(Effect.result), (replay) => {
|
|
373
|
+
if (Result.isSuccess(replay)) {
|
|
374
|
+
replayed = true;
|
|
375
|
+
replays.add(index);
|
|
376
|
+
}
|
|
377
|
+
settled[index] = replay;
|
|
378
|
+
return undefined;
|
|
379
|
+
});
|
|
380
|
+
if (request.external === true &&
|
|
381
|
+
Number(admitted.now) + (request.clockOffset ?? clock.offsetMillis()) >=
|
|
382
|
+
commandTimes(request.commandId).expiresAt + expiryMarginMs) {
|
|
383
|
+
settled[index] = Result.fail(ActorError.make({ reason: CommandExpired.make({ commandId: request.commandId }) }));
|
|
384
|
+
return Effect.undefined;
|
|
385
|
+
}
|
|
386
|
+
if (command.internal && !isSystem(request.caller))
|
|
387
|
+
return Effect.die(new Error("Internal commands require a System caller"));
|
|
388
|
+
const { delivery } = request;
|
|
389
|
+
const acknowledge = (reason) => {
|
|
390
|
+
replayed = true;
|
|
391
|
+
settled[index] = Result.succeed(Outcome.cases.Acknowledged.make({ reason }));
|
|
392
|
+
};
|
|
393
|
+
if (command.handler || delivery !== undefined) {
|
|
394
|
+
const caller = request.caller;
|
|
395
|
+
if (!command.handler ||
|
|
396
|
+
delivery === undefined ||
|
|
397
|
+
!isSystem(caller) ||
|
|
398
|
+
caller.source !== "subscription" ||
|
|
399
|
+
caller.ref?.actor !== delivery.sourceType ||
|
|
400
|
+
caller.ref.id !== delivery.sourceId)
|
|
401
|
+
return Effect.die(new Error("Subscription handlers accept only subscription deliveries"));
|
|
402
|
+
if (caller.ref.tenant !== tenant)
|
|
403
|
+
return Effect.die(new Error("A subscription delivery crosses tenants"));
|
|
404
|
+
const reason = acknowledgement(delivery, cursorAt(index, delivery));
|
|
405
|
+
if (reason !== undefined) {
|
|
406
|
+
acknowledge(reason);
|
|
407
|
+
return Effect.undefined;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (policy.createdBy !== undefined && !created && policy.createdBy !== request.command) {
|
|
411
|
+
if (delivery !== undefined && delivery.epoch === "0" && delivery.kind === "event") {
|
|
412
|
+
staged.push(applyCursor(delivery));
|
|
413
|
+
apply(index, delivery);
|
|
414
|
+
acknowledge("NotCreated");
|
|
415
|
+
return Effect.undefined;
|
|
416
|
+
}
|
|
417
|
+
settled[index] = Result.fail(ActorError.make({ reason: NotCreated.make({}) }));
|
|
418
|
+
return Effect.undefined;
|
|
419
|
+
}
|
|
420
|
+
if (!mintable ||
|
|
421
|
+
policy.createdBy !== request.command ||
|
|
422
|
+
created ||
|
|
423
|
+
!isMintedId(parent === undefined ? id : (parseChildId(id)?.local ?? "")))
|
|
424
|
+
return Effect.succeed(hash);
|
|
425
|
+
const refuse = () => {
|
|
426
|
+
settled[index] = Result.fail(ActorError.make({ reason: Unauthorized.make({ code: "access_denied" }) }));
|
|
427
|
+
return undefined;
|
|
428
|
+
};
|
|
429
|
+
return request.external === true
|
|
430
|
+
? Effect.sync(refuse)
|
|
431
|
+
: Effect.map(committedMintIntent(request, parent), (committed) => committed ? hash : refuse());
|
|
432
|
+
};
|
|
433
|
+
const admitOne = (index) => {
|
|
434
|
+
const request = batch[index].request;
|
|
435
|
+
const identityNow = Number(admissions[index].now) + (request.clockOffset ?? clock.offsetMillis());
|
|
436
|
+
const hashed = Effect.flatMap(hashCanonical(admissions[index].canonical), (hash) => admitHashed(index, hash));
|
|
437
|
+
if (request.external !== true)
|
|
438
|
+
return hashed;
|
|
439
|
+
return Effect.flatMap(checkIdentity(request.commandId, retryWindowMs, identityNow).pipe(Effect.result), (identity) => {
|
|
440
|
+
if (Result.isFailure(identity) &&
|
|
441
|
+
(request.redelivered !== true || !Schema.is(CommandExpired)(identity.failure.reason))) {
|
|
442
|
+
settled[index] = Result.fail(identity.failure);
|
|
443
|
+
return Effect.undefined;
|
|
444
|
+
}
|
|
445
|
+
batch[index].admitted = true;
|
|
446
|
+
return hashed;
|
|
447
|
+
});
|
|
448
|
+
};
|
|
449
|
+
let index = 0;
|
|
450
|
+
while (index < batch.length) {
|
|
451
|
+
const { request, command } = batch[index];
|
|
452
|
+
const hash = yield* admitOne(index);
|
|
453
|
+
if (hash === undefined) {
|
|
454
|
+
index += 1;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const start = index;
|
|
458
|
+
const members = [{ index, request, hash }];
|
|
459
|
+
index += 1;
|
|
460
|
+
while (index < batch.length &&
|
|
461
|
+
members.length < MERGE_CAP &&
|
|
462
|
+
merges({ previous: batch[start], next: batch[index] })) {
|
|
463
|
+
const joined = yield* admitOne(index);
|
|
464
|
+
if (joined !== undefined)
|
|
465
|
+
members.push({ index, request: batch[index].request, hash: joined });
|
|
466
|
+
index += 1;
|
|
467
|
+
}
|
|
468
|
+
next ??= readsState
|
|
469
|
+
? new Map(stored.map(({ key, value }) => [key, decompress(value)]))
|
|
470
|
+
: new Map(view.state);
|
|
471
|
+
const given = next;
|
|
472
|
+
const head = String(BigInt(first.head) + BigInt(events));
|
|
473
|
+
const savepoint = `SAVEPOINT ${HANDLER_SAVEPOINT}`;
|
|
474
|
+
if (statements && start > 0)
|
|
475
|
+
yield* session.defer(savepoint);
|
|
476
|
+
const business = yield* hooks.at("beforeHandler", request).pipe(Effect.andThen(() => members.length === 1
|
|
477
|
+
? command.run(request, [...given], { head, connections })
|
|
478
|
+
: command.merge(members.map((member) => member.request), [...given])), Effect.catchIf(SqlError.isSqlError, Effect.die), Effect.result);
|
|
479
|
+
const result = Result.isSuccess(business)
|
|
480
|
+
? business.success
|
|
481
|
+
: business.failure;
|
|
482
|
+
if (statements && (start === 0 || !session.withdraw(savepoint)))
|
|
483
|
+
yield* session.defer(Result.isSuccess(business)
|
|
484
|
+
? `RELEASE SAVEPOINT ${HANDLER_SAVEPOINT}`
|
|
485
|
+
: `ROLLBACK TO SAVEPOINT ${HANDLER_SAVEPOINT}`);
|
|
486
|
+
const written = new Map(result.state);
|
|
487
|
+
if (result.complete)
|
|
488
|
+
for (const key of given.keys())
|
|
489
|
+
if (!written.has(key)) {
|
|
490
|
+
given.delete(key);
|
|
491
|
+
dirty.delete(key);
|
|
492
|
+
removed.add(key);
|
|
493
|
+
}
|
|
494
|
+
for (const [key, value] of written) {
|
|
495
|
+
given.set(key, value);
|
|
496
|
+
dirty.set(key, value);
|
|
497
|
+
removed.delete(key);
|
|
498
|
+
}
|
|
499
|
+
if (result.events.length > 0) {
|
|
500
|
+
const appended = yield* eventsStatement(request, routingKey, result.events);
|
|
501
|
+
staged.push(appended.statement);
|
|
502
|
+
committed.push({
|
|
503
|
+
after: head,
|
|
504
|
+
events: result.events,
|
|
505
|
+
commandId: request.commandId,
|
|
506
|
+
});
|
|
507
|
+
emitted.push(appended.stamp);
|
|
508
|
+
}
|
|
509
|
+
events += result.events.length;
|
|
510
|
+
for (const event of result.events)
|
|
511
|
+
eventTags.add(event.tag);
|
|
512
|
+
for (const table of result.writes?.tables ?? [])
|
|
513
|
+
tables.add(table);
|
|
514
|
+
for (const blob of result.writes?.blobs ?? [])
|
|
515
|
+
blobs.add(blob);
|
|
516
|
+
intents += result.outbox.intents.length;
|
|
517
|
+
jobs += result.outbox.jobs.length;
|
|
518
|
+
if (Outcome.guards.Success(result.outcome))
|
|
519
|
+
broadcasts.push(...(result.broadcasts ?? []));
|
|
520
|
+
if (yield* notifyEvents(request, routingKey, result.events, waited))
|
|
521
|
+
wake = true;
|
|
522
|
+
if (Outcome.guards.Success(result.outcome) &&
|
|
523
|
+
policy.createdBy === request.command &&
|
|
524
|
+
!created) {
|
|
525
|
+
created = true;
|
|
526
|
+
creates = true;
|
|
527
|
+
}
|
|
528
|
+
const outbox = yield* outboxStatements(routingKey, request.ref, result.outbox, Effect.succeed(now), { slackMs: policy.executionMs });
|
|
529
|
+
staged.push(...outbox.statements);
|
|
530
|
+
outboxes.push(outbox.replies);
|
|
531
|
+
const { delivery } = request;
|
|
532
|
+
if (delivery !== undefined) {
|
|
533
|
+
apply(start, delivery);
|
|
534
|
+
staged.push(delivery.kind === "rejected"
|
|
535
|
+
? Effect.asVoid(sql `UPDATE actor_subscription_cursors SET active = false
|
|
536
|
+
WHERE ${cursorOf(delivery)} AND epoch = ${delivery.epoch}`)
|
|
537
|
+
: delivery.epoch === "0"
|
|
538
|
+
? applyCursor(delivery)
|
|
539
|
+
: Effect.asVoid(sql `UPDATE actor_subscription_cursors SET applied = ${delivery.position}
|
|
540
|
+
WHERE ${cursorOf(delivery)} AND epoch = ${delivery.epoch}`));
|
|
541
|
+
}
|
|
542
|
+
const outcome = yield* encodeOutcome(result.outcome).pipe(Effect.orDie);
|
|
543
|
+
for (const member of members) {
|
|
544
|
+
receipts.push({
|
|
545
|
+
routing_key: routingKey,
|
|
546
|
+
tenant_id: tenant,
|
|
547
|
+
actor_type: actor,
|
|
548
|
+
actor_id: id,
|
|
549
|
+
command_id: member.request.commandId,
|
|
550
|
+
command: member.request.command,
|
|
551
|
+
payload_hash: member.hash,
|
|
552
|
+
caller_key: callerKey(member.request.caller),
|
|
553
|
+
outcome,
|
|
554
|
+
expires_at_ms: commandTimes(member.request.commandId).expiresAt,
|
|
555
|
+
});
|
|
556
|
+
yield* hooks.at("beforeCommit", member.request);
|
|
557
|
+
settled[member.index] = Result.succeed(result.outcome);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (receipts.length === 0 && staged.length === 0 && !(cold && replayed))
|
|
561
|
+
return {
|
|
562
|
+
writes: undefined,
|
|
563
|
+
settled,
|
|
564
|
+
generation: current,
|
|
565
|
+
state: view.state,
|
|
566
|
+
wake: false,
|
|
567
|
+
broadcasts: [],
|
|
568
|
+
head: first.head,
|
|
569
|
+
committed: [],
|
|
570
|
+
emitted: [],
|
|
571
|
+
outbox: [],
|
|
572
|
+
replays,
|
|
573
|
+
written: nothingWritten,
|
|
574
|
+
wrote: nothingWrote,
|
|
575
|
+
};
|
|
576
|
+
const writes = [];
|
|
577
|
+
if (dirty.size > 0)
|
|
578
|
+
writes.push(Effect.asVoid(sql `INSERT INTO actor_state ${sql.insert([...dirty].map(([key, value]) => ({
|
|
579
|
+
routing_key: routingKey,
|
|
580
|
+
tenant_id: tenant,
|
|
581
|
+
actor_type: actor,
|
|
582
|
+
actor_id: id,
|
|
583
|
+
key,
|
|
584
|
+
value: compress(value),
|
|
585
|
+
})))}
|
|
586
|
+
ON CONFLICT (routing_key, tenant_id, actor_type, actor_id, key) DO UPDATE SET value = EXCLUDED.value`));
|
|
587
|
+
if (removed.size > 0)
|
|
588
|
+
writes.push(Effect.asVoid(sql `DELETE FROM actor_state WHERE ${actorRow} AND key IN ${sql.in([...removed])}`));
|
|
589
|
+
writes.push(...staged);
|
|
590
|
+
if (creates)
|
|
591
|
+
writes.push(Effect.asVoid(sql `UPDATE actor_generations SET created = true WHERE ${actorRow}`));
|
|
592
|
+
if (receipts.length > 0) {
|
|
593
|
+
writes.push(Effect.asVoid(sql `INSERT INTO actor_receipts ${sql.insert(receipts)}`));
|
|
594
|
+
if (accounting !== undefined)
|
|
595
|
+
writes.push(accounting.commands({
|
|
596
|
+
ref,
|
|
597
|
+
commandIds: receipts.map((receipt) => receipt.command_id),
|
|
598
|
+
sql,
|
|
599
|
+
}));
|
|
600
|
+
}
|
|
601
|
+
writes.push(...(yield* ticks));
|
|
602
|
+
return {
|
|
603
|
+
writes,
|
|
604
|
+
settled,
|
|
605
|
+
generation: current,
|
|
606
|
+
state: next,
|
|
607
|
+
wake,
|
|
608
|
+
broadcasts,
|
|
609
|
+
head: String(BigInt(first.head) + BigInt(events)),
|
|
610
|
+
committed,
|
|
611
|
+
emitted,
|
|
612
|
+
outbox: outboxes,
|
|
613
|
+
replays,
|
|
614
|
+
written: { receipts: receipts.length, events, intents, jobs },
|
|
615
|
+
wrote: {
|
|
616
|
+
state: dirty.size > 0 || removed.size > 0,
|
|
617
|
+
events: [...eventTags],
|
|
618
|
+
tables: [...tables],
|
|
619
|
+
blobs: [...blobs],
|
|
620
|
+
},
|
|
621
|
+
};
|
|
622
|
+
});
|
|
623
|
+
return { group, resume };
|
|
624
|
+
};
|
|
625
|
+
const bounded = (effect) => effect.pipe(Effect.interruptible, Effect.timeoutOrElse({
|
|
626
|
+
duration: policy.executionMs,
|
|
627
|
+
orElse: () => Effect.die(RetryTurn.make({ message: "Command execution timeout" })),
|
|
628
|
+
}), Effect.catchIf(SqlError.isSqlError, Effect.die));
|
|
629
|
+
const view = () => ({ generation: cache.generation, state: cache.state });
|
|
630
|
+
let current = run.first;
|
|
631
|
+
let orphan;
|
|
632
|
+
let answering = false;
|
|
633
|
+
let poolRefused = false;
|
|
634
|
+
const locate = (batch, following) => {
|
|
635
|
+
current = batch;
|
|
636
|
+
orphan = following;
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* Remembers what a commit proved and hands the ended batch to the
|
|
640
|
+
* activation, which publishes it. Only a commit replaces the cache.
|
|
641
|
+
*/
|
|
642
|
+
const finish = (batch, plan, version, endedAtMs) => Effect.suspend(() => {
|
|
643
|
+
if (plan.writes !== undefined) {
|
|
644
|
+
cache.generation = plan.generation;
|
|
645
|
+
cache.state = plan.state;
|
|
646
|
+
}
|
|
647
|
+
answering = true;
|
|
648
|
+
return run.committed(batch, {
|
|
649
|
+
settled: plan.settled,
|
|
650
|
+
broadcasts: plan.broadcasts,
|
|
651
|
+
head: plan.head,
|
|
652
|
+
committed: plan.committed.map((entry, index) => ({
|
|
653
|
+
...entry,
|
|
654
|
+
emittedAtMs: plan.emitted[index].emittedAtMs,
|
|
655
|
+
})),
|
|
656
|
+
cancelledJobs: plan.outbox.flatMap((replies) => replies.cancelledIds),
|
|
657
|
+
generation: plan.generation,
|
|
658
|
+
replays: plan.replays,
|
|
659
|
+
written: plan.writes === undefined ? nothingWritten : plan.written,
|
|
660
|
+
version,
|
|
661
|
+
endedAtMs,
|
|
662
|
+
wrote: plan.writes === undefined ? nothingWrote : plan.wrote,
|
|
663
|
+
wake: plan.wake ||
|
|
664
|
+
plan.outbox.some((replies) => replies.wake) ||
|
|
665
|
+
plan.emitted.some((stamp) => stamp.fed),
|
|
666
|
+
cancelled: plan.outbox.some((replies) => replies.cancelled),
|
|
667
|
+
});
|
|
668
|
+
}).pipe(Effect.andThen(Effect.sync(() => {
|
|
669
|
+
answering = false;
|
|
670
|
+
})));
|
|
671
|
+
/**
|
|
672
|
+
* The one batch lifecycle both backends share. Each batch is located, so a
|
|
673
|
+
* failure names it and any batch admitted behind it, and prepared before its
|
|
674
|
+
* handlers can run; one whose admission is not yet under way is opened;
|
|
675
|
+
* `transact` ends its transaction; and the ended batch is finished within its
|
|
676
|
+
* own span before the next is taken. The next batch is the one `transact`
|
|
677
|
+
* took while committing, whose admission may already ride behind this
|
|
678
|
+
* `COMMIT`, else whatever is waiting once this batch was answered.
|
|
679
|
+
* Opening stays lazy because preparation may acquire a generation and fill
|
|
680
|
+
* the cache whose view admission uses.
|
|
681
|
+
*/
|
|
682
|
+
const drive = (open, transact) => {
|
|
683
|
+
const step = (admitting, pending) => {
|
|
684
|
+
locate(admitting, undefined);
|
|
685
|
+
return run.prepare.pipe(Effect.andThen(() => (pending === undefined ? open(admitting) : Effect.succeed(pending))), Effect.flatMap((admitted) => transact(admitting, admitted).pipe(Effect.tap((ended) => finish(admitting, ended.plan, ended.version, ended.endedAtMs)), run.observe(admitting))), Effect.flatMap((ended) => ended.following === undefined
|
|
686
|
+
? Effect.flatMap(run.next, (next) => next === undefined ? Effect.void : step(next, ended.chained))
|
|
687
|
+
: step(ended.following, ended.chained)));
|
|
688
|
+
};
|
|
689
|
+
return Effect.suspend(() => step(run.first, undefined));
|
|
690
|
+
};
|
|
691
|
+
/**
|
|
692
|
+
* Postgres: a leased session per chain of batches. A batch is opened by
|
|
693
|
+
* queuing `BEGIN` and its admission group; `transact` waits for those
|
|
694
|
+
* replies, runs the handlers, takes the batch already waiting and, when this
|
|
695
|
+
* one leaves the cache warm, queues that batch's `BEGIN` and admission right
|
|
696
|
+
* behind this `COMMIT` in the same flight. The commit tag and version are read
|
|
697
|
+
* in that flight too.
|
|
698
|
+
*
|
|
699
|
+
* A batch that chains nothing ends with every reply in and no transaction
|
|
700
|
+
* open, so the session goes back to the pool before its callers are
|
|
701
|
+
* answered; publishing never holds a session another turn waits for. A batch
|
|
702
|
+
* that arrives later leases a session again.
|
|
703
|
+
*/
|
|
704
|
+
const pipelined = (turns) => Effect.scoped(Effect.gen(function* () {
|
|
705
|
+
const scope = yield* Effect.scope;
|
|
706
|
+
let lease;
|
|
707
|
+
const leased = Effect.suspend(() => {
|
|
708
|
+
if (lease !== undefined)
|
|
709
|
+
return Effect.void;
|
|
710
|
+
return Effect.gen(function* () {
|
|
711
|
+
const held = yield* Scope.fork(scope);
|
|
712
|
+
const leasing = yield* Clock.currentTimeMillis;
|
|
713
|
+
const connection = yield* Scope.provide(turns.lease, held).pipe(Effect.tapError((error) => Effect.sync(() => {
|
|
714
|
+
poolRefused = isPoolRefusal(error);
|
|
715
|
+
})), Effect.onError((cause) => Scope.close(held, Exit.failCause(cause))));
|
|
716
|
+
yield* record(Metrics.poolWait, {}, (yield* Clock.currentTimeMillis) - leasing);
|
|
717
|
+
lease = { connection, scope: held };
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
const release = Effect.suspend(() => {
|
|
721
|
+
const held = lease;
|
|
722
|
+
lease = undefined;
|
|
723
|
+
return held === undefined ? Effect.void : Scope.close(held.scope, Exit.void);
|
|
724
|
+
});
|
|
725
|
+
const session = () => lease.connection;
|
|
726
|
+
let open = false;
|
|
727
|
+
const deferred = [];
|
|
728
|
+
const control = (text) => session().query(text, [], true);
|
|
729
|
+
const flush = () => deferred.splice(0).map((text) => Effect.asVoid(control(text)));
|
|
730
|
+
const send = (statement) => Effect.suspend(() => {
|
|
731
|
+
if (deferred.length === 0)
|
|
732
|
+
return statement;
|
|
733
|
+
let reply;
|
|
734
|
+
return sendPipelined([
|
|
735
|
+
...flush(),
|
|
736
|
+
Effect.map(statement, (value) => {
|
|
737
|
+
reply = { value };
|
|
738
|
+
}),
|
|
739
|
+
]).pipe(Effect.map(() => reply.value));
|
|
740
|
+
});
|
|
741
|
+
const pipeline = {
|
|
742
|
+
send: sendPipelined,
|
|
743
|
+
control: (text) => Effect.asVoid(control(text)),
|
|
744
|
+
defer: (text) => Effect.sync(() => {
|
|
745
|
+
deferred.push(text);
|
|
746
|
+
}),
|
|
747
|
+
withdraw: (text) => deferred.at(-1) === text && deferred.pop() !== undefined,
|
|
748
|
+
};
|
|
749
|
+
const begin = Effect.suspend(() => {
|
|
750
|
+
open = true;
|
|
751
|
+
return Effect.asVoid(control("BEGIN"));
|
|
752
|
+
});
|
|
753
|
+
const inTurn = (effect) => Effect.suspend(() => Effect.provideService(effect, sql.transactionService, [
|
|
754
|
+
asSqlConnection({ connection: session(), send }),
|
|
755
|
+
0,
|
|
756
|
+
]));
|
|
757
|
+
const queue = (batch, view, ahead) => Effect.flatMap(canonicalsOf(batch), (canonicals) => {
|
|
758
|
+
const admission = admit(batch, canonicals, view, pipeline, [begin]);
|
|
759
|
+
return Effect.map(queueStatements({ scope, group: [...ahead, ...admission.group] }), (flight) => ({ admission, flight }));
|
|
760
|
+
});
|
|
761
|
+
/**
|
|
762
|
+
* Queues a batch's commit group, and the next batch's admission behind
|
|
763
|
+
* it when the cache stays warm, then waits for the commit replies.
|
|
764
|
+
*/
|
|
765
|
+
const commitPlan = (batch, plan, following) => {
|
|
766
|
+
const ending = plan.writes === undefined ? "ROLLBACK" : "COMMIT";
|
|
767
|
+
const after = plan.writes === undefined ? view() : { generation: plan.generation, state: plan.state };
|
|
768
|
+
const chained = following !== undefined &&
|
|
769
|
+
after.generation !== undefined &&
|
|
770
|
+
after.state !== undefined &&
|
|
771
|
+
!run.publishesUnderLock(batch);
|
|
772
|
+
let tag;
|
|
773
|
+
let version = "";
|
|
774
|
+
let endedAtMs = 0;
|
|
775
|
+
const commit = [
|
|
776
|
+
...(plan.writes === undefined ? [] : [...flush(), ...plan.writes]),
|
|
777
|
+
Effect.map(control(ending), (result) => {
|
|
778
|
+
tag = result.command;
|
|
779
|
+
if (!chained)
|
|
780
|
+
open = false;
|
|
781
|
+
}),
|
|
782
|
+
Effect.map(session().query(COMMIT_VERSION, [], true), (result) => {
|
|
783
|
+
const ended = result.rows[0];
|
|
784
|
+
version = ended.version;
|
|
785
|
+
endedAtMs = Number(ended.now);
|
|
786
|
+
}),
|
|
787
|
+
];
|
|
788
|
+
locate(batch, following);
|
|
789
|
+
const upcoming = chained
|
|
790
|
+
? queue(following, after, commit)
|
|
791
|
+
: Effect.map(queueStatements({ scope, group: commit }), (flight) => ({
|
|
792
|
+
admission: undefined,
|
|
793
|
+
flight,
|
|
794
|
+
}));
|
|
795
|
+
return Effect.flatMap(upcoming, ({ admission: next, flight }) => {
|
|
796
|
+
const answered = awaitReplies(flight.slice(0, commit.length));
|
|
797
|
+
return Effect.map(ending === "COMMIT" ? answered.pipe(Effect.withSpan(SpanNames.commit)) : answered, () => ({
|
|
798
|
+
plan,
|
|
799
|
+
version,
|
|
800
|
+
endedAtMs,
|
|
801
|
+
ending,
|
|
802
|
+
tag,
|
|
803
|
+
following,
|
|
804
|
+
chained: next === undefined
|
|
805
|
+
? undefined
|
|
806
|
+
: { admission: next, admitted: flight.slice(commit.length) },
|
|
807
|
+
}));
|
|
808
|
+
});
|
|
809
|
+
};
|
|
810
|
+
const alone = (batch, { admission, admitted }, taken) => awaitReplies(admitted).pipe(Effect.andThen(() => admission.resume()), Effect.flatMap((plan) => Effect.flatMap(taken === undefined ? run.next : Effect.succeed(taken), (following) => commitPlan(batch, plan, following))), inTurn, bounded, Effect.filterOrElse((stepped) => stepped.ending !== "COMMIT" || stepped.tag === "COMMIT", () => Effect.die(RetryTurn.make({ message: "Turn commit rolled back" }))), Effect.tap((stepped) => (stepped.chained === undefined ? release : Effect.void)));
|
|
811
|
+
const openAlone = (batch) => leased.pipe(Effect.andThen(queue(batch, view(), []).pipe(inTurn)), Effect.map(({ admission, flight }) => ({ admission, admitted: flight })));
|
|
812
|
+
const onMember = (member) => (effect) => Effect.suspend(() => Effect.provideService(effect, sql.transactionService, [
|
|
813
|
+
asSqlConnection({ connection: member.connection(), send: member.send }),
|
|
814
|
+
0,
|
|
815
|
+
]));
|
|
816
|
+
/**
|
|
817
|
+
* Joins the forming group for this batch's settings with its fenced
|
|
818
|
+
* admission read alone; the group opens the transaction.
|
|
819
|
+
*/
|
|
820
|
+
const openGrouped = (batch, joining) => Effect.flatMap(canonicalsOf(batch), (canonicals) => {
|
|
821
|
+
const admission = admit(batch, canonicals, view(), pipeline, [], true);
|
|
822
|
+
return joining
|
|
823
|
+
.join({
|
|
824
|
+
key: groupKey,
|
|
825
|
+
settings: (connection) => Effect.provideService(Effect.asVoid(sql `SELECT ${timeouts}`), sql.transactionService, [asSqlConnection({ connection, send: (statement) => statement }), 0]),
|
|
826
|
+
cancel: (connection) => Effect.ignore(sql `SELECT pg_cancel_backend(${connection.processId})`),
|
|
827
|
+
admission: (member) => admission.group.map(onMember(member)),
|
|
828
|
+
})
|
|
829
|
+
.pipe(Effect.tapError((error) => Effect.sync(() => {
|
|
830
|
+
poolRefused = isPoolRefusal(error);
|
|
831
|
+
})), Effect.map((member) => ({ admission, admitted: [], member })));
|
|
832
|
+
});
|
|
833
|
+
/**
|
|
834
|
+
* A grouped batch: its own admission replies, its handlers, and its
|
|
835
|
+
* writes handed to the group, answered once the group commits. When
|
|
836
|
+
* the actor's next batch is already waiting and this one leaves the
|
|
837
|
+
* cache warm, that batch's admission rides behind the group's
|
|
838
|
+
* `COMMIT` and the batch takes the group's session, as a batch on its
|
|
839
|
+
* own session would. `ended` is undefined when the batch must run again
|
|
840
|
+
* alone: the group ended without it, aborted on a neighbour's
|
|
841
|
+
* statement, skipped its locked generation row, or the batch tried to
|
|
842
|
+
* send another statement.
|
|
843
|
+
*/
|
|
844
|
+
const grouped = (batch, admission, member) => {
|
|
845
|
+
let following;
|
|
846
|
+
return Effect.gen(function* () {
|
|
847
|
+
const replies = yield* Effect.exit(awaitReplies(member.replies()));
|
|
848
|
+
if (Exit.isFailure(replies)) {
|
|
849
|
+
yield* member.leave(true);
|
|
850
|
+
const error = errorOf(replies);
|
|
851
|
+
if (error !== undefined && abortedBefore(error))
|
|
852
|
+
return undefined;
|
|
853
|
+
return yield* Effect.failCause(replies.cause);
|
|
854
|
+
}
|
|
855
|
+
if (!(yield* member.handling))
|
|
856
|
+
return undefined;
|
|
857
|
+
const plan = yield* onMember(member)(admission.resume());
|
|
858
|
+
following = yield* run.next;
|
|
859
|
+
locate(batch, following);
|
|
860
|
+
const after = plan.writes === undefined
|
|
861
|
+
? view()
|
|
862
|
+
: { generation: plan.generation, state: plan.state };
|
|
863
|
+
let next;
|
|
864
|
+
const handoff = following !== undefined &&
|
|
865
|
+
after.generation !== undefined &&
|
|
866
|
+
after.state !== undefined &&
|
|
867
|
+
!run.publishesUnderLock(batch)
|
|
868
|
+
? yield* Effect.map(canonicalsOf(following), (canonicals) => ({
|
|
869
|
+
chain: (connection) => {
|
|
870
|
+
const on = (effect) => Effect.provideService(effect, sql.transactionService, [
|
|
871
|
+
asSqlConnection({ connection, send }),
|
|
872
|
+
0,
|
|
873
|
+
]);
|
|
874
|
+
next = admit(following, canonicals, after, pipeline, [
|
|
875
|
+
Effect.asVoid(connection.query("BEGIN", [], true)),
|
|
876
|
+
]);
|
|
877
|
+
return next.group.map(on);
|
|
878
|
+
},
|
|
879
|
+
adopt: (connection, held) => Effect.suspend(() => {
|
|
880
|
+
if (Predicate.isTagged(scope.state, "Closed"))
|
|
881
|
+
return Effect.succeed(false);
|
|
882
|
+
lease = { connection, scope: held };
|
|
883
|
+
open = true;
|
|
884
|
+
return Effect.as(Scope.addFinalizer(scope, Scope.close(held, Exit.void)), true);
|
|
885
|
+
}),
|
|
886
|
+
}))
|
|
887
|
+
: undefined;
|
|
888
|
+
const writes = (plan.writes ?? []).map(onMember(member));
|
|
889
|
+
const waiting = member.commit(writes, handoff);
|
|
890
|
+
const shared = yield* writes.length > 0
|
|
891
|
+
? waiting.pipe(Effect.withSpan(SpanNames.commit))
|
|
892
|
+
: waiting;
|
|
893
|
+
if (Shared.$is("Alone")(shared))
|
|
894
|
+
return undefined;
|
|
895
|
+
return {
|
|
896
|
+
plan,
|
|
897
|
+
version: shared.version,
|
|
898
|
+
endedAtMs: shared.endedAtMs,
|
|
899
|
+
following,
|
|
900
|
+
chained: shared.chained === undefined || next === undefined
|
|
901
|
+
? undefined
|
|
902
|
+
: { admission: next, admitted: [...shared.chained] },
|
|
903
|
+
};
|
|
904
|
+
}).pipe(Effect.catchDefect((defect) => defect instanceof Unseated ? Effect.undefined : Effect.die(defect)), Effect.onExit(() => member.leave(false)), bounded, Effect.map((ended) => ({ ended, following })));
|
|
905
|
+
};
|
|
906
|
+
const transact = (batch, admitting) => admitting.member === undefined
|
|
907
|
+
? alone(batch, admitting)
|
|
908
|
+
: Effect.flatMap(grouped(batch, admitting.admission, admitting.member), (outcome) => outcome.ended !== undefined
|
|
909
|
+
? Effect.succeed(outcome.ended)
|
|
910
|
+
: Effect.flatMap(openAlone(batch), (opened) => alone(batch, opened, outcome.following)));
|
|
911
|
+
let first = true;
|
|
912
|
+
return yield* drive((batch) => {
|
|
913
|
+
const joining = first && view().generation !== undefined && view().state !== undefined
|
|
914
|
+
? groups
|
|
915
|
+
: undefined;
|
|
916
|
+
first = false;
|
|
917
|
+
return joining === undefined ? openAlone(batch) : openGrouped(batch, joining);
|
|
918
|
+
}, transact).pipe(Effect.onExit((exit) => {
|
|
919
|
+
if (Exit.isSuccess(exit) || !open)
|
|
920
|
+
return Effect.void;
|
|
921
|
+
const connection = session();
|
|
922
|
+
if (Cause.hasInterrupts(exit.cause))
|
|
923
|
+
return sql `SELECT pg_cancel_backend(${connection.processId})`.pipe(Effect.ignore, Effect.andThen(turns.invalidate(connection)));
|
|
924
|
+
return control("ROLLBACK").pipe(Effect.flatMap((result) => result.command === "ROLLBACK" ? Effect.void : turns.invalidate(connection)), Effect.catch(() => turns.invalidate(connection)));
|
|
925
|
+
}));
|
|
926
|
+
}));
|
|
927
|
+
/**
|
|
928
|
+
* PGlite: one in-process session and nothing to pipeline, so each batch runs
|
|
929
|
+
* its groups one statement at a time inside `withTransaction`, which holds
|
|
930
|
+
* the session only for that transaction; a batch with nothing to commit rolls
|
|
931
|
+
* back. The commit version is read after the transaction ends.
|
|
932
|
+
*/
|
|
933
|
+
const sequential = drive(() => Effect.void, (batch) => Effect.gen(function* () {
|
|
934
|
+
const control = (text) => Effect.asVoid(sql.unsafe(text));
|
|
935
|
+
const session = { send: sendSequentially, control, defer: control, withdraw: () => false };
|
|
936
|
+
const plan = yield* bounded(sql
|
|
937
|
+
.withTransaction(Effect.gen(function* () {
|
|
938
|
+
const admission = admit(batch, yield* canonicalsOf(batch), view(), session, []);
|
|
939
|
+
yield* sendSequentially(admission.group);
|
|
940
|
+
const decided = yield* admission.resume();
|
|
941
|
+
if (decided.writes === undefined)
|
|
942
|
+
return yield* Effect.fail(new RolledBack(decided));
|
|
943
|
+
yield* sendSequentially(decided.writes).pipe(Effect.withSpan(SpanNames.commit));
|
|
944
|
+
return decided;
|
|
945
|
+
}))
|
|
946
|
+
.pipe(Effect.catchIf((error) => error instanceof RolledBack, (rolled) => Effect.succeed(rolled.plan))));
|
|
947
|
+
const [ended] = yield* sql.unsafe(COMMIT_VERSION);
|
|
948
|
+
return {
|
|
949
|
+
plan,
|
|
950
|
+
version: ended.version,
|
|
951
|
+
endedAtMs: Number(ended.now),
|
|
952
|
+
};
|
|
953
|
+
}));
|
|
954
|
+
const turns = yield* Effect.serviceOption(TurnConnections);
|
|
955
|
+
const groups = statements || waited.size > 0 || (yield* NekiTurnSessions)
|
|
956
|
+
? undefined
|
|
957
|
+
: Option.getOrUndefined(yield* Effect.serviceOption(TurnGroups));
|
|
958
|
+
const exit = yield* (Option.isSome(turns) ? pipelined(turns.value) : sequential).pipe(Effect.onError(() => Effect.sync(() => {
|
|
959
|
+
cache.state = undefined;
|
|
960
|
+
})), Effect.exit);
|
|
961
|
+
if (Exit.isSuccess(exit))
|
|
962
|
+
return undefined;
|
|
963
|
+
if (Cause.hasInterruptsOnly(exit.cause))
|
|
964
|
+
return yield* Effect.failCause(exit.cause);
|
|
965
|
+
return {
|
|
966
|
+
batch: current,
|
|
967
|
+
orphan,
|
|
968
|
+
cause: exit.cause,
|
|
969
|
+
committed: answering,
|
|
970
|
+
poolRefused,
|
|
971
|
+
};
|
|
972
|
+
});
|