@rikalabs/akter 0.1.0-alpha.0 → 0.1.0-alpha.3
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 +46 -3
- package/README.md +2 -2
- package/dist/actor/definition.d.ts +13 -9
- package/dist/actor/descriptor.d.ts +2 -2
- package/dist/actor/descriptor.js +4 -4
- package/dist/client/calls.js +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/errors/actor.d.ts +12 -42
- package/dist/errors/actor.js +13 -42
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/members/payload.d.ts +1 -1
- package/dist/protocol/inspection.d.ts +221 -1
- package/dist/protocol/inspection.js +151 -2
- package/dist/protocol/wire.d.ts +6 -49
- package/dist/protocol/wire.js +1 -10
- package/dist/runtime/actors.d.ts +3 -0
- package/dist/runtime/adoption/backfill.js +1 -1
- package/dist/runtime/adoption/enforce.js +2 -2
- package/dist/runtime/adoption/observe.js +1 -1
- package/dist/runtime/adoption/plan.d.ts +1 -1
- package/dist/runtime/adoption/startup.js +1 -1
- package/dist/runtime/adoption/status.d.ts +1 -1
- package/dist/runtime/app.d.ts +65 -0
- package/dist/runtime/app.js +38 -0
- package/dist/runtime/connections/holder.d.ts +11 -1
- package/dist/runtime/connections/holder.js +8 -4
- package/dist/runtime/connections/streams.d.ts +4 -4
- package/dist/runtime/content/store.js +15 -11
- package/dist/runtime/database/bounded.d.ts +4 -18
- package/dist/runtime/database/bounded.js +36 -22
- package/dist/runtime/database/coordination.d.ts +12 -3
- package/dist/runtime/database/coordination.js +29 -36
- package/dist/runtime/database/migrations.d.ts +61 -29
- package/dist/runtime/database/migrations.js +375 -139
- package/dist/runtime/database/replica.d.ts +4 -1
- package/dist/runtime/database/replica.js +4 -1
- package/dist/runtime/database/schema.d.ts +13 -0
- package/dist/runtime/database/schema.js +13 -0
- package/dist/runtime/database/shards.d.ts +31 -13
- package/dist/runtime/database/shards.js +38 -26
- package/dist/runtime/entity/register.d.ts +8 -1
- package/dist/runtime/entity/register.js +58 -7
- package/dist/runtime/fleet/checks.js +4 -4
- package/dist/runtime/fleet/maintainer.d.ts +1 -1
- package/dist/runtime/fleet/maintainer.js +1 -1
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/inspector/layer.d.ts +20 -0
- package/dist/runtime/inspector/layer.js +224 -5
- package/dist/runtime/inspector/queries.d.ts +54 -8
- package/dist/runtime/inspector/queries.js +84 -15
- package/dist/runtime/layer.d.ts +48 -39
- package/dist/runtime/layer.js +124 -56
- package/dist/runtime/operators/audit.d.ts +3 -0
- package/dist/runtime/operators/audit.js +6 -2
- package/dist/runtime/operators/repair.js +30 -10
- package/dist/runtime/operators/seed.js +4 -2
- package/dist/runtime/payloads/versions.d.ts +4 -4
- package/dist/runtime/payloads/versions.js +24 -15
- package/dist/runtime/queries.js +4 -2
- package/dist/runtime/registration.d.ts +5 -2
- package/dist/runtime/registration.js +3 -3
- package/dist/runtime/runner.d.ts +12 -0
- package/dist/runtime/runner.js +12 -0
- package/dist/runtime/storage/codec.d.ts +19 -7
- package/dist/runtime/storage/codec.js +23 -7
- package/dist/runtime/storage/placements.d.ts +6 -1
- package/dist/runtime/storage/placements.js +98 -6
- package/dist/runtime/storage/retention.d.ts +6 -0
- package/dist/runtime/storage/retention.js +25 -13
- package/dist/runtime/subscriptions/relay.js +47 -44
- package/dist/runtime/subscriptions/storage.d.ts +3 -2
- package/dist/runtime/subscriptions/storage.js +9 -6
- package/dist/runtime/telemetry/live.d.ts +255 -0
- package/dist/runtime/telemetry/live.js +540 -0
- package/dist/runtime/telemetry/metrics.d.ts +2 -0
- package/dist/runtime/telemetry/metrics.js +4 -1
- package/dist/runtime/telemetry/sampler.js +33 -13
- package/dist/runtime/telemetry/spans.d.ts +1 -1
- package/dist/runtime/telemetry/spans.js +1 -1
- package/dist/runtime/topology/locks.d.ts +6 -3
- package/dist/runtime/topology/locks.js +27 -4
- package/dist/runtime/turn/execute.d.ts +2 -0
- package/dist/runtime/turn/execute.js +5 -2
- package/dist/runtime/turn/pipeline.d.ts +5 -12
- package/dist/runtime/turn/pipeline.js +11 -11
- package/dist/runtime/turn/relay.d.ts +13 -3
- package/dist/runtime/turn/relay.js +27 -14
- package/dist/runtime/turn/rows.js +4 -4
- package/dist/runtime/workflows/compatibility.d.ts +1 -1
- package/dist/runtime/workflows/compatibility.js +85 -31
- package/dist/runtime/workflows/engine.js +20 -17
- package/dist/serve/layer.js +3 -2
- package/dist/tables/owned.d.ts +1 -1
- package/dist/tables/owned.js +1 -1
- package/dist/testing/actor-test.d.ts +2 -24
- package/dist/testing/actor-test.js +1 -27
- package/dist/testing/index.d.ts +0 -12
- package/dist/testing/index.js +0 -6
- package/package.json +1 -1
- package/dist/runtime/database/neki/migrations.d.ts +0 -31
- package/dist/runtime/database/neki/migrations.js +0 -276
- package/dist/runtime/database/neki/session.d.ts +0 -23
- package/dist/runtime/database/neki/session.js +0 -31
- package/dist/testing/cluster.d.ts +0 -93
- package/dist/testing/cluster.js +0 -296
- package/dist/testing/conformance/access.d.ts +0 -5
- package/dist/testing/conformance/access.js +0 -531
- package/dist/testing/conformance/admission.d.ts +0 -11
- package/dist/testing/conformance/admission.js +0 -182
- package/dist/testing/conformance/adoption.d.ts +0 -2
- package/dist/testing/conformance/adoption.js +0 -1019
- package/dist/testing/conformance/assertions.d.ts +0 -202
- package/dist/testing/conformance/assertions.js +0 -849
- package/dist/testing/conformance/batches.d.ts +0 -41
- package/dist/testing/conformance/batches.js +0 -388
- package/dist/testing/conformance/blobs.d.ts +0 -14
- package/dist/testing/conformance/blobs.js +0 -405
- package/dist/testing/conformance/capacity.d.ts +0 -8
- package/dist/testing/conformance/capacity.js +0 -433
- package/dist/testing/conformance/client.d.ts +0 -5
- package/dist/testing/conformance/client.js +0 -719
- package/dist/testing/conformance/cold-serve.d.ts +0 -16
- package/dist/testing/conformance/cold-serve.js +0 -254
- package/dist/testing/conformance/connections/actors.d.ts +0 -1143
- package/dist/testing/conformance/connections/actors.js +0 -180
- package/dist/testing/conformance/connections/harness.d.ts +0 -1754
- package/dist/testing/conformance/connections/harness.js +0 -142
- package/dist/testing/conformance/connections/holders.d.ts +0 -4
- package/dist/testing/conformance/connections/holders.js +0 -165
- package/dist/testing/conformance/connections/lifecycle.d.ts +0 -4
- package/dist/testing/conformance/connections/lifecycle.js +0 -113
- package/dist/testing/conformance/connections/limits.d.ts +0 -4
- package/dist/testing/conformance/connections/limits.js +0 -127
- package/dist/testing/conformance/connections/reauthorization.d.ts +0 -4
- package/dist/testing/conformance/connections/reauthorization.js +0 -170
- package/dist/testing/conformance/connections/resync.d.ts +0 -4
- package/dist/testing/conformance/connections/resync.js +0 -360
- package/dist/testing/conformance/connections/takeover.d.ts +0 -4
- package/dist/testing/conformance/connections/takeover.js +0 -287
- package/dist/testing/conformance/connections.d.ts +0 -4
- package/dist/testing/conformance/connections.js +0 -15
- package/dist/testing/conformance/content-blobs.d.ts +0 -16
- package/dist/testing/conformance/content-blobs.js +0 -534
- package/dist/testing/conformance/counter.d.ts +0 -16
- package/dist/testing/conformance/counter.js +0 -813
- package/dist/testing/conformance/cron.d.ts +0 -5
- package/dist/testing/conformance/cron.js +0 -665
- package/dist/testing/conformance/drain.d.ts +0 -26
- package/dist/testing/conformance/drain.js +0 -540
- package/dist/testing/conformance/effect-control.d.ts +0 -38
- package/dist/testing/conformance/effect-control.js +0 -1059
- package/dist/testing/conformance/effects.d.ts +0 -23
- package/dist/testing/conformance/effects.js +0 -432
- package/dist/testing/conformance/events.d.ts +0 -28
- package/dist/testing/conformance/events.js +0 -335
- package/dist/testing/conformance/export.d.ts +0 -2
- package/dist/testing/conformance/export.js +0 -368
- package/dist/testing/conformance/fleet.d.ts +0 -444
- package/dist/testing/conformance/fleet.js +0 -1009
- package/dist/testing/conformance/groups.d.ts +0 -7
- package/dist/testing/conformance/groups.js +0 -353
- package/dist/testing/conformance/heap.d.ts +0 -3
- package/dist/testing/conformance/heap.js +0 -149
- package/dist/testing/conformance/http.d.ts +0 -2809
- package/dist/testing/conformance/http.js +0 -1234
- package/dist/testing/conformance/inspection-views.d.ts +0 -8
- package/dist/testing/conformance/inspection-views.js +0 -272
- package/dist/testing/conformance/inspector.d.ts +0 -7
- package/dist/testing/conformance/inspector.js +0 -650
- package/dist/testing/conformance/mint.d.ts +0 -58
- package/dist/testing/conformance/mint.js +0 -692
- package/dist/testing/conformance/multi-runner.d.ts +0 -8
- package/dist/testing/conformance/multi-runner.js +0 -403
- package/dist/testing/conformance/neki/plan.d.ts +0 -19
- package/dist/testing/conformance/neki/plan.js +0 -30
- package/dist/testing/conformance/observability.d.ts +0 -3
- package/dist/testing/conformance/observability.js +0 -343
- package/dist/testing/conformance/offline.d.ts +0 -4
- package/dist/testing/conformance/offline.js +0 -474
- package/dist/testing/conformance/operator-harness.d.ts +0 -57
- package/dist/testing/conformance/operator-harness.js +0 -78
- package/dist/testing/conformance/operator.d.ts +0 -3
- package/dist/testing/conformance/operator.js +0 -379
- package/dist/testing/conformance/outbox-cross-shard.d.ts +0 -14
- package/dist/testing/conformance/outbox-cross-shard.js +0 -160
- package/dist/testing/conformance/outbox.d.ts +0 -36
- package/dist/testing/conformance/outbox.js +0 -437
- package/dist/testing/conformance/payload-migrations.d.ts +0 -3
- package/dist/testing/conformance/payload-migrations.js +0 -612
- package/dist/testing/conformance/pipeline.d.ts +0 -3
- package/dist/testing/conformance/pipeline.js +0 -1040
- package/dist/testing/conformance/placement.d.ts +0 -22
- package/dist/testing/conformance/placement.js +0 -539
- package/dist/testing/conformance/platform.d.ts +0 -40
- package/dist/testing/conformance/platform.js +0 -101
- package/dist/testing/conformance/progress.d.ts +0 -39
- package/dist/testing/conformance/progress.js +0 -666
- package/dist/testing/conformance/properties.d.ts +0 -6
- package/dist/testing/conformance/properties.js +0 -295
- package/dist/testing/conformance/protocols.d.ts +0 -4
- package/dist/testing/conformance/protocols.js +0 -618
- package/dist/testing/conformance/provider/advertise.d.ts +0 -21
- package/dist/testing/conformance/provider/advertise.js +0 -44
- package/dist/testing/conformance/read-your-writes.d.ts +0 -20
- package/dist/testing/conformance/read-your-writes.js +0 -192
- package/dist/testing/conformance/reducers.d.ts +0 -8
- package/dist/testing/conformance/reducers.js +0 -258
- package/dist/testing/conformance/relay.d.ts +0 -42
- package/dist/testing/conformance/relay.js +0 -1115
- package/dist/testing/conformance/restore.d.ts +0 -43
- package/dist/testing/conformance/restore.js +0 -341
- package/dist/testing/conformance/retention.d.ts +0 -14
- package/dist/testing/conformance/retention.js +0 -463
- package/dist/testing/conformance/rls.d.ts +0 -24
- package/dist/testing/conformance/rls.js +0 -576
- package/dist/testing/conformance/simulation.d.ts +0 -31
- package/dist/testing/conformance/simulation.js +0 -167
- package/dist/testing/conformance/single-shard.d.ts +0 -2
- package/dist/testing/conformance/single-shard.js +0 -125
- package/dist/testing/conformance/singleton.d.ts +0 -3
- package/dist/testing/conformance/singleton.js +0 -293
- package/dist/testing/conformance/statements.d.ts +0 -60
- package/dist/testing/conformance/statements.js +0 -108
- package/dist/testing/conformance/streams.d.ts +0 -7
- package/dist/testing/conformance/streams.js +0 -280
- package/dist/testing/conformance/subscriptions/access.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/access.js +0 -98
- package/dist/testing/conformance/subscriptions/actors.d.ts +0 -4910
- package/dist/testing/conformance/subscriptions/actors.js +0 -372
- package/dist/testing/conformance/subscriptions/cluster.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/cluster.js +0 -89
- package/dist/testing/conformance/subscriptions/delivery.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/delivery.js +0 -253
- package/dist/testing/conformance/subscriptions/deploys.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/deploys.js +0 -162
- package/dist/testing/conformance/subscriptions/epochs.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/epochs.js +0 -169
- package/dist/testing/conformance/subscriptions/failures.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/failures.js +0 -108
- package/dist/testing/conformance/subscriptions/feeds.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/feeds.js +0 -218
- package/dist/testing/conformance/subscriptions/harness.d.ts +0 -124
- package/dist/testing/conformance/subscriptions/harness.js +0 -143
- package/dist/testing/conformance/subscriptions/operator.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/operator.js +0 -128
- package/dist/testing/conformance/subscriptions/retention.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/retention.js +0 -288
- package/dist/testing/conformance/subscriptions/routing.d.ts +0 -4
- package/dist/testing/conformance/subscriptions/routing.js +0 -179
- package/dist/testing/conformance/subscriptions.d.ts +0 -4
- package/dist/testing/conformance/subscriptions.js +0 -19
- package/dist/testing/conformance/tables.d.ts +0 -125
- package/dist/testing/conformance/tables.js +0 -664
- package/dist/testing/conformance/transports/actors.d.ts +0 -1907
- package/dist/testing/conformance/transports/actors.js +0 -163
- package/dist/testing/conformance/transports/client.d.ts +0 -3
- package/dist/testing/conformance/transports/client.js +0 -237
- package/dist/testing/conformance/transports/feeds.d.ts +0 -3
- package/dist/testing/conformance/transports/feeds.js +0 -232
- package/dist/testing/conformance/transports/harness.d.ts +0 -93
- package/dist/testing/conformance/transports/harness.js +0 -84
- package/dist/testing/conformance/transports/streams.d.ts +0 -3
- package/dist/testing/conformance/transports/streams.js +0 -96
- package/dist/testing/conformance/transports/websocket.d.ts +0 -3
- package/dist/testing/conformance/transports/websocket.js +0 -384
- package/dist/testing/conformance/transports/wire.d.ts +0 -141
- package/dist/testing/conformance/transports/wire.js +0 -204
- package/dist/testing/conformance/transports.d.ts +0 -3
- package/dist/testing/conformance/transports.js +0 -11
- package/dist/testing/conformance/watch.d.ts +0 -59
- package/dist/testing/conformance/watch.js +0 -879
- package/dist/testing/conformance/workflow-engine.d.ts +0 -97
- package/dist/testing/conformance/workflow-engine.js +0 -253
- package/dist/testing/conformance/workflow-versions.d.ts +0 -3
- package/dist/testing/conformance/workflow-versions.js +0 -545
- package/dist/testing/conformance/workflows/access.d.ts +0 -4
- package/dist/testing/conformance/workflows/access.js +0 -147
- package/dist/testing/conformance/workflows/actors.d.ts +0 -2317
- package/dist/testing/conformance/workflows/actors.js +0 -305
- package/dist/testing/conformance/workflows/basics.d.ts +0 -4
- package/dist/testing/conformance/workflows/basics.js +0 -144
- package/dist/testing/conformance/workflows/engine.d.ts +0 -4
- package/dist/testing/conformance/workflows/engine.js +0 -80
- package/dist/testing/conformance/workflows/guards.d.ts +0 -4
- package/dist/testing/conformance/workflows/guards.js +0 -172
- package/dist/testing/conformance/workflows/harness.d.ts +0 -14
- package/dist/testing/conformance/workflows/harness.js +0 -44
- package/dist/testing/conformance/workflows/recovery.d.ts +0 -4
- package/dist/testing/conformance/workflows/recovery.js +0 -181
- package/dist/testing/conformance/workflows/starts.d.ts +0 -4
- package/dist/testing/conformance/workflows/starts.js +0 -57
- package/dist/testing/conformance/workflows/suspension.d.ts +0 -4
- package/dist/testing/conformance/workflows/suspension.js +0 -106
- package/dist/testing/conformance/workflows/waits.d.ts +0 -4
- package/dist/testing/conformance/workflows/waits.js +0 -284
- package/dist/testing/conformance/workflows.d.ts +0 -4
- package/dist/testing/conformance/workflows.js +0 -19
- package/dist/testing/conformance.d.ts +0 -330
- package/dist/testing/conformance.js +0 -364
- package/dist/testing/foundation.d.ts +0 -41
- package/dist/testing/foundation.js +0 -517
- package/dist/testing/simulate-cluster.d.ts +0 -112
- package/dist/testing/simulate-cluster.js +0 -257
- package/dist/testing/simulate.d.ts +0 -90
- package/dist/testing/simulate.js +0 -127
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { Cause, DateTime, Deferred, Effect, Exit } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
import { InvalidExecutionKey } from "../../../index.js";
|
|
4
|
-
import { ActorTest } from "../../actor-test.js";
|
|
5
|
-
import { EngineProbe, Ledger, Shipper } from "./actors.js";
|
|
6
|
-
import { eventually, reset, suspendedRow } from "./harness.js";
|
|
7
|
-
/** Expiry bounds, fencing, deduplication, and body-shape guards of workflows. */
|
|
8
|
-
export const workflowGuardConformance = [
|
|
9
|
-
{
|
|
10
|
-
name: "workflows: dies with ActivityOutcomeUnknown instead of calling past the expiry bound",
|
|
11
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
12
|
-
yield* reset(fixture);
|
|
13
|
-
const test = yield* ActorTest;
|
|
14
|
-
const gate = yield* Deferred.make();
|
|
15
|
-
fixture.blocked = gate;
|
|
16
|
-
const run = yield* (yield* Shipper.get("late")).Ship({ orderId: "late1", sku: "late" });
|
|
17
|
-
yield* eventually(Effect.sync(() => fixture.runs.get("reserve:late1") === 1), "the activity to start");
|
|
18
|
-
yield* test.advance("61 seconds");
|
|
19
|
-
yield* Deferred.succeed(gate, undefined);
|
|
20
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
21
|
-
expect(Exit.isFailure(exit) && Cause.pretty(exit.cause).includes("ActivityOutcomeUnknown")).toBe(true);
|
|
22
|
-
const ledger = yield* Ledger.get("late1");
|
|
23
|
-
expect(yield* test.receiptsFor(ledger.ref, "Charge")).toBe(0);
|
|
24
|
-
})),
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "workflows: interrupts once when interrupt races completion",
|
|
28
|
-
timeoutMs: 120_000,
|
|
29
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
30
|
-
const sql = yield* SqlClient.SqlClient;
|
|
31
|
-
const engine = fixture.engine;
|
|
32
|
-
const outcomes = new Set();
|
|
33
|
-
for (let round = 0; round < 8; round++) {
|
|
34
|
-
const key = `race-${round}`;
|
|
35
|
-
const gate = yield* Deferred.make();
|
|
36
|
-
engine.gates.set(key, gate);
|
|
37
|
-
const probe = yield* EngineProbe.get(key);
|
|
38
|
-
const run = yield* probe.Probe({ scenario: "compensate-last", key });
|
|
39
|
-
yield* eventually(Effect.sync(() => (engine.runs.get(`hold:${key}`) ?? 0) >= 1), "the activity to start");
|
|
40
|
-
yield* Effect.all([Deferred.succeed(gate, undefined), run.interrupt], {
|
|
41
|
-
concurrency: 2,
|
|
42
|
-
});
|
|
43
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
44
|
-
const interrupted = Exit.isFailure(exit) && Exit.hasInterrupts(exit);
|
|
45
|
-
if (!interrupted)
|
|
46
|
-
expect(exit).toEqual(Exit.succeed("held"));
|
|
47
|
-
outcomes.add(interrupted ? "interrupted" : "completed");
|
|
48
|
-
expect(engine.runs.get(`compensate:${key}`) ?? 0).toBe(interrupted ? 1 : 0);
|
|
49
|
-
yield* run.interrupt;
|
|
50
|
-
const again = yield* run.result.pipe(Effect.exit);
|
|
51
|
-
expect(Exit.isFailure(again) && Exit.hasInterrupts(again)).toBe(interrupted);
|
|
52
|
-
const [left] = yield* sql `
|
|
53
|
-
SELECT (SELECT count(*)::int FROM actor_workflow_step WHERE execution_id = ${run.executionId}) AS steps,
|
|
54
|
-
(SELECT count(*)::int FROM actor_outbox WHERE timer_key = ${`wf:${run.executionId}`}) AS timers`;
|
|
55
|
-
expect(left).toEqual({ steps: 0, timers: 0 });
|
|
56
|
-
expect(engine.runs.get(`compensate:${key}`) ?? 0).toBe(interrupted ? 1 : 0);
|
|
57
|
-
}
|
|
58
|
-
expect(outcomes.size > 0).toBe(true);
|
|
59
|
-
})),
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "workflows: rejects step writes from a stale generation",
|
|
63
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
64
|
-
yield* reset(fixture);
|
|
65
|
-
const sql = yield* SqlClient.SqlClient;
|
|
66
|
-
const test = yield* ActorTest;
|
|
67
|
-
const gate = yield* Deferred.make();
|
|
68
|
-
fixture.blocked = gate;
|
|
69
|
-
const shipper = yield* Shipper.get("stale");
|
|
70
|
-
const run = yield* shipper.Ship({ orderId: "st1", sku: "block-stale" });
|
|
71
|
-
yield* eventually(Effect.sync(() => fixture.runs.get("reserve:st1") === 1), "the activity to start");
|
|
72
|
-
yield* sql `UPDATE actor_generations SET generation = generation + 1
|
|
73
|
-
WHERE tenant_id = ${shipper.ref.tenant} AND actor_type = 'Shipper' AND actor_id = 'stale'`;
|
|
74
|
-
yield* Deferred.succeed(gate, undefined);
|
|
75
|
-
yield* Effect.sleep("300 millis");
|
|
76
|
-
const pending = () => sql `SELECT exit IS NOT NULL AS exit FROM actor_workflow_step
|
|
77
|
-
WHERE execution_id = ${run.executionId} AND step = 'reserve'`;
|
|
78
|
-
expect(yield* pending()).toEqual([{ exit: false }]);
|
|
79
|
-
yield* test.invalidate(shipper.ref);
|
|
80
|
-
yield* test.advance("31 seconds");
|
|
81
|
-
expect(yield* run.result).toBe("r-block-stale:v2");
|
|
82
|
-
expect(fixture.runs.get("reserve:st1")).toBe(2);
|
|
83
|
-
})),
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: "workflows: deduplicates a rerun activity's actor calls by derived command id",
|
|
87
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
88
|
-
yield* reset(fixture);
|
|
89
|
-
const test = yield* ActorTest;
|
|
90
|
-
const gate = yield* Deferred.make();
|
|
91
|
-
fixture.blocked = gate;
|
|
92
|
-
const shipper = yield* Shipper.get("rerun-calls");
|
|
93
|
-
const run = yield* shipper.Ship({ orderId: "rc1", sku: "charge-block" });
|
|
94
|
-
const ledger = yield* Ledger.get("rc1");
|
|
95
|
-
yield* eventually(test.receiptsFor(ledger.ref, "Charge").pipe(Effect.map((count) => count === 1)), "the first call");
|
|
96
|
-
yield* test.invalidate(shipper.ref);
|
|
97
|
-
const sql = yield* SqlClient.SqlClient;
|
|
98
|
-
const now = DateTime.toEpochMillis(yield* test.now);
|
|
99
|
-
yield* sql `UPDATE actor_outbox SET due_at_ms = ${now}
|
|
100
|
-
WHERE timer_key = ${`wf:${run.executionId}`}`;
|
|
101
|
-
yield* test.advance("1 second");
|
|
102
|
-
expect(yield* run.result).toBe("r-charge-block-1-2:v2");
|
|
103
|
-
expect(fixture.runs.get("reserve:rc1")).toBe(2);
|
|
104
|
-
expect(yield* test.receiptsFor(ledger.ref, "Charge")).toBe(2);
|
|
105
|
-
yield* Deferred.succeed(gate, undefined);
|
|
106
|
-
})),
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: "workflows: replays the first result when a step is called twice",
|
|
110
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
111
|
-
yield* reset(fixture);
|
|
112
|
-
const run = yield* (yield* Shipper.get("twice")).Ship({ orderId: "tw1", sku: "twice" });
|
|
113
|
-
expect(yield* run.result).toBe("r-twice|r-twice");
|
|
114
|
-
expect(fixture.runs.get("reserve:tw1")).toBe(1);
|
|
115
|
-
})),
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: "workflows: dies on an actor call outside an activity",
|
|
119
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
120
|
-
yield* reset(fixture);
|
|
121
|
-
const test = yield* ActorTest;
|
|
122
|
-
const run = yield* (yield* Shipper.get("outside")).Ship({
|
|
123
|
-
orderId: "out1",
|
|
124
|
-
sku: "outside",
|
|
125
|
-
});
|
|
126
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
127
|
-
expect(Exit.isFailure(exit) && Cause.pretty(exit.cause).includes("outside a step")).toBe(true);
|
|
128
|
-
expect(yield* test.receiptsFor((yield* Ledger.get("out1")).ref, "Charge")).toBe(0);
|
|
129
|
-
})),
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: "workflows: dies on a constructor created inside a body with Unregistered workflow step",
|
|
133
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
134
|
-
const run = yield* (yield* Shipper.get("loose")).Loose({});
|
|
135
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
136
|
-
expect(Exit.isFailure(exit) && Cause.pretty(exit.cause).includes("Unregistered workflow step")).toBe(true);
|
|
137
|
-
})),
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: "workflows: rejects an oversized key with InvalidExecutionKey",
|
|
141
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
142
|
-
const shipper = yield* Shipper.get("oversized");
|
|
143
|
-
const error = yield* shipper
|
|
144
|
-
.Ship({ orderId: "k".repeat(257), sku: "a" })
|
|
145
|
-
.pipe(Effect.flip);
|
|
146
|
-
expect(error).toBeInstanceOf(InvalidExecutionKey);
|
|
147
|
-
expect(error).toMatchObject({ bytes: 257 });
|
|
148
|
-
})),
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: "workflows: attaches a repeated start without rewriting its markers",
|
|
152
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
153
|
-
yield* reset(fixture);
|
|
154
|
-
const sql = yield* SqlClient.SqlClient;
|
|
155
|
-
const test = yield* ActorTest;
|
|
156
|
-
const shipper = yield* Shipper.get("markers");
|
|
157
|
-
const run = yield* shipper.Ship({ orderId: "mk1", sku: "sleep-mk" });
|
|
158
|
-
yield* suspendedRow(run.executionId);
|
|
159
|
-
yield* sql `UPDATE actor_workflow_step SET version = 1
|
|
160
|
-
WHERE execution_id = ${run.executionId} AND kind = 'version'`;
|
|
161
|
-
const again = yield* shipper.Ship({ orderId: "mk1", sku: "sleep-mk" });
|
|
162
|
-
expect(again.executionId).toBe(run.executionId);
|
|
163
|
-
const markers = yield* sql `
|
|
164
|
-
SELECT step, version FROM actor_workflow_step
|
|
165
|
-
WHERE execution_id = ${run.executionId} AND kind = 'version'`;
|
|
166
|
-
expect(markers).toEqual([{ step: "label", version: 1 }]);
|
|
167
|
-
yield* test.advance("11 seconds");
|
|
168
|
-
expect(yield* again.result).toBe("r-sleep-mk:v1");
|
|
169
|
-
expect(fixture.runs.get("reserve:mk1")).toBe(1);
|
|
170
|
-
})),
|
|
171
|
-
},
|
|
172
|
-
];
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Duration, Effect } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
import { ActorTest } from "../../actor-test.ts";
|
|
4
|
-
import { ActorCluster } from "../../cluster.ts";
|
|
5
|
-
import type { ConformanceEnvironment } from "../../conformance.ts";
|
|
6
|
-
import { type WorkflowsFixture } from "./actors.ts";
|
|
7
|
-
export declare const reset: (fixture: WorkflowsFixture) => Effect.Effect<void, never, never>;
|
|
8
|
-
export declare const withCluster: <A, E>(environment: ConformanceEnvironment, fixture: WorkflowsFixture, body: Effect.Effect<A, E, ActorCluster>) => Promise<A>;
|
|
9
|
-
export declare const on: <A, E, R>(runner: number, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, ActorCluster | Exclude<R, ActorTest | import("../../../index.ts").Actors | import("../../../index.ts").ContentStore | import("../../../runtime/index.ts").DefectLog | import("../../index.ts").InternalActors | import("../../../runtime/operators/repair.ts").OperatorRuntime | import("@effect/sql-pglite/PgliteClient").PgliteClient | import("../../../runtime/drain.ts").RuntimeControl | SqlClient.SqlClient | import("../../../runtime/index.ts").TelemetrySampler>>;
|
|
10
|
-
export declare const advance: (runner: number, duration: Duration.Input) => Effect.Effect<void, never, ActorCluster>;
|
|
11
|
-
/** Kills the runner owning `id` and returns a survivor once it holds the shards. */
|
|
12
|
-
export declare const killOwner: (id: string) => Effect.Effect<number, never, ActorCluster>;
|
|
13
|
-
export declare const eventually: <E, R>(check: Effect.Effect<boolean, E, R>, what: string) => Effect.Effect<void, E, R>;
|
|
14
|
-
export declare const suspendedRow: (executionId: string) => Effect.Effect<void, never, SqlClient.SqlClient>;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Duration, Effect, Layer, Schedule } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
import { User } from "../../../index.js";
|
|
4
|
-
import { ActorTest } from "../../actor-test.js";
|
|
5
|
-
import { ActorCluster } from "../../cluster.js";
|
|
6
|
-
import { Shipper, workflowsLayer } from "./actors.js";
|
|
7
|
-
export const reset = (fixture) => Effect.sync(() => {
|
|
8
|
-
fixture.runs.clear();
|
|
9
|
-
fixture.blocked = undefined;
|
|
10
|
-
});
|
|
11
|
-
const EXPIRATION_SECONDS = 3;
|
|
12
|
-
export const withCluster = (environment, fixture, body) => environment.run(Effect.gen(function* () {
|
|
13
|
-
yield* reset(fixture);
|
|
14
|
-
const database = yield* environment.freshDatabase;
|
|
15
|
-
const context = yield* Layer.build(ActorTest.cluster({
|
|
16
|
-
database,
|
|
17
|
-
runners: 3,
|
|
18
|
-
shardLockExpiration: `${EXPIRATION_SECONDS} seconds`,
|
|
19
|
-
actors: workflowsLayer(fixture),
|
|
20
|
-
as: User.make({ subject: "alice" }),
|
|
21
|
-
authorize: () => Effect.succeed(true),
|
|
22
|
-
}));
|
|
23
|
-
return yield* body.pipe(Effect.provideContext(context));
|
|
24
|
-
}));
|
|
25
|
-
export const on = (runner, effect) => ActorCluster.use((cluster) => cluster.on(runner)(effect));
|
|
26
|
-
export const advance = (runner, duration) => on(runner, ActorTest.use((test) => test.advance(duration)));
|
|
27
|
-
/** Kills the runner owning `id` and returns a survivor once it holds the shards. */
|
|
28
|
-
export const killOwner = (id) => ActorCluster.use((cluster) => Effect.gen(function* () {
|
|
29
|
-
const ref = (yield* cluster.on(0)(Shipper.get(id))).ref;
|
|
30
|
-
const owner = (yield* cluster.owner(ref));
|
|
31
|
-
yield* cluster.kill(owner);
|
|
32
|
-
yield* cluster.ready;
|
|
33
|
-
return (owner + 1) % cluster.runners;
|
|
34
|
-
}));
|
|
35
|
-
export const eventually = (check, what) => check.pipe(Effect.repeat({ schedule: Schedule.spaced("25 millis"), until: (held) => held }), Effect.timeoutOrElse({
|
|
36
|
-
duration: "30 seconds",
|
|
37
|
-
orElse: () => Effect.die(new Error(`Timed out waiting for ${what}`)),
|
|
38
|
-
}), Effect.asVoid);
|
|
39
|
-
export const suspendedRow = (executionId) => eventually(Effect.gen(function* () {
|
|
40
|
-
const sql = yield* SqlClient.SqlClient;
|
|
41
|
-
const rows = yield* sql `SELECT status FROM actor_workflow_executions
|
|
42
|
-
WHERE execution_id = ${executionId}`;
|
|
43
|
-
return rows[0]?.status === "suspended";
|
|
44
|
-
}).pipe(Effect.orDie), "the execution to suspend");
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ConformanceCase } from "../../conformance.ts";
|
|
2
|
-
import { type WorkflowsFixture } from "./actors.ts";
|
|
3
|
-
/** Redelivery, execution-id validation, eviction, owner death, and drain recovery of workflows. */
|
|
4
|
-
export declare const workflowRecoveryConformance: ReadonlyArray<ConformanceCase<WorkflowsFixture>>;
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { Deferred, Effect, Exit, Option, Predicate } from "effect";
|
|
2
|
-
import { InvalidExecutionId } from "../../../index.js";
|
|
3
|
-
import { ActorTest } from "../../actor-test.js";
|
|
4
|
-
import { ActorCluster } from "../../cluster.js";
|
|
5
|
-
import { RuntimeControl } from "../../../runtime/drain.js";
|
|
6
|
-
import { EngineProbe, Probe, Ship, Shipper } from "./actors.js";
|
|
7
|
-
import { advance, eventually, killOwner, on, reset, suspendedRow, withCluster } from "./harness.js";
|
|
8
|
-
import { SqlClient } from "effect/sql";
|
|
9
|
-
/** Redelivery, execution-id validation, eviction, owner death, and drain recovery of workflows. */
|
|
10
|
-
export const workflowRecoveryConformance = [
|
|
11
|
-
{
|
|
12
|
-
name: "workflows: a resume whose reply is lost after commit still wakes the execution on redelivery",
|
|
13
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
14
|
-
yield* reset(fixture);
|
|
15
|
-
const test = yield* ActorTest;
|
|
16
|
-
const shipper = yield* Shipper.get("resume-replay");
|
|
17
|
-
const run = yield* shipper.Ship({ orderId: "rr1", sku: "sleep-rr" });
|
|
18
|
-
yield* suspendedRow(run.executionId);
|
|
19
|
-
yield* test.crashNext("afterCommit");
|
|
20
|
-
yield* test.advance("11 seconds");
|
|
21
|
-
expect(yield* run.result).toBe("r-sleep-rr:v2");
|
|
22
|
-
expect(fixture.runs.get("reserve:rr1")).toBe(1);
|
|
23
|
-
expect(yield* test.receiptsFor(shipper.ref, "$workflow/resume")).toBe(1);
|
|
24
|
-
})),
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "workflows: a malformed, foreign, or wrong-member execution id is InvalidExecutionId",
|
|
28
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
29
|
-
const bad = yield* Shipper.run(Ship, "w1.nope").pipe(Effect.flip);
|
|
30
|
-
expect(bad).toBeInstanceOf(InvalidExecutionId);
|
|
31
|
-
const other = yield* Shipper.run(Ship, "x1.abc").pipe(Effect.flip);
|
|
32
|
-
expect(other).toBeInstanceOf(InvalidExecutionId);
|
|
33
|
-
})),
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: "workflows: an evicted activation resumes its suspended execution without rerunning the activity",
|
|
37
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
38
|
-
yield* reset(fixture);
|
|
39
|
-
const test = yield* ActorTest;
|
|
40
|
-
const shipper = yield* Shipper.get("evicted");
|
|
41
|
-
const run = yield* shipper.Ship({ orderId: "o8", sku: "sleep-e" });
|
|
42
|
-
yield* suspendedRow(run.executionId);
|
|
43
|
-
yield* test.invalidate(shipper.ref);
|
|
44
|
-
yield* test.advance("11 seconds");
|
|
45
|
-
const reattached = yield* Shipper.run(Ship, run.executionId);
|
|
46
|
-
expect(yield* reattached.result).toBe("r-sleep-e:v2");
|
|
47
|
-
expect(fixture.runs.get("reserve:o8")).toBe(1);
|
|
48
|
-
})),
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: "workflows: a killed owner's suspended execution resumes on a survivor via its relay timer",
|
|
52
|
-
requiresFreshDatabase: true,
|
|
53
|
-
requiresIndependentConnections: true,
|
|
54
|
-
timeoutMs: 90_000,
|
|
55
|
-
run: ({ expect, environment, fixture }) => withCluster(environment, fixture, Effect.gen(function* () {
|
|
56
|
-
const id = yield* on(0, Effect.gen(function* () {
|
|
57
|
-
const run = yield* (yield* Shipper.get("killed-sleep")).Ship({
|
|
58
|
-
orderId: "k1",
|
|
59
|
-
sku: "sleep-k",
|
|
60
|
-
});
|
|
61
|
-
return run.executionId;
|
|
62
|
-
}));
|
|
63
|
-
yield* on(0, eventually(Effect.gen(function* () {
|
|
64
|
-
const polled = yield* (yield* Shipper.run(Ship, id)).poll;
|
|
65
|
-
return Option.isSome(polled) && Predicate.isTagged(polled.value, "Suspended");
|
|
66
|
-
}), "the sleep to suspend"));
|
|
67
|
-
const survivor = yield* killOwner("killed-sleep");
|
|
68
|
-
yield* advance(survivor, "11 seconds");
|
|
69
|
-
const result = yield* on(survivor, Effect.gen(function* () {
|
|
70
|
-
return yield* (yield* Shipper.run(Ship, id)).result;
|
|
71
|
-
}));
|
|
72
|
-
expect(result).toBe("r-sleep-k:v2");
|
|
73
|
-
expect(fixture.runs.get("reserve:k1")).toBe(1);
|
|
74
|
-
})),
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: "workflows: abandons a running execution on drain and resumes it on another runner",
|
|
78
|
-
requiresFreshDatabase: true,
|
|
79
|
-
requiresIndependentConnections: true,
|
|
80
|
-
timeoutMs: 120_000,
|
|
81
|
-
run: ({ expect, environment, fixture }) => withCluster(environment, fixture, Effect.gen(function* () {
|
|
82
|
-
const gate = yield* Deferred.make();
|
|
83
|
-
fixture.blocked = gate;
|
|
84
|
-
const id = yield* on(0, Effect.gen(function* () {
|
|
85
|
-
const run = yield* (yield* Shipper.get("drained-activity")).Ship({
|
|
86
|
-
orderId: "d1",
|
|
87
|
-
sku: "block",
|
|
88
|
-
});
|
|
89
|
-
return run.executionId;
|
|
90
|
-
}));
|
|
91
|
-
yield* eventually(Effect.sync(() => fixture.runs.get("reserve:d1") === 1), "the activity to start");
|
|
92
|
-
const cluster = yield* ActorCluster;
|
|
93
|
-
const ref = (yield* cluster.on(0)(Shipper.get("drained-activity"))).ref;
|
|
94
|
-
const owner = (yield* cluster.owner(ref));
|
|
95
|
-
const survivor = (owner + 1) % cluster.runners;
|
|
96
|
-
expect(yield* on(owner, RuntimeControl.use((control) => control.drain({ deadline: "5 seconds" })))).toEqual({ outcome: "clean", interruptedTurns: 0, interruptedJobs: 0 });
|
|
97
|
-
yield* cluster.shutdown(owner);
|
|
98
|
-
yield* cluster.ready;
|
|
99
|
-
yield* advance(survivor, "31 seconds");
|
|
100
|
-
const result = yield* on(survivor, Effect.gen(function* () {
|
|
101
|
-
return yield* (yield* Shipper.run(Ship, id)).result;
|
|
102
|
-
}));
|
|
103
|
-
expect(result).toBe("r-block:v2");
|
|
104
|
-
expect(fixture.runs.get("reserve:d1")).toBe(2);
|
|
105
|
-
yield* Deferred.succeed(gate, undefined);
|
|
106
|
-
})),
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: "workflows: replays an interrupted execution's compensation on a survivor when its runner dies mid-compensation, and records the interrupt once",
|
|
110
|
-
requiresFreshDatabase: true,
|
|
111
|
-
requiresIndependentConnections: true,
|
|
112
|
-
timeoutMs: 120_000,
|
|
113
|
-
run: ({ expect, environment, fixture }) => withCluster(environment, fixture, Effect.gen(function* () {
|
|
114
|
-
const engine = fixture.engine;
|
|
115
|
-
const key = "compensation-kill";
|
|
116
|
-
const hold = yield* Deferred.make();
|
|
117
|
-
const compensating = yield* Deferred.make();
|
|
118
|
-
engine.gates.set(key, hold);
|
|
119
|
-
engine.gates.set(`compensate:${key}`, compensating);
|
|
120
|
-
engine.runs.clear();
|
|
121
|
-
const executionId = yield* on(0, Effect.gen(function* () {
|
|
122
|
-
const run = yield* (yield* EngineProbe.get(key)).Probe({
|
|
123
|
-
scenario: "compensate-block",
|
|
124
|
-
key,
|
|
125
|
-
});
|
|
126
|
-
return run.executionId;
|
|
127
|
-
}));
|
|
128
|
-
yield* eventually(Effect.sync(() => (engine.runs.get(`hold:${key}`) ?? 0) >= 1), "the activity to start");
|
|
129
|
-
yield* on(0, Effect.flatMap(EngineProbe.run(Probe, executionId), (run) => run.interrupt));
|
|
130
|
-
yield* eventually(Effect.sync(() => (engine.runs.get(`compensate:${key}`) ?? 0) >= 1), "the compensation to start");
|
|
131
|
-
const survivor = yield* ActorCluster.use((cluster) => Effect.gen(function* () {
|
|
132
|
-
const ref = (yield* cluster.on(0)(EngineProbe.get(key))).ref;
|
|
133
|
-
const owner = (yield* cluster.owner(ref));
|
|
134
|
-
yield* cluster.kill(owner);
|
|
135
|
-
yield* cluster.ready;
|
|
136
|
-
return (owner + 1) % cluster.runners;
|
|
137
|
-
}));
|
|
138
|
-
yield* advance(survivor, "31 seconds");
|
|
139
|
-
yield* eventually(Effect.sync(() => (engine.runs.get(`compensate:${key}`) ?? 0) >= 2), "the survivor to run the compensation again");
|
|
140
|
-
yield* Deferred.succeed(compensating, undefined);
|
|
141
|
-
const exit = yield* on(survivor, Effect.flatMap(EngineProbe.run(Probe, executionId), (run) => run.result).pipe(Effect.exit));
|
|
142
|
-
expect(Exit.isFailure(exit) && Exit.hasInterrupts(exit)).toBe(true);
|
|
143
|
-
expect((engine.runs.get(`compensate:${key}`) ?? 0) >= 2).toBe(true);
|
|
144
|
-
const rows = yield* on(survivor, Effect.gen(function* () {
|
|
145
|
-
const sql = yield* SqlClient.SqlClient;
|
|
146
|
-
return yield* sql `SELECT status,
|
|
147
|
-
(SELECT count(*)::int FROM actor_workflow_step
|
|
148
|
-
WHERE execution_id = ${executionId}) AS steps
|
|
149
|
-
FROM actor_workflow_executions WHERE execution_id = ${executionId}`;
|
|
150
|
-
}).pipe(Effect.orDie));
|
|
151
|
-
expect(rows).toEqual([{ status: "finished", steps: 0 }]);
|
|
152
|
-
yield* Deferred.succeed(hold, undefined);
|
|
153
|
-
})),
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "workflows: an activity whose runner is killed mid-run is rerun on a survivor with the same identity",
|
|
157
|
-
requiresFreshDatabase: true,
|
|
158
|
-
requiresIndependentConnections: true,
|
|
159
|
-
timeoutMs: 120_000,
|
|
160
|
-
run: ({ expect, environment, fixture }) => withCluster(environment, fixture, Effect.gen(function* () {
|
|
161
|
-
const gate = yield* Deferred.make();
|
|
162
|
-
fixture.blocked = gate;
|
|
163
|
-
const id = yield* on(0, Effect.gen(function* () {
|
|
164
|
-
const run = yield* (yield* Shipper.get("killed-activity")).Ship({
|
|
165
|
-
orderId: "k2",
|
|
166
|
-
sku: "block",
|
|
167
|
-
});
|
|
168
|
-
return run.executionId;
|
|
169
|
-
}));
|
|
170
|
-
yield* eventually(Effect.sync(() => fixture.runs.get("reserve:k2") === 1), "the activity to start");
|
|
171
|
-
const survivor = yield* killOwner("killed-activity");
|
|
172
|
-
yield* advance(survivor, "31 seconds");
|
|
173
|
-
const result = yield* on(survivor, Effect.gen(function* () {
|
|
174
|
-
return yield* (yield* Shipper.run(Ship, id)).result;
|
|
175
|
-
}));
|
|
176
|
-
expect(result).toBe("r-block:v2");
|
|
177
|
-
expect(fixture.runs.get("reserve:k2")).toBe(2);
|
|
178
|
-
yield* Deferred.succeed(gate, undefined);
|
|
179
|
-
})),
|
|
180
|
-
},
|
|
181
|
-
];
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ConformanceCase } from "../../conformance.ts";
|
|
2
|
-
import { type WorkflowsFixture } from "./actors.ts";
|
|
3
|
-
/** Turn-staged starts, activity actor calls, and wait registration races of workflows. */
|
|
4
|
-
export declare const workflowStartConformance: ReadonlyArray<ConformanceCase<WorkflowsFixture>>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { ActorTest } from "../../actor-test.js";
|
|
3
|
-
import { Ledger, Ship, Shipper } from "./actors.js";
|
|
4
|
-
import { reset } from "./harness.js";
|
|
5
|
-
/** Turn-staged starts, activity actor calls, and wait registration races of workflows. */
|
|
6
|
-
export const workflowStartConformance = [
|
|
7
|
-
{
|
|
8
|
-
name: "workflows: a turn stages a start whose wait sees that turn's own event",
|
|
9
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
10
|
-
yield* reset(fixture);
|
|
11
|
-
const shipper = yield* Shipper.get("staged");
|
|
12
|
-
const id = yield* shipper.Begin({ orderId: "s1", sku: "wait-s" });
|
|
13
|
-
expect(yield* (yield* Shipper.run(Ship, id)).result).toBe("r-wait-s:paid-3");
|
|
14
|
-
const again = yield* shipper.Ship({ orderId: "s1", sku: "wait-s" });
|
|
15
|
-
expect(again.executionId).toBe(id);
|
|
16
|
-
expect(yield* again.result).toBe("r-wait-s:paid-3");
|
|
17
|
-
})),
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: "workflows: a start staged by a turn that emits nothing sees an owner event committed before the start is delivered",
|
|
21
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
22
|
-
yield* reset(fixture);
|
|
23
|
-
const shipper = yield* Shipper.get("deferred");
|
|
24
|
-
const id = yield* shipper.Defer({ orderId: "d1", sku: "wait-d" });
|
|
25
|
-
yield* shipper.Pay({ orderId: "d1", amount: 5 });
|
|
26
|
-
expect(yield* (yield* Shipper.run(Ship, id)).result).toBe("r-wait-d:paid-5");
|
|
27
|
-
})),
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "workflows: activity actor calls get distinct ids per call and reach the receiver once each",
|
|
31
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
32
|
-
yield* reset(fixture);
|
|
33
|
-
const test = yield* ActorTest;
|
|
34
|
-
const shipper = yield* Shipper.get("charging");
|
|
35
|
-
const run = yield* shipper.Ship({ orderId: "c1", sku: "charge" });
|
|
36
|
-
expect(yield* run.result).toBe("r-charge-1-2:v2");
|
|
37
|
-
const ledger = yield* Ledger.get("c1");
|
|
38
|
-
expect(yield* test.receiptsFor(ledger.ref, "Charge")).toBe(2);
|
|
39
|
-
})),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: "workflows: an event appended while a wait is registering is never lost",
|
|
43
|
-
requiresIndependentConnections: true,
|
|
44
|
-
timeoutMs: 60_000,
|
|
45
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
46
|
-
yield* reset(fixture);
|
|
47
|
-
const results = yield* Effect.forEach(Array.from({ length: 20 }, (_, index) => index), (index) => Effect.gen(function* () {
|
|
48
|
-
const shipper = yield* Shipper.get(`race-${index}`);
|
|
49
|
-
const orderId = `race-${index}`;
|
|
50
|
-
const run = yield* shipper.Ship({ orderId, sku: "wait-race" });
|
|
51
|
-
yield* shipper.Pay({ orderId, amount: index });
|
|
52
|
-
return yield* run.result;
|
|
53
|
-
}), { concurrency: 5 });
|
|
54
|
-
expect(results).toEqual(Array.from({ length: 20 }, (_, index) => `r-wait-race:paid-${index}`));
|
|
55
|
-
})),
|
|
56
|
-
},
|
|
57
|
-
];
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ConformanceCase } from "../../conformance.ts";
|
|
2
|
-
import { type WorkflowsFixture } from "./actors.ts";
|
|
3
|
-
/** Waits, races, interrupts, and keyless restarts of suspended workflows. */
|
|
4
|
-
export declare const workflowSuspensionConformance: ReadonlyArray<ConformanceCase<WorkflowsFixture>>;
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { Effect, Exit } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
import { Unauthorized } from "../../../index.js";
|
|
4
|
-
import { ActorTest } from "../../actor-test.js";
|
|
5
|
-
import { Ship, Shipper } from "./actors.js";
|
|
6
|
-
import { reset, suspendedRow } from "./harness.js";
|
|
7
|
-
/** Waits, races, interrupts, and keyless restarts of suspended workflows. */
|
|
8
|
-
export const workflowSuspensionConformance = [
|
|
9
|
-
{
|
|
10
|
-
name: "workflows: a wait sees a matching owner event appended after it registered",
|
|
11
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
12
|
-
yield* reset(fixture);
|
|
13
|
-
const shipper = yield* Shipper.get("waiting");
|
|
14
|
-
const run = yield* shipper.Ship({ orderId: "o4", sku: "wait" });
|
|
15
|
-
yield* suspendedRow(run.executionId);
|
|
16
|
-
yield* shipper.Pay({ orderId: "other", amount: 1 });
|
|
17
|
-
yield* shipper.Pay({ orderId: "o4", amount: 7 });
|
|
18
|
-
expect(yield* run.result).toBe("r-wait:paid-7");
|
|
19
|
-
})),
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: "workflows: a wait times out on the framework clock",
|
|
23
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
24
|
-
yield* reset(fixture);
|
|
25
|
-
const test = yield* ActorTest;
|
|
26
|
-
const shipper = yield* Shipper.get("timeout");
|
|
27
|
-
const run = yield* shipper.Ship({ orderId: "o5", sku: "wait" });
|
|
28
|
-
yield* suspendedRow(run.executionId);
|
|
29
|
-
yield* test.advance("61 seconds");
|
|
30
|
-
expect(yield* run.result).toBe("r-wait:unpaid");
|
|
31
|
-
})),
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: "workflows: interrupt finishes a suspended execution as interrupted",
|
|
35
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
36
|
-
yield* reset(fixture);
|
|
37
|
-
const sql = yield* SqlClient.SqlClient;
|
|
38
|
-
const shipper = yield* Shipper.get("interrupt");
|
|
39
|
-
const run = yield* shipper.Ship({ orderId: "o6", sku: "wait" });
|
|
40
|
-
yield* suspendedRow(run.executionId);
|
|
41
|
-
yield* run.interrupt;
|
|
42
|
-
yield* run.interrupt;
|
|
43
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
44
|
-
expect(Exit.isFailure(exit) && Exit.hasInterrupts(exit)).toBe(true);
|
|
45
|
-
const steps = yield* sql `SELECT count(*)::int AS count
|
|
46
|
-
FROM actor_workflow_step WHERE execution_id = ${run.executionId}`;
|
|
47
|
-
expect(steps[0].count).toBe(0);
|
|
48
|
-
})),
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: "workflows: a race whose branches all suspend resumes and records the event winner",
|
|
52
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
53
|
-
yield* reset(fixture);
|
|
54
|
-
const shipper = yield* Shipper.get("race-event");
|
|
55
|
-
const run = yield* shipper.Ship({ orderId: "r1", sku: "race" });
|
|
56
|
-
yield* suspendedRow(run.executionId);
|
|
57
|
-
yield* shipper.Pay({ orderId: "r1", amount: 5 });
|
|
58
|
-
expect(yield* run.result).toBe("r-race:paid-5");
|
|
59
|
-
expect(fixture.runs.get("reserve:r1")).toBe(1);
|
|
60
|
-
})),
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: "workflows: a race whose branches all suspend resumes and records the clock winner",
|
|
64
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
65
|
-
yield* reset(fixture);
|
|
66
|
-
const test = yield* ActorTest;
|
|
67
|
-
const shipper = yield* Shipper.get("race-clock");
|
|
68
|
-
const run = yield* shipper.Ship({ orderId: "r2", sku: "race" });
|
|
69
|
-
yield* suspendedRow(run.executionId);
|
|
70
|
-
yield* test.advance("11 seconds");
|
|
71
|
-
expect(yield* run.result).toBe("r-race:grace");
|
|
72
|
-
const again = yield* Shipper.run(Ship, run.executionId);
|
|
73
|
-
expect(yield* again.result).toBe("r-race:grace");
|
|
74
|
-
})),
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: "workflows: rerunning one keyless start effect attaches to its execution",
|
|
78
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
79
|
-
const shipper = yield* Shipper.get("keyless");
|
|
80
|
-
const start = shipper.Quote({ n: 1 });
|
|
81
|
-
const first = yield* start;
|
|
82
|
-
const retried = yield* start;
|
|
83
|
-
expect(retried.executionId).toBe(first.executionId);
|
|
84
|
-
expect(yield* retried.result).toBe("q-1");
|
|
85
|
-
const fresh = yield* shipper.Quote({ n: 1 });
|
|
86
|
-
expect(fresh.executionId).not.toBe(first.executionId);
|
|
87
|
-
})),
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: "workflows: interrupt is authorized as the execution's workflow member",
|
|
91
|
-
run: ({ expect, environment, fixture, access }) => environment.run(Effect.gen(function* () {
|
|
92
|
-
yield* reset(fixture);
|
|
93
|
-
const shipper = yield* Shipper.get("interrupt-authz");
|
|
94
|
-
const run = yield* shipper.Ship({ orderId: "a1", sku: "wait" });
|
|
95
|
-
yield* suspendedRow(run.executionId);
|
|
96
|
-
access.denied.add("Ship");
|
|
97
|
-
expect(yield* run.interrupt.pipe(Effect.flip)).toMatchObject({
|
|
98
|
-
reason: Unauthorized.make({ code: "access_denied" }),
|
|
99
|
-
});
|
|
100
|
-
access.denied.delete("Ship");
|
|
101
|
-
yield* run.interrupt;
|
|
102
|
-
const exit = yield* run.result.pipe(Effect.exit);
|
|
103
|
-
expect(Exit.isFailure(exit) && Exit.hasInterrupts(exit)).toBe(true);
|
|
104
|
-
}).pipe(Effect.ensuring(Effect.sync(() => access.denied.delete("Ship"))))),
|
|
105
|
-
},
|
|
106
|
-
];
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ConformanceCase } from "../../conformance.ts";
|
|
2
|
-
import { type WorkflowsFixture } from "./actors.ts";
|
|
3
|
-
/** Owner-event waits, clocks, and retention pins of workflows. */
|
|
4
|
-
export declare const workflowWaitConformance: ReadonlyArray<ConformanceCase<WorkflowsFixture>>;
|