@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,685 @@
|
|
|
1
|
+
import { Cause, Clock, Crypto, Deferred, Effect, Exit, FiberSet, Queue, Random, Result, Schema, Semaphore, } from "effect";
|
|
2
|
+
import { SqlClient, Statement } from "effect/sql";
|
|
3
|
+
import { Outcome, Request } from "../request.js";
|
|
4
|
+
import {} from "../members.js";
|
|
5
|
+
import { progressPool } from "../jobs/progress.js";
|
|
6
|
+
import { groupLock, groupRow, jobAttempts, } from "../jobs/attempt.js";
|
|
7
|
+
import { CRON_PREFIX } from "../cron/key.js";
|
|
8
|
+
import { cronTicks } from "../cron/schedule.js";
|
|
9
|
+
import { TurnHooks } from "./hooks.js";
|
|
10
|
+
import { count as tally, Metrics } from "../telemetry/metrics.js";
|
|
11
|
+
import { SpanNames } from "../telemetry/spans.js";
|
|
12
|
+
import { databaseTime, FrameworkClock } from "./admission.js";
|
|
13
|
+
import { BUCKETS, CallerJson } from "./outbox.js";
|
|
14
|
+
import { shardClients } from "../database/shards.js";
|
|
15
|
+
/**
|
|
16
|
+
* A drain whose deliveries keep staging due work after this many rounds, each
|
|
17
|
+
* of which claimed every row then due, is a delivery loop.
|
|
18
|
+
*/
|
|
19
|
+
const DRAIN_ROUNDS = 100;
|
|
20
|
+
/**
|
|
21
|
+
* The claim statement's result columns before its last three, in order: each
|
|
22
|
+
* outbox column's source in the claimed row aliased `o`, and the value a
|
|
23
|
+
* result row that claimed no outbox row carries in its place.
|
|
24
|
+
*/
|
|
25
|
+
const CLAIMED_COLUMNS = [
|
|
26
|
+
["kind", "o.kind", "NULL::text"],
|
|
27
|
+
["routing_key", "o.routing_key::text", "NULL::text"],
|
|
28
|
+
["intent_id", "o.intent_id", "NULL::text"],
|
|
29
|
+
["attempts", "o.attempts", "0"],
|
|
30
|
+
["last_error", "o.last_error", "NULL::text"],
|
|
31
|
+
["ambiguous", "o.ambiguous", "false"],
|
|
32
|
+
["tenant_id", "o.tenant_id", "NULL::text"],
|
|
33
|
+
["actor_type", "o.actor_type", "NULL::text"],
|
|
34
|
+
["actor_id", "o.actor_id", "NULL::text"],
|
|
35
|
+
["target_type", "o.target_type", "NULL::text"],
|
|
36
|
+
["target_id", "o.target_id", "NULL::text"],
|
|
37
|
+
["command", "o.command", "NULL::text"],
|
|
38
|
+
["payload", "o.payload", "NULL::text"],
|
|
39
|
+
["payload_version", "o.payload_version", "0"],
|
|
40
|
+
["caller", "o.caller", "NULL::text"],
|
|
41
|
+
["claimed_until", "o.due_at_ms::text", "NULL::text"],
|
|
42
|
+
["timer_key", "o.timer_key", "NULL::text"],
|
|
43
|
+
["scheduled_at", "o.scheduled_at_ms::text", "NULL::text"],
|
|
44
|
+
["cancelled", "o.cancelled_at_ms IS NOT NULL", "false"],
|
|
45
|
+
["maybe_applied", "o.maybe_applied", "false"],
|
|
46
|
+
];
|
|
47
|
+
/** The claimed columns of the outbox row aliased `o`. */
|
|
48
|
+
const claimedColumns = (sql) => sql.literal(CLAIMED_COLUMNS.map(([name, source]) => `${source} AS ${name}`).join(", "));
|
|
49
|
+
/** The claimed columns of a result row that claimed no outbox row, with `kind` set. */
|
|
50
|
+
const unclaimedColumns = (sql, kind) => sql `${kind}::text, ${sql.literal(CLAIMED_COLUMNS.slice(1)
|
|
51
|
+
.map(([, , empty]) => empty)
|
|
52
|
+
.join(", "))}`;
|
|
53
|
+
/**
|
|
54
|
+
* The due-work probe: up to `limit` due rows of `kind` from one
|
|
55
|
+
* `(bucket, kind, due_at_ms)` index range per bucket, so its cost follows due
|
|
56
|
+
* rows of one kind, not stored actors or future timers. It takes no locks; a
|
|
57
|
+
* claim locks only the rows it takes from it. `only` filters inside each
|
|
58
|
+
* bucket's probe, so due rows the caller cannot claim never fill the
|
|
59
|
+
* per-bucket limit ahead of rows it can.
|
|
60
|
+
*/
|
|
61
|
+
export const candidates = ({ sql, kind, now, limit, only = sql.literal(""), range = BUCKETS, }) => sql `SELECT o.routing_key, o.intent_id, o.actor_type, o.command
|
|
62
|
+
FROM generate_series(${range.first}::int, ${range.last}::int) AS b(bucket)
|
|
63
|
+
CROSS JOIN LATERAL (
|
|
64
|
+
SELECT routing_key, intent_id, due_at_ms, actor_type, command FROM actor_outbox
|
|
65
|
+
WHERE actor_outbox.bucket = b.bucket AND actor_outbox.kind = ${kind}
|
|
66
|
+
AND actor_outbox.due_at_ms <= ${now} ${only}
|
|
67
|
+
ORDER BY actor_outbox.due_at_ms LIMIT ${limit}
|
|
68
|
+
) o`;
|
|
69
|
+
/**
|
|
70
|
+
* Claims due intents and due jobs in one autocommit statement. `now` is the
|
|
71
|
+
* outbox clock: the database's statement start time plus the test offset,
|
|
72
|
+
* which is never earlier than a row committed before the claim was sent. A
|
|
73
|
+
* pass therefore costs one round trip whatever it claims.
|
|
74
|
+
*
|
|
75
|
+
* `SKIP LOCKED` passes over rows another runner is claiming, and each claim
|
|
76
|
+
* moves the row's `due_at_ms` past its lease, so no runner scans it again
|
|
77
|
+
* until the lease ends. An intent whose settle dies therefore waits
|
|
78
|
+
* `max(lease, backoff(attempts))` instead of sorting ahead of newer work. A
|
|
79
|
+
* job with no executor on this runner is never claimed here; it stays due for
|
|
80
|
+
* a runner that has one. The two kinds never share a row, so the two updates
|
|
81
|
+
* are disjoint. A kind that claims nothing although its probe found
|
|
82
|
+
* candidates returns one `skipped-*` row with the candidate count, so the
|
|
83
|
+
* relay can widen the next probe past rows other transactions hold locked.
|
|
84
|
+
* Subscription work rides in the same statement, so a pass stays one round
|
|
85
|
+
* trip.
|
|
86
|
+
*/
|
|
87
|
+
const claimDue = ({ sql, now, intents, jobs, subscriptions, range, }) => {
|
|
88
|
+
const parts = [];
|
|
89
|
+
const results = [];
|
|
90
|
+
if (intents !== undefined) {
|
|
91
|
+
const { limit, leaseMs, maxBackoffMs, probe = 2 * limit, cronActors = [] } = intents;
|
|
92
|
+
const local = cronActors.length === 0 ? sql `` : sql ` OR actor_type IN ${sql.in(cronActors)}`;
|
|
93
|
+
parts.push(sql `intent_candidates AS (
|
|
94
|
+
${candidates({
|
|
95
|
+
sql,
|
|
96
|
+
range,
|
|
97
|
+
kind: "intent",
|
|
98
|
+
now,
|
|
99
|
+
limit: probe,
|
|
100
|
+
only: sql `AND (timer_key IS NULL OR left(timer_key, 6) <> ${CRON_PREFIX}${local})`,
|
|
101
|
+
})}
|
|
102
|
+
ORDER BY o.due_at_ms LIMIT ${probe}
|
|
103
|
+
),
|
|
104
|
+
intent_locked AS (
|
|
105
|
+
SELECT o.routing_key, o.intent_id FROM actor_outbox o
|
|
106
|
+
JOIN intent_candidates USING (routing_key, intent_id)
|
|
107
|
+
WHERE o.kind = 'intent' AND o.due_at_ms <= ${now}
|
|
108
|
+
ORDER BY o.due_at_ms LIMIT ${limit}
|
|
109
|
+
FOR UPDATE OF o SKIP LOCKED
|
|
110
|
+
),
|
|
111
|
+
intent_claimed AS (
|
|
112
|
+
UPDATE actor_outbox o SET attempts = o.attempts + 1,
|
|
113
|
+
due_at_ms = ${now} + greatest(${leaseMs}::bigint,
|
|
114
|
+
least(1000 * power(2, least(o.attempts, 31)), ${maxBackoffMs}::bigint))::bigint
|
|
115
|
+
FROM intent_locked c
|
|
116
|
+
WHERE o.routing_key = c.routing_key AND o.intent_id = c.intent_id
|
|
117
|
+
RETURNING ${claimedColumns(sql)},
|
|
118
|
+
(SELECT count(*) FROM intent_candidates)::int AS candidates, false AS exhausted,
|
|
119
|
+
NULL::text AS work
|
|
120
|
+
)`);
|
|
121
|
+
results.push(sql `SELECT * FROM intent_claimed`, skipped(sql, "intent"));
|
|
122
|
+
}
|
|
123
|
+
if (jobs !== undefined && jobs.executors.length > 0) {
|
|
124
|
+
const { permits, leaseMs, executors, probe = 2 * permits } = jobs;
|
|
125
|
+
parts.push(sql `mine (actor_type, command, max_attempts) AS (
|
|
126
|
+
VALUES ${sql.csv(executors.map(({ actor, job, registered }) => sql `(${actor}::text, ${job}::text, ${registered.attempts}::int)`))}
|
|
127
|
+
),
|
|
128
|
+
job_candidates AS (
|
|
129
|
+
${candidates({
|
|
130
|
+
sql,
|
|
131
|
+
range,
|
|
132
|
+
kind: "job",
|
|
133
|
+
now,
|
|
134
|
+
limit: probe,
|
|
135
|
+
only: sql.literal("AND (actor_type, command) IN (SELECT actor_type, command FROM mine)"),
|
|
136
|
+
})}
|
|
137
|
+
ORDER BY o.due_at_ms LIMIT ${probe}
|
|
138
|
+
),
|
|
139
|
+
job_locked AS (
|
|
140
|
+
SELECT o.routing_key, o.intent_id, o.attempts AS previous, m.max_attempts
|
|
141
|
+
FROM actor_outbox o
|
|
142
|
+
JOIN job_candidates USING (routing_key, intent_id)
|
|
143
|
+
JOIN mine m ON m.actor_type = o.actor_type AND m.command = o.command
|
|
144
|
+
WHERE o.kind = 'job' AND o.due_at_ms <= ${now}
|
|
145
|
+
ORDER BY o.due_at_ms LIMIT ${permits}
|
|
146
|
+
FOR UPDATE OF o SKIP LOCKED
|
|
147
|
+
),
|
|
148
|
+
job_claimed AS (
|
|
149
|
+
${claimJobs(sql, now, leaseMs, sql `job_locked`, sql `(SELECT count(*) FROM job_candidates)::int`)}
|
|
150
|
+
)`);
|
|
151
|
+
results.push(sql `SELECT *, NULL::text AS work FROM job_claimed`, skipped(sql, "job"));
|
|
152
|
+
}
|
|
153
|
+
if (subscriptions !== undefined) {
|
|
154
|
+
parts.push(...subscriptions.parts);
|
|
155
|
+
for (const result of subscriptions.results)
|
|
156
|
+
results.push(sql `SELECT ${unclaimedColumns(sql, "work")}, 0, false, claimed.work
|
|
157
|
+
FROM (${result}) AS claimed`);
|
|
158
|
+
}
|
|
159
|
+
if (results.length === 0)
|
|
160
|
+
return Effect.succeed([]);
|
|
161
|
+
return sql `WITH ${sql.csv(parts)}
|
|
162
|
+
${sql.join(" UNION ALL ", false)(results)}`;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Claims the job rows `locked` names. A cancelled row is claimed only to be
|
|
166
|
+
* settled, so it keeps its attempts; any other row starts its next attempt
|
|
167
|
+
* and runs, unless its last attempt already ended without an outcome or an
|
|
168
|
+
* attempt's failure was final, either of which exhausts it. `maybe_applied`
|
|
169
|
+
* then covers every attempt before this one. RETURNING sees the updated row,
|
|
170
|
+
* so exhaustion is judged on the attempts before this claim.
|
|
171
|
+
*/
|
|
172
|
+
const claimJobs = (sql, now, leaseMs, locked, candidateCount) => {
|
|
173
|
+
const attempting = sql `o.cancelled_at_ms IS NULL AND o.attempts < c.max_attempts
|
|
174
|
+
AND NOT o.final_failure`;
|
|
175
|
+
return sql `UPDATE actor_outbox o SET
|
|
176
|
+
due_at_ms = ${now} + ${leaseMs}::bigint,
|
|
177
|
+
attempts = CASE WHEN ${attempting} THEN o.attempts + 1 ELSE o.attempts END,
|
|
178
|
+
ambiguous = CASE WHEN ${attempting} THEN true ELSE o.ambiguous END,
|
|
179
|
+
maybe_applied = CASE WHEN ${attempting}
|
|
180
|
+
THEN o.maybe_applied OR (o.attempts > 0 AND o.ambiguous) ELSE o.maybe_applied END,
|
|
181
|
+
last_error = CASE WHEN ${attempting}
|
|
182
|
+
THEN 'Attempt ' || (o.attempts + 1) || ' ended without reporting an outcome'
|
|
183
|
+
ELSE o.last_error END,
|
|
184
|
+
running = ${attempting},
|
|
185
|
+
waiting = false
|
|
186
|
+
FROM ${locked} c
|
|
187
|
+
WHERE o.routing_key = c.routing_key AND o.intent_id = c.intent_id
|
|
188
|
+
RETURNING ${claimedColumns(sql)}, ${candidateCount} AS candidates,
|
|
189
|
+
o.cancelled_at_ms IS NULL
|
|
190
|
+
AND (c.previous >= c.max_attempts OR o.final_failure) AS exhausted`;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* The actors with due rows of capped jobs this runner executes, oldest first;
|
|
194
|
+
* at most `limit`. Like the uncapped probe, it reads one index range per
|
|
195
|
+
* bucket and takes no locks.
|
|
196
|
+
*/
|
|
197
|
+
const cappedGroups = ({ sql, now, executors, limit, range, }) => sql `WITH mine (actor_type, command) AS (
|
|
198
|
+
VALUES ${sql.csv(executors.map(({ actor, job }) => sql `(${actor}::text, ${job}::text)`))}
|
|
199
|
+
),
|
|
200
|
+
due AS (
|
|
201
|
+
${candidates({
|
|
202
|
+
sql,
|
|
203
|
+
range,
|
|
204
|
+
kind: "job",
|
|
205
|
+
now,
|
|
206
|
+
limit,
|
|
207
|
+
only: sql.literal("AND (actor_type, command) IN (SELECT actor_type, command FROM mine)"),
|
|
208
|
+
})}
|
|
209
|
+
)
|
|
210
|
+
SELECT o.routing_key::text AS routing_key, o.tenant_id, o.actor_type, o.actor_id, o.command,
|
|
211
|
+
(SELECT count(*) FROM due)::int AS due_rows
|
|
212
|
+
FROM actor_outbox o JOIN due USING (routing_key, intent_id)
|
|
213
|
+
GROUP BY o.routing_key, o.tenant_id, o.actor_type, o.actor_id, o.command
|
|
214
|
+
ORDER BY min(o.due_at_ms) LIMIT ${limit}`;
|
|
215
|
+
/**
|
|
216
|
+
* Claims one capped group's jobs in its own transaction, under its actor's
|
|
217
|
+
* generation row lock, so claims on every runner see each other's running rows. It
|
|
218
|
+
* settles cancelled rows whose attempt ended, starts the oldest rows by
|
|
219
|
+
* `(ready_at_ms, intent_id)` while fewer than `cap` attempts hold a live
|
|
220
|
+
* lease, and moves the group's other due rows out of the due range as
|
|
221
|
+
* waiting, so they never fill a probe ahead of other actors' work. A waiting
|
|
222
|
+
* row becomes due again when an attempt of its group settles, or after one
|
|
223
|
+
* lease.
|
|
224
|
+
*
|
|
225
|
+
* An attempt renews, backs off, and settles its own row without the group's
|
|
226
|
+
* lock, so before it counts live leases the claim waits for those writes on
|
|
227
|
+
* the group's running rows. Otherwise `SKIP LOCKED` would pass over an older
|
|
228
|
+
* row whose lost lease is being renewed and start a younger one, and the
|
|
229
|
+
* renewal would then leave two attempts holding leases under a cap of one.
|
|
230
|
+
*/
|
|
231
|
+
export const claimCapped = ({ sql, now, group, cap, maxAttempts, permits, leaseMs, }) => sql.withTransaction(Effect.gen(function* () {
|
|
232
|
+
yield* groupLock({ sql, group });
|
|
233
|
+
const inGroup = groupRow({ sql, group });
|
|
234
|
+
yield* sql `SELECT 1 FROM actor_outbox o WHERE ${inGroup} AND o.running FOR UPDATE OF o`;
|
|
235
|
+
return yield* sql `WITH live AS (
|
|
236
|
+
SELECT count(*)::int AS n FROM actor_outbox o
|
|
237
|
+
WHERE ${inGroup} AND o.running AND o.due_at_ms > ${now}
|
|
238
|
+
),
|
|
239
|
+
settle AS (
|
|
240
|
+
SELECT o.routing_key, o.intent_id, o.attempts AS previous, ${maxAttempts}::int AS max_attempts
|
|
241
|
+
FROM actor_outbox o
|
|
242
|
+
WHERE ${inGroup} AND o.cancelled_at_ms IS NOT NULL AND o.due_at_ms <= ${now}
|
|
243
|
+
ORDER BY o.due_at_ms, o.intent_id
|
|
244
|
+
LIMIT ${permits}::int
|
|
245
|
+
FOR UPDATE OF o SKIP LOCKED
|
|
246
|
+
),
|
|
247
|
+
next AS (
|
|
248
|
+
SELECT o.routing_key, o.intent_id, o.attempts AS previous, ${maxAttempts}::int AS max_attempts
|
|
249
|
+
FROM actor_outbox o
|
|
250
|
+
WHERE ${inGroup} AND o.cancelled_at_ms IS NULL
|
|
251
|
+
AND (o.due_at_ms <= ${now} OR (o.waiting AND NOT o.running))
|
|
252
|
+
ORDER BY o.ready_at_ms, o.intent_id
|
|
253
|
+
LIMIT greatest(0, least(${cap}::int - (SELECT n FROM live),
|
|
254
|
+
${permits}::int - (SELECT count(*)::int FROM settle)))
|
|
255
|
+
FOR UPDATE OF o SKIP LOCKED
|
|
256
|
+
),
|
|
257
|
+
locked AS (SELECT * FROM settle UNION ALL SELECT * FROM next),
|
|
258
|
+
claimed AS (${claimJobs(sql, now, leaseMs, sql `locked`, sql `0`)}),
|
|
259
|
+
deferred AS (
|
|
260
|
+
UPDATE actor_outbox o SET waiting = true, running = false,
|
|
261
|
+
due_at_ms = ${now} + ${leaseMs}::bigint
|
|
262
|
+
WHERE ${inGroup} AND o.cancelled_at_ms IS NULL AND o.due_at_ms <= ${now}
|
|
263
|
+
AND o.intent_id NOT IN (SELECT intent_id FROM next)
|
|
264
|
+
AND o.intent_id IN (
|
|
265
|
+
SELECT o.intent_id FROM actor_outbox o
|
|
266
|
+
WHERE ${inGroup} AND o.cancelled_at_ms IS NULL AND o.due_at_ms <= ${now}
|
|
267
|
+
FOR UPDATE OF o SKIP LOCKED
|
|
268
|
+
)
|
|
269
|
+
RETURNING 1
|
|
270
|
+
)
|
|
271
|
+
SELECT * FROM claimed`;
|
|
272
|
+
}));
|
|
273
|
+
/** One row reporting `kind`'s candidates when its claim took none of them. */
|
|
274
|
+
const skipped = (sql, kind) => {
|
|
275
|
+
const claimed = sql.literal(`${kind}_claimed`);
|
|
276
|
+
const found = sql.literal(`${kind}_candidates`);
|
|
277
|
+
return sql `SELECT ${unclaimedColumns(sql, `skipped-${kind}`)},
|
|
278
|
+
(SELECT count(*) FROM ${found})::int, false, NULL::text
|
|
279
|
+
WHERE NOT EXISTS (SELECT 1 FROM ${claimed}) AND EXISTS (SELECT 1 FROM ${found})`;
|
|
280
|
+
};
|
|
281
|
+
/** The outbox clock inside a statement: its start time on the database plus the test offset. */
|
|
282
|
+
export const outboxNow = ({ sql, offsetMillis, }) => sql `(floor(extract(epoch FROM statement_timestamp()) * 1000)::bigint + ${offsetMillis}::bigint)`;
|
|
283
|
+
/** The intent half of `claimDue` at a fixed `now`, as a statement to inspect. */
|
|
284
|
+
export const claimIntents = ({ sql, now, range, ...intents }) => claimDue({
|
|
285
|
+
sql,
|
|
286
|
+
range,
|
|
287
|
+
now: sql `${now}::bigint`,
|
|
288
|
+
intents,
|
|
289
|
+
});
|
|
290
|
+
/** The widest probe, as a multiple of twice the free capacity. */
|
|
291
|
+
const MAX_WIDEN = 64;
|
|
292
|
+
/** Logs a non-interrupt failure of the work under `message` and completes with `void`; an interruption stays one. */
|
|
293
|
+
const logFailure = (message) => (self) => Effect.catchCause(self, (cause) => Cause.hasInterruptsOnly(cause) ? Effect.interrupt : Effect.logError(message, cause));
|
|
294
|
+
/**
|
|
295
|
+
* One family of claimed work on this runner: its running fibers, how many may
|
|
296
|
+
* run at once, and what its last claim saw. A claim takes at most `free` rows
|
|
297
|
+
* of the family, so no claimed row waits locally while its lease runs down.
|
|
298
|
+
* When the last claim saw more due candidates than it took, each finishing
|
|
299
|
+
* fiber wakes the relay to claim again instead of waiting for the poll; when
|
|
300
|
+
* it also left room, which happens when other transactions hold the earliest
|
|
301
|
+
* rows locked, the next probe doubles, up to `MAX_WIDEN`.
|
|
302
|
+
*/
|
|
303
|
+
const lane = Effect.fnUntraced(function* (capacity, wake) {
|
|
304
|
+
const fibers = yield* FiberSet.make();
|
|
305
|
+
let more = false;
|
|
306
|
+
let widen = 1;
|
|
307
|
+
return {
|
|
308
|
+
fibers,
|
|
309
|
+
/** Rows a claim may take now. */
|
|
310
|
+
free: Effect.map(FiberSet.size(fibers), (running) => capacity - running),
|
|
311
|
+
/** The probe size for `free` rows. */
|
|
312
|
+
probe: (free) => 2 * free * widen,
|
|
313
|
+
get more() {
|
|
314
|
+
return more;
|
|
315
|
+
},
|
|
316
|
+
/** Records what a claim of up to `free` rows took and how many due candidates it found. */
|
|
317
|
+
claimed: (free, taken, found) => {
|
|
318
|
+
more = taken > 0 && found > taken;
|
|
319
|
+
widen = taken < free && found > taken ? Math.min(widen * 2, MAX_WIDEN) : 1;
|
|
320
|
+
},
|
|
321
|
+
/** Records whether a claim may have left due rows of the family behind. */
|
|
322
|
+
backlog: (left) => {
|
|
323
|
+
more = left;
|
|
324
|
+
},
|
|
325
|
+
/** Runs `work` on the family; its end wakes the relay when rows were left due. */
|
|
326
|
+
start: (work) => FiberSet.run(fibers, work.pipe(Effect.ensuring(Effect.suspend(() => (more ? wake : Effect.void))))).pipe(Effect.asVoid),
|
|
327
|
+
};
|
|
328
|
+
});
|
|
329
|
+
/**
|
|
330
|
+
* The outbox relay of one runner among any number sharing the database. Each
|
|
331
|
+
* pass claims, for every family of work (intents, job attempts, and feed,
|
|
332
|
+
* control, and subscription work), only as many rows as the family has free
|
|
333
|
+
* fibers, then starts each one at once. A pass runs uninterruptibly under a
|
|
334
|
+
* lock, so every row a claim returns reaches a fiber that can release it.
|
|
335
|
+
* When both capped and uncapped jobs are due, passes alternate which claims
|
|
336
|
+
* first, so a steady stream of either can't take every permit.
|
|
337
|
+
*
|
|
338
|
+
* An intent is delivered as a direct command whose command id is the intent
|
|
339
|
+
* id, and its row is deleted only after the receiver's receipt has committed.
|
|
340
|
+
* A crash leaves the claim in place until its lease ends; any runner then
|
|
341
|
+
* redelivers, and the receipt deduplicates. A row that cannot form a request,
|
|
342
|
+
* a delivery that fails, and a defect outcome back off and retry with no retry
|
|
343
|
+
* limit; the logged warning and `attempts` are the operator signal. A declared
|
|
344
|
+
* failure is a committed receipt, so only a missing receipt retries. An
|
|
345
|
+
* interrupted delivery makes its row due at once.
|
|
346
|
+
*
|
|
347
|
+
* A job attempt runs on its own family and settles its row itself; see
|
|
348
|
+
* `jobAttempts`. A running attempt is registered before the pass releases its
|
|
349
|
+
* lock and until its outcome is written, so every clock jump after the claim
|
|
350
|
+
* moves its lease; a local cancellation signals running attempts directly.
|
|
351
|
+
*
|
|
352
|
+
* Subscription work runs in its own families, so a subscription backlog never
|
|
353
|
+
* delays intents, timers, or jobs; expansion work starts the rows it leased
|
|
354
|
+
* without a claim pass; `schedules` names the cron actors whose ticks this
|
|
355
|
+
* runner claims. The returned handle: `run` loops passes on a jittered poll
|
|
356
|
+
* and `wake`; `drain` waits for in-flight work, which may stage more, then
|
|
357
|
+
* claims again until a claim finds nothing while nothing was running (work
|
|
358
|
+
* runs only on fibers a pass starts, so none running means nothing can stage
|
|
359
|
+
* rows after that claim read), failing if deliveries keep staging due work,
|
|
360
|
+
* and counting toward that limit only rounds that drained every due row;
|
|
361
|
+
* `stop` ends claims and interrupts deliveries at once, since a delivery only
|
|
362
|
+
* waits on a turn its receiver's owner finishes or rolls back on its own, and
|
|
363
|
+
* the receiver's receipt answers a redelivery of work that did commit;
|
|
364
|
+
* `interruptAttempts` interrupts running attempts and returns how many,
|
|
365
|
+
* leaving their claims and `ambiguous` marks because the provider may have
|
|
366
|
+
* applied the call, so another runner takes the job over once the lease ends;
|
|
367
|
+
* `attemptsIdle` waits for attempts to end; `extendLeases` moves running
|
|
368
|
+
* attempts' leases with a jump of the outbox clock, as the renewals during
|
|
369
|
+
* that time would have, holding the pass lock so no claim reads the clock
|
|
370
|
+
* between the moved leases and the jump; `cancelled` makes running attempts
|
|
371
|
+
* check for cancellation now. Closing the scope stops further claims, after
|
|
372
|
+
* taking the lock so a pass in progress hands its rows to fibers first and
|
|
373
|
+
* they are interrupted and released.
|
|
374
|
+
*/
|
|
375
|
+
export const outboxRelay = Effect.fnUntraced(function* (deliver, executors, settings, subscriptions, schedules = () => new Map()) {
|
|
376
|
+
const sql = yield* SqlClient.SqlClient;
|
|
377
|
+
const shards = yield* shardClients;
|
|
378
|
+
let firstShard = 0;
|
|
379
|
+
const services = yield* Effect.context();
|
|
380
|
+
const lock = Semaphore.makeUnsafe(1);
|
|
381
|
+
const signals = yield* Queue.sliding(1);
|
|
382
|
+
const wakeRelay = Queue.offer(signals, undefined).pipe(Effect.asVoid);
|
|
383
|
+
const workConcurrency = subscriptions?.concurrency ?? 0;
|
|
384
|
+
const lanes = {
|
|
385
|
+
intents: yield* lane(settings.deliveryConcurrency, wakeRelay),
|
|
386
|
+
jobs: yield* lane(settings.executorConcurrency, wakeRelay),
|
|
387
|
+
feed: yield* lane(workConcurrency, wakeRelay),
|
|
388
|
+
control: yield* lane(workConcurrency, wakeRelay),
|
|
389
|
+
subscription: yield* lane(workConcurrency, wakeRelay),
|
|
390
|
+
};
|
|
391
|
+
const hooks = yield* TurnHooks;
|
|
392
|
+
let cancelChecks = Deferred.makeUnsafe();
|
|
393
|
+
const renewEveryMs = Math.min(settings.cancelCheckMs ?? settings.executorLeaseMs / 3, settings.executorLeaseMs / 3);
|
|
394
|
+
const attempt = yield* jobAttempts({
|
|
395
|
+
progress: yield* progressPool(),
|
|
396
|
+
leaseMs: settings.executorLeaseMs,
|
|
397
|
+
renewEveryMs,
|
|
398
|
+
wake: wakeRelay,
|
|
399
|
+
});
|
|
400
|
+
const ticks = cronTicks({
|
|
401
|
+
sql,
|
|
402
|
+
crypto: yield* Crypto.Crypto,
|
|
403
|
+
schedules,
|
|
404
|
+
retryWindowMs: settings.retryWindowMs,
|
|
405
|
+
});
|
|
406
|
+
const startWork = (work) => subscriptions === undefined
|
|
407
|
+
? Effect.void
|
|
408
|
+
: lanes[work.kind].start(subscriptions.run(work, handoff).pipe(logFailure("Subscription relay work failed")));
|
|
409
|
+
const handoff = { free: lanes.subscription.free, start: startWork };
|
|
410
|
+
let cappedTurn = false;
|
|
411
|
+
let stopping = false;
|
|
412
|
+
const running = new Map();
|
|
413
|
+
const backoffMs = (attempts) => Math.min(1000 * 2 ** Math.max(attempts - 1, 0), settings.maxBackoffMs);
|
|
414
|
+
const requestOf = (row) => Schema.decodeEffect(CallerJson)(row.caller).pipe(Effect.flatMap((caller) => Schema.decodeEffect(Request)({
|
|
415
|
+
ref: { tenant: row.tenant_id, actor: row.target_type, id: row.target_id },
|
|
416
|
+
caller,
|
|
417
|
+
command: row.command,
|
|
418
|
+
commandId: row.intent_id,
|
|
419
|
+
payload: row.payload,
|
|
420
|
+
intent: { tenant: row.tenant_id, actor: row.actor_type, id: row.actor_id },
|
|
421
|
+
})));
|
|
422
|
+
const deliverIntent = Effect.fnUntraced(function* (row) {
|
|
423
|
+
const routingKey = BigInt(row.routing_key);
|
|
424
|
+
const claim = sql `routing_key = ${routingKey} AND intent_id = ${row.intent_id}
|
|
425
|
+
AND kind = 'intent' AND due_at_ms = ${BigInt(row.claimed_until)}`;
|
|
426
|
+
const retryLater = (reason, cause) => Effect.gen(function* () {
|
|
427
|
+
yield* Effect.logWarning("Outbox delivery failed; retrying with backoff", cause).pipe(Effect.annotateLogs({
|
|
428
|
+
actor: row.target_type,
|
|
429
|
+
id: row.target_id,
|
|
430
|
+
tenant: row.tenant_id,
|
|
431
|
+
command: row.command,
|
|
432
|
+
commandId: row.intent_id,
|
|
433
|
+
reason,
|
|
434
|
+
}));
|
|
435
|
+
yield* sql `UPDATE actor_outbox SET due_at_ms = ${(yield* databaseTime) + backoffMs(row.attempts)}
|
|
436
|
+
WHERE ${claim}`;
|
|
437
|
+
yield* tally(Metrics.relayRetried, { kind: "intent" }, 1);
|
|
438
|
+
});
|
|
439
|
+
return yield* Effect.gen(function* () {
|
|
440
|
+
const tick = ticks.isTick(row);
|
|
441
|
+
const route = tick ? yield* ticks.settleUnfired(row, claim, backoffMs(row.attempts)) : row;
|
|
442
|
+
if (route === undefined)
|
|
443
|
+
return;
|
|
444
|
+
const decoded = yield* requestOf({ ...row, ...route }).pipe(Effect.result);
|
|
445
|
+
if (Result.isFailure(decoded))
|
|
446
|
+
return yield* retryLater("UnreadableRow", decoded.failure);
|
|
447
|
+
const request = decoded.success;
|
|
448
|
+
yield* hooks.at("afterClaim", request);
|
|
449
|
+
const delivered = yield* deliver(request).pipe(Effect.result);
|
|
450
|
+
if (Result.isFailure(delivered))
|
|
451
|
+
return yield* retryLater(delivered.failure.reason._tag, delivered.failure);
|
|
452
|
+
if (Outcome.guards.Defect(delivered.success))
|
|
453
|
+
return yield* retryLater("Defect", delivered.success.cause);
|
|
454
|
+
yield* hooks.at("beforeOutboxDelete", request);
|
|
455
|
+
if (tick)
|
|
456
|
+
yield* ticks.settleFired(row, claim);
|
|
457
|
+
else
|
|
458
|
+
yield* sql `DELETE FROM actor_outbox WHERE ${claim}`;
|
|
459
|
+
yield* tally(Metrics.relayDelivered, { kind: "intent" }, 1);
|
|
460
|
+
}).pipe(Effect.withSpan(SpanNames.relayIntent, {
|
|
461
|
+
kind: "producer",
|
|
462
|
+
attributes: {
|
|
463
|
+
"actor.type": row.target_type,
|
|
464
|
+
"actor.tenant": row.tenant_id,
|
|
465
|
+
"actor.id": row.target_id,
|
|
466
|
+
"command.name": row.command,
|
|
467
|
+
"command.id": row.intent_id,
|
|
468
|
+
"relay.attempt": row.attempts,
|
|
469
|
+
"relay.timer": row.timer_key !== null,
|
|
470
|
+
},
|
|
471
|
+
}, { captureStackTrace: false }), Effect.onInterrupt(() => databaseTime.pipe(Effect.flatMap((at) => sql `UPDATE actor_outbox SET due_at_ms = ${at} WHERE ${claim}`), Effect.ignore)));
|
|
472
|
+
});
|
|
473
|
+
const cancelled = Effect.sync(() => {
|
|
474
|
+
const previous = cancelChecks;
|
|
475
|
+
cancelChecks = Deferred.makeUnsafe();
|
|
476
|
+
Deferred.doneUnsafe(previous, Exit.void);
|
|
477
|
+
});
|
|
478
|
+
const all = Object.values(lanes);
|
|
479
|
+
const interruptible = all.filter((family) => family !== lanes.jobs);
|
|
480
|
+
const inFlight = Effect.map(Effect.forEach(all, (family) => FiberSet.size(family.fibers)), (sizes) => sizes.reduce((sum, size) => sum + size, 0));
|
|
481
|
+
const pass = lock
|
|
482
|
+
.withPermit(Effect.uninterruptible(Effect.gen(function* () {
|
|
483
|
+
if (stopping)
|
|
484
|
+
return { claimed: 0, backlog: false, quiet: true };
|
|
485
|
+
const quiet = (yield* inFlight) === 0;
|
|
486
|
+
const slots = Math.min(yield* lanes.intents.free, settings.passLimit);
|
|
487
|
+
const local = executors();
|
|
488
|
+
const uncapped = local.filter(({ registered }) => registered.perActor === undefined);
|
|
489
|
+
const capped = local.filter(({ registered }) => registered.perActor !== undefined);
|
|
490
|
+
const permits = yield* lanes.jobs.free;
|
|
491
|
+
const claimedAt = yield* Clock.currentTimeNanos;
|
|
492
|
+
const claimSignal = cancelChecks;
|
|
493
|
+
const clock = yield* FrameworkClock;
|
|
494
|
+
const workSlots = subscriptions === undefined
|
|
495
|
+
? undefined
|
|
496
|
+
: {
|
|
497
|
+
feed: yield* lanes.feed.free,
|
|
498
|
+
control: yield* lanes.control.free,
|
|
499
|
+
subscription: yield* lanes.subscription.free,
|
|
500
|
+
};
|
|
501
|
+
const now = outboxNow({ sql, offsetMillis: clock.offsetMillis() });
|
|
502
|
+
const ordered = [...shards.slice(firstShard), ...shards.slice(0, firstShard)];
|
|
503
|
+
firstShard = shards.length === 0 ? 0 : (firstShard + 1) % shards.length;
|
|
504
|
+
const claimGroups = (limit) => Effect.gen(function* () {
|
|
505
|
+
const claimed = [];
|
|
506
|
+
let backlog = false;
|
|
507
|
+
for (const { sql, range } of ordered) {
|
|
508
|
+
const remaining = limit - claimed.length;
|
|
509
|
+
if (remaining <= 0) {
|
|
510
|
+
backlog = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
const groups = yield* cappedGroups({
|
|
514
|
+
sql,
|
|
515
|
+
range,
|
|
516
|
+
now,
|
|
517
|
+
executors: capped,
|
|
518
|
+
limit: remaining,
|
|
519
|
+
});
|
|
520
|
+
backlog ||= groups.length === remaining || (groups[0]?.due_rows ?? 0) >= remaining;
|
|
521
|
+
for (const group of groups) {
|
|
522
|
+
const left = limit - claimed.length;
|
|
523
|
+
if (left <= 0)
|
|
524
|
+
break;
|
|
525
|
+
const { registered } = capped.find(({ actor, job }) => actor === group.actor_type && job === group.command);
|
|
526
|
+
claimed.push(...(yield* claimCapped({
|
|
527
|
+
sql,
|
|
528
|
+
now,
|
|
529
|
+
group,
|
|
530
|
+
cap: registered.perActor,
|
|
531
|
+
maxAttempts: registered.attempts,
|
|
532
|
+
permits: left,
|
|
533
|
+
leaseMs: settings.executorLeaseMs,
|
|
534
|
+
})));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return { claimed, backlog };
|
|
538
|
+
});
|
|
539
|
+
const cappedFirst = capped.length > 0 && uncapped.length > 0 && cappedTurn;
|
|
540
|
+
if (capped.length > 0 && uncapped.length > 0)
|
|
541
|
+
cappedTurn = !cappedTurn;
|
|
542
|
+
const early = cappedFirst && permits > 0
|
|
543
|
+
? yield* claimGroups(permits)
|
|
544
|
+
: { claimed: [], backlog: false };
|
|
545
|
+
const uncappedPermits = permits - early.claimed.length;
|
|
546
|
+
const rows = [];
|
|
547
|
+
const work = [];
|
|
548
|
+
let intentSlots = slots;
|
|
549
|
+
let jobSlots = uncappedPermits;
|
|
550
|
+
const remainingWork = workSlots === undefined ? undefined : { ...workSlots };
|
|
551
|
+
const found = { intent: 0, job: 0 };
|
|
552
|
+
for (const { sql, range } of ordered) {
|
|
553
|
+
const claimed = yield* claimDue({
|
|
554
|
+
sql,
|
|
555
|
+
range,
|
|
556
|
+
now,
|
|
557
|
+
intents: intentSlots > 0
|
|
558
|
+
? {
|
|
559
|
+
limit: intentSlots,
|
|
560
|
+
leaseMs: settings.claimLeaseMs(),
|
|
561
|
+
maxBackoffMs: settings.maxBackoffMs,
|
|
562
|
+
probe: lanes.intents.probe(intentSlots),
|
|
563
|
+
cronActors: [...schedules().keys()],
|
|
564
|
+
}
|
|
565
|
+
: undefined,
|
|
566
|
+
jobs: jobSlots > 0 && uncapped.length > 0
|
|
567
|
+
? {
|
|
568
|
+
permits: jobSlots,
|
|
569
|
+
leaseMs: settings.executorLeaseMs,
|
|
570
|
+
executors: uncapped,
|
|
571
|
+
probe: lanes.jobs.probe(jobSlots),
|
|
572
|
+
}
|
|
573
|
+
: undefined,
|
|
574
|
+
subscriptions: remainingWork === undefined
|
|
575
|
+
? undefined
|
|
576
|
+
: subscriptions.claim(remainingWork, range),
|
|
577
|
+
});
|
|
578
|
+
for (const kind of ["intent", "job"]) {
|
|
579
|
+
found[kind] +=
|
|
580
|
+
claimed.find((row) => row.kind === kind || row.kind === `skipped-${kind}`)
|
|
581
|
+
?.candidates ?? 0;
|
|
582
|
+
}
|
|
583
|
+
intentSlots -= claimed.filter((row) => row.kind === "intent").length;
|
|
584
|
+
jobSlots -= claimed.filter((row) => row.kind === "job").length;
|
|
585
|
+
rows.push(...claimed);
|
|
586
|
+
if (subscriptions !== undefined && remainingWork !== undefined) {
|
|
587
|
+
for (const row of claimed.filter((row) => row.kind === "work")) {
|
|
588
|
+
const item = yield* subscriptions.decode(row.work);
|
|
589
|
+
remainingWork[item.kind]--;
|
|
590
|
+
work.push(item);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const intents = rows.filter((row) => row.kind === "intent");
|
|
595
|
+
const uncappedRows = rows.filter((row) => row.kind === "job");
|
|
596
|
+
const late = !cappedFirst && capped.length > 0 && uncappedPermits - uncappedRows.length > 0
|
|
597
|
+
? yield* claimGroups(uncappedPermits - uncappedRows.length)
|
|
598
|
+
: { claimed: [], backlog: false };
|
|
599
|
+
const jobs = [...early.claimed, ...uncappedRows, ...late.claimed];
|
|
600
|
+
if (slots > 0)
|
|
601
|
+
lanes.intents.claimed(slots, intents.length, found.intent);
|
|
602
|
+
if (permits > 0 && local.length > 0) {
|
|
603
|
+
lanes.jobs.claimed(uncappedPermits, uncappedRows.length, found.job);
|
|
604
|
+
if (early.backlog || late.backlog)
|
|
605
|
+
lanes.jobs.backlog(true);
|
|
606
|
+
}
|
|
607
|
+
let claimedWork = 0;
|
|
608
|
+
if (subscriptions !== undefined && workSlots !== undefined) {
|
|
609
|
+
claimedWork = work.length;
|
|
610
|
+
for (const kind of ["feed", "control", "subscription"]) {
|
|
611
|
+
const taken = work.filter((item) => item.kind === kind).length;
|
|
612
|
+
lanes[kind].backlog(workSlots[kind] > 0 && taken >= workSlots[kind]);
|
|
613
|
+
}
|
|
614
|
+
for (const item of work)
|
|
615
|
+
yield* startWork(item);
|
|
616
|
+
}
|
|
617
|
+
for (const row of intents)
|
|
618
|
+
yield* lanes.intents.start(deliverIntent(row).pipe(logFailure("Outbox relay crashed settling a row")));
|
|
619
|
+
for (const row of jobs) {
|
|
620
|
+
const registered = local.find(({ actor, job }) => actor === row.actor_type && job === row.command).registered;
|
|
621
|
+
const lease = { until: Number(row.claimed_until) };
|
|
622
|
+
running.set(row.intent_id, {
|
|
623
|
+
routingKey: BigInt(row.routing_key),
|
|
624
|
+
attempt: row.attempts,
|
|
625
|
+
lease,
|
|
626
|
+
});
|
|
627
|
+
yield* lanes.jobs.start(attempt(row, registered, {
|
|
628
|
+
at: claimedAt,
|
|
629
|
+
signal: claimSignal,
|
|
630
|
+
next: () => cancelChecks,
|
|
631
|
+
lease,
|
|
632
|
+
}).pipe(Effect.ensuring(Effect.sync(() => running.delete(row.intent_id))), logFailure("Job attempt crashed before it settled")));
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
claimed: intents.length + jobs.length + claimedWork,
|
|
636
|
+
backlog: all.some((family) => family.more),
|
|
637
|
+
quiet,
|
|
638
|
+
};
|
|
639
|
+
})))
|
|
640
|
+
.pipe(Effect.provideContext(services));
|
|
641
|
+
const run = Effect.gen(function* () {
|
|
642
|
+
while (true) {
|
|
643
|
+
const jitter = 0.9 + 0.2 * (yield* Random.next);
|
|
644
|
+
yield* Queue.take(signals).pipe(Effect.timeoutOption(settings.pollMs * jitter));
|
|
645
|
+
yield* pass.pipe(logFailure("Outbox relay pass failed"));
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
const idle = Effect.forEach(all, (family) => FiberSet.awaitEmpty(family.fibers), {
|
|
649
|
+
discard: true,
|
|
650
|
+
});
|
|
651
|
+
const drain = Effect.gen(function* () {
|
|
652
|
+
for (let rounds = 0; rounds < DRAIN_ROUNDS;) {
|
|
653
|
+
yield* idle;
|
|
654
|
+
const { claimed, backlog, quiet } = yield* pass;
|
|
655
|
+
if (claimed === 0 && quiet)
|
|
656
|
+
return;
|
|
657
|
+
if (claimed > 0 && !backlog)
|
|
658
|
+
rounds++;
|
|
659
|
+
}
|
|
660
|
+
return yield* Effect.die(new Error("Outbox did not settle; intents keep producing due work"));
|
|
661
|
+
}).pipe(Effect.orDie);
|
|
662
|
+
const halt = lock.withPermit(Effect.sync(() => {
|
|
663
|
+
stopping = true;
|
|
664
|
+
}));
|
|
665
|
+
yield* Effect.addFinalizer(() => halt);
|
|
666
|
+
const extendLeases = (millis, jump) => lock
|
|
667
|
+
.withPermit(Effect.forEach([...running], ([intentId, { routingKey, attempt: current, lease }]) => sql `UPDATE actor_outbox SET due_at_ms = due_at_ms + ${millis}
|
|
668
|
+
WHERE routing_key = ${routingKey} AND intent_id = ${intentId}
|
|
669
|
+
AND kind = 'job' AND attempts = ${current}`.pipe(Effect.tap(Effect.sync(() => {
|
|
670
|
+
lease.until += millis;
|
|
671
|
+
}))), { discard: true }).pipe(Effect.andThen(jump)))
|
|
672
|
+
.pipe(Effect.orDie);
|
|
673
|
+
const stop = Effect.andThen(halt, Effect.forEach(interruptible, (family) => FiberSet.clear(family.fibers), { discard: true }));
|
|
674
|
+
const interruptAttempts = Effect.flatMap(FiberSet.size(lanes.jobs.fibers), (count) => FiberSet.clear(lanes.jobs.fibers).pipe(Effect.as(count)));
|
|
675
|
+
return {
|
|
676
|
+
run,
|
|
677
|
+
drain,
|
|
678
|
+
stop,
|
|
679
|
+
attemptsIdle: FiberSet.awaitEmpty(lanes.jobs.fibers),
|
|
680
|
+
interruptAttempts,
|
|
681
|
+
extendLeases,
|
|
682
|
+
wake: wakeRelay,
|
|
683
|
+
cancelled,
|
|
684
|
+
};
|
|
685
|
+
});
|