@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,463 +0,0 @@
|
|
|
1
|
-
import { Cause, Clock, Effect, Exit, Fiber, Layer, Option, Schema } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
import { Actor, Intent, RetentionGap, UnknownCursor } from "../../index.js";
|
|
4
|
-
import { ActorError, CommandExpired, Timeout } from "../../errors/actor.js";
|
|
5
|
-
import { databaseTime } from "../../runtime/turn/admission.js";
|
|
6
|
-
import { ActorTest } from "../actor-test.js";
|
|
7
|
-
import { CLAIM_LEASE } from "./outbox.js";
|
|
8
|
-
export const retentionFixture = () => ({ adds: 0, receives: 0 });
|
|
9
|
-
const Noted = Actor.event("Noted", { body: Schema.String });
|
|
10
|
-
const files = Actor.blob("files");
|
|
11
|
-
const Note = Actor.command("Note", { payload: Schema.String, success: Schema.String });
|
|
12
|
-
const NoteMany = Actor.command("NoteMany", {
|
|
13
|
-
payload: Schema.Struct({ count: Schema.Int, bytes: Schema.Int }),
|
|
14
|
-
});
|
|
15
|
-
const Add = Actor.command("Add", { payload: Schema.Int, success: Schema.Int });
|
|
16
|
-
const Forward = Actor.command("Forward", { payload: Schema.String });
|
|
17
|
-
const Receive = Actor.command("Receive", { payload: Schema.String });
|
|
18
|
-
const Put = Actor.command("Put", {
|
|
19
|
-
payload: Schema.Struct({ name: Schema.String, bytes: Schema.Int }),
|
|
20
|
-
});
|
|
21
|
-
const Grow = Actor.command("Grow", {
|
|
22
|
-
payload: Schema.Struct({ name: Schema.String, bytes: Schema.Int }),
|
|
23
|
-
});
|
|
24
|
-
const Drop = Actor.command("Drop", { payload: Schema.String });
|
|
25
|
-
/** Replaces an entry and swallows the defect, so the turn commits whatever the write left. */
|
|
26
|
-
const PutCaught = Actor.command("PutCaught", {
|
|
27
|
-
payload: Schema.Struct({ name: Schema.String, bytes: Schema.Int }),
|
|
28
|
-
success: Schema.Boolean,
|
|
29
|
-
});
|
|
30
|
-
const Entry = Schema.Struct({ cursor: Schema.String, body: Schema.String });
|
|
31
|
-
const History = Actor.query("History", {
|
|
32
|
-
payload: Schema.Struct({
|
|
33
|
-
after: Schema.optional(Schema.String),
|
|
34
|
-
limit: Schema.optional(Schema.Finite),
|
|
35
|
-
}),
|
|
36
|
-
success: Schema.Array(Entry),
|
|
37
|
-
error: Schema.Union([UnknownCursor, RetentionGap]),
|
|
38
|
-
});
|
|
39
|
-
const Size = Actor.query("Size", { payload: Schema.String, success: Schema.Int });
|
|
40
|
-
const Total = Actor.query("Total", { success: Schema.Int });
|
|
41
|
-
const Journal = Actor.make("Journal", {
|
|
42
|
-
key: Schema.String,
|
|
43
|
-
state: Actor.state({ total: Schema.Int.pipe(Schema.withDecodingDefault(Effect.succeed(0))) }),
|
|
44
|
-
events: [Noted],
|
|
45
|
-
blobs: [files],
|
|
46
|
-
api: { Note, NoteMany, Add, Forward, Put, Grow, PutCaught, Drop, History, Size, Total },
|
|
47
|
-
internal: { Receive },
|
|
48
|
-
policy: {
|
|
49
|
-
keepReceipts: "2 days",
|
|
50
|
-
keepEvents: "1 day",
|
|
51
|
-
maxBlobBytes: 1024,
|
|
52
|
-
maxBlobEntries: 3,
|
|
53
|
-
executionTimeout: "2 seconds",
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
/** The same events under a longer horizon, so one sweep applies each type's own policy. */
|
|
57
|
-
const Tally = Actor.command("Tally", { payload: Schema.Int, success: Schema.Int });
|
|
58
|
-
/** Keeps receipts for less than the retry window, so only the delivery-timeout grace holds them. */
|
|
59
|
-
const Brief = Actor.make("Brief", {
|
|
60
|
-
key: Schema.String,
|
|
61
|
-
state: Actor.state({ total: Schema.Int.pipe(Schema.withDecodingDefault(Effect.succeed(0))) }),
|
|
62
|
-
api: { Tally },
|
|
63
|
-
policy: { keepReceipts: "1 second" },
|
|
64
|
-
});
|
|
65
|
-
const Chronicle = Actor.make("Chronicle", {
|
|
66
|
-
key: Schema.String,
|
|
67
|
-
events: [Noted],
|
|
68
|
-
api: { Note },
|
|
69
|
-
policy: { keepEvents: "90 days" },
|
|
70
|
-
});
|
|
71
|
-
export const retentionLayer = (fixture) => Layer.mergeAll(Journal.toLayer(Effect.succeed({
|
|
72
|
-
Forward: Effect.fnUntraced(function* (to) {
|
|
73
|
-
yield* Journal.Turn;
|
|
74
|
-
yield* (yield* Journal.intents(to)).Receive("forwarded").pipe(Intent.after("1 minute"));
|
|
75
|
-
}),
|
|
76
|
-
Note: Effect.fnUntraced(function* (body) {
|
|
77
|
-
const turn = yield* Journal.Turn;
|
|
78
|
-
yield* turn.emit(Noted.make({ body }));
|
|
79
|
-
return turn.commandId;
|
|
80
|
-
}),
|
|
81
|
-
NoteMany: Effect.fnUntraced(function* ({ count, bytes }) {
|
|
82
|
-
const turn = yield* Journal.Turn;
|
|
83
|
-
for (let index = 0; index < count; index++)
|
|
84
|
-
yield* turn.emit(Noted.make({ body: "x".repeat(bytes) }));
|
|
85
|
-
}),
|
|
86
|
-
Add: Effect.fnUntraced(function* (amount) {
|
|
87
|
-
const turn = yield* Journal.Turn;
|
|
88
|
-
fixture.adds += 1;
|
|
89
|
-
yield* turn.state.set({ total: turn.state.total + amount });
|
|
90
|
-
return turn.state.total;
|
|
91
|
-
}),
|
|
92
|
-
Receive: Effect.fnUntraced(function* () {
|
|
93
|
-
const turn = yield* Journal.Turn;
|
|
94
|
-
fixture.receives += 1;
|
|
95
|
-
yield* turn.state.set({ total: turn.state.total + 1 });
|
|
96
|
-
}),
|
|
97
|
-
Put: Effect.fnUntraced(function* ({ name, bytes }) {
|
|
98
|
-
yield* (yield* Journal.Turn).blob(files).set(name, new Uint8Array(bytes));
|
|
99
|
-
}),
|
|
100
|
-
Grow: Effect.fnUntraced(function* ({ name, bytes }) {
|
|
101
|
-
yield* (yield* Journal.Turn).blob(files).append(name, new Uint8Array(bytes));
|
|
102
|
-
}),
|
|
103
|
-
Drop: Effect.fnUntraced(function* (name) {
|
|
104
|
-
yield* (yield* Journal.Turn).blob(files).delete(name);
|
|
105
|
-
}),
|
|
106
|
-
PutCaught: Effect.fnUntraced(function* ({ name, bytes }) {
|
|
107
|
-
return yield* (yield* Journal.Turn)
|
|
108
|
-
.blob(files)
|
|
109
|
-
.set(name, new Uint8Array(bytes))
|
|
110
|
-
.pipe(Effect.as(true), Effect.catchDefect(() => Effect.succeed(false)));
|
|
111
|
-
}),
|
|
112
|
-
})), Journal.toQueryLayer(Effect.succeed({
|
|
113
|
-
History: Effect.fnUntraced(function* ({ after, limit }) {
|
|
114
|
-
const read = yield* Journal.Read;
|
|
115
|
-
const entries = yield* read.events(Noted, { after, limit });
|
|
116
|
-
return entries.map(({ cursor, event }) => ({ cursor, body: event.body }));
|
|
117
|
-
}),
|
|
118
|
-
Size: Effect.fnUntraced(function* (name) {
|
|
119
|
-
const bytes = yield* (yield* Journal.Read).blob(files).get(name);
|
|
120
|
-
return Option.match(bytes, { onNone: () => -1, onSome: (value) => value.byteLength });
|
|
121
|
-
}),
|
|
122
|
-
Total: Effect.fnUntraced(function* () {
|
|
123
|
-
return (yield* Journal.Read).state.total;
|
|
124
|
-
}),
|
|
125
|
-
})), Brief.toLayer(Effect.succeed({
|
|
126
|
-
Tally: Effect.fnUntraced(function* (amount) {
|
|
127
|
-
const turn = yield* Brief.Turn;
|
|
128
|
-
yield* turn.state.set({ total: turn.state.total + amount });
|
|
129
|
-
return turn.state.total;
|
|
130
|
-
}),
|
|
131
|
-
})), Chronicle.toLayer(Effect.succeed({
|
|
132
|
-
Note: Effect.fnUntraced(function* (body) {
|
|
133
|
-
const turn = yield* Chronicle.Turn;
|
|
134
|
-
yield* turn.emit(Noted.make({ body }));
|
|
135
|
-
return turn.commandId;
|
|
136
|
-
}),
|
|
137
|
-
})));
|
|
138
|
-
const defect = (exit) => Exit.isFailure(exit) ? Cause.pretty(exit.cause) : "succeeded";
|
|
139
|
-
const cursors = (entries) => entries.map(({ cursor }) => cursor);
|
|
140
|
-
/**
|
|
141
|
-
* Runs `effect` in a runtime of its own on a fresh database, for a case whose
|
|
142
|
-
* pause points must not catch a turn the shared runtime's relay delivers. The
|
|
143
|
-
* shared runtime stops meanwhile: two in-process runtimes at once can starve
|
|
144
|
-
* Cluster's runner health checks.
|
|
145
|
-
*/
|
|
146
|
-
const isolated = (environment, effect) => Effect.runPromise(Effect.gen(function* () {
|
|
147
|
-
yield* Effect.acquireRelease(environment.stop, () => environment.restart);
|
|
148
|
-
const database = yield* environment.freshDatabase;
|
|
149
|
-
const runtime = yield* Effect.acquireRelease(Effect.sync(() => environment.build({ database })), (built) => Effect.promise(() => built.dispose()));
|
|
150
|
-
return yield* Effect.promise(() => runtime.runPromise(Effect.scoped(effect)));
|
|
151
|
-
}).pipe(Effect.scoped));
|
|
152
|
-
const eventSequence = Effect.fnUntraced(function* (id) {
|
|
153
|
-
const sql = yield* SqlClient.SqlClient;
|
|
154
|
-
const test = yield* ActorTest;
|
|
155
|
-
const [row] = yield* sql `
|
|
156
|
-
SELECT event_sequence::text AS sequence FROM actor_generations
|
|
157
|
-
WHERE tenant_id = ${test.tenant} AND actor_type = 'Journal' AND actor_id = ${id}`;
|
|
158
|
-
return row?.sequence;
|
|
159
|
-
}, Effect.orDie);
|
|
160
|
-
/** Retention cases: pruning of receipts and events keeps expired ids rejected, outbox dedup intact, and sequences continuous. */
|
|
161
|
-
export const retentionConformance = [
|
|
162
|
-
{
|
|
163
|
-
name: "prunes receipts past keepReceipts and still rejects the expired id after pruning and restart",
|
|
164
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
165
|
-
const test = yield* ActorTest;
|
|
166
|
-
const journal = yield* Journal.get("receipts");
|
|
167
|
-
const before = fixture.adds;
|
|
168
|
-
const add = journal.Add(3);
|
|
169
|
-
expect(yield* add).toBe(3);
|
|
170
|
-
expect(yield* journal.Add(4)).toBe(7);
|
|
171
|
-
yield* test.advance("1 day");
|
|
172
|
-
yield* test.cleanup;
|
|
173
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ receipts: 2 });
|
|
174
|
-
expect((yield* add.pipe(Effect.flip)).reason).toBeInstanceOf(CommandExpired);
|
|
175
|
-
yield* test.advance("2 days");
|
|
176
|
-
yield* test.cleanup;
|
|
177
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({
|
|
178
|
-
receipts: 0,
|
|
179
|
-
state: { total: 7 },
|
|
180
|
-
});
|
|
181
|
-
expect((yield* add.pipe(Effect.flip)).reason).toBeInstanceOf(CommandExpired);
|
|
182
|
-
expect(fixture.adds - before).toBe(2);
|
|
183
|
-
expect(yield* journal.Total()).toBe(7);
|
|
184
|
-
})),
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
name: "rejects a pruned expired id after restart without running its handler",
|
|
188
|
-
run: ({ expect, environment, fixture }) => Effect.runPromise(Effect.gen(function* () {
|
|
189
|
-
yield* environment.stop;
|
|
190
|
-
const first = environment.build();
|
|
191
|
-
const saved = yield* Effect.promise(() => first
|
|
192
|
-
.runPromise(Effect.scoped(Effect.gen(function* () {
|
|
193
|
-
const test = yield* ActorTest;
|
|
194
|
-
const journal = yield* Journal.get("restart");
|
|
195
|
-
const now = yield* databaseTime;
|
|
196
|
-
const id = `v1.${now - 59_000}.${now + 1_000}.6f6f1e0a-3a42-4c1e-9d6b-2b1c1f1a9e01`;
|
|
197
|
-
expect(yield* journal.Add(5).pipe(Actor.commandId(id))).toBe(5);
|
|
198
|
-
yield* test.advance("3 days");
|
|
199
|
-
yield* test.cleanup;
|
|
200
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ receipts: 0 });
|
|
201
|
-
return { id, tenant: test.tenant };
|
|
202
|
-
})))
|
|
203
|
-
.finally(() => first.dispose()));
|
|
204
|
-
const adds = fixture.adds;
|
|
205
|
-
const second = environment.build();
|
|
206
|
-
yield* Effect.promise(() => second
|
|
207
|
-
.runPromise(Effect.scoped(Effect.gen(function* () {
|
|
208
|
-
const journal = yield* Journal.get("restart").pipe(Actor.tenant(saved.tenant));
|
|
209
|
-
yield* Effect.sleep("1100 millis");
|
|
210
|
-
const failure = yield* journal
|
|
211
|
-
.Add(5)
|
|
212
|
-
.pipe(Actor.commandId(saved.id), Effect.flip);
|
|
213
|
-
expect(failure.reason).toBeInstanceOf(CommandExpired);
|
|
214
|
-
expect(yield* journal.Total()).toBe(5);
|
|
215
|
-
})))
|
|
216
|
-
.finally(() => second.dispose()));
|
|
217
|
-
expect(fixture.adds).toBe(adds);
|
|
218
|
-
}).pipe(Effect.ensuring(environment.restart))),
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: "prunes receipts past keepReceipts without breaking outbox dedup",
|
|
222
|
-
run: ({ expect, environment, fixture }) => environment.run(Effect.gen(function* () {
|
|
223
|
-
const test = yield* ActorTest;
|
|
224
|
-
const sender = yield* Journal.get("sender");
|
|
225
|
-
const receiver = yield* Journal.get("receiver");
|
|
226
|
-
const bystander = yield* Journal.get("bystander");
|
|
227
|
-
const before = fixture.receives;
|
|
228
|
-
yield* sender.Forward("receiver");
|
|
229
|
-
const sql = yield* SqlClient.SqlClient;
|
|
230
|
-
yield* bystander.Add(1);
|
|
231
|
-
yield* sql `UPDATE actor_receipts SET command_id = o.intent_id FROM actor_outbox o
|
|
232
|
-
WHERE o.tenant_id = ${test.tenant} AND o.actor_id = 'sender'
|
|
233
|
-
AND actor_receipts.tenant_id = ${test.tenant} AND actor_receipts.actor_id = 'bystander'`;
|
|
234
|
-
yield* test.crashNext("beforeOutboxDelete");
|
|
235
|
-
yield* test.crashNext("beforeOutboxDelete");
|
|
236
|
-
yield* test.advance("10 days");
|
|
237
|
-
expect(yield* test.receiptsFor(receiver.ref, "Receive")).toBe(1);
|
|
238
|
-
expect(yield* test.inspect(sender.ref)).toMatchObject({ outbox: 1 });
|
|
239
|
-
yield* test.cleanup;
|
|
240
|
-
expect(yield* test.receiptsFor(receiver.ref, "Receive")).toBe(1);
|
|
241
|
-
expect(yield* test.receiptsFor(bystander.ref, "Add")).toBe(0);
|
|
242
|
-
yield* test.advance(CLAIM_LEASE);
|
|
243
|
-
yield* test.cleanup;
|
|
244
|
-
expect(yield* test.inspect(sender.ref)).toMatchObject({ outbox: 1 });
|
|
245
|
-
yield* test.advance(CLAIM_LEASE);
|
|
246
|
-
expect(fixture.receives - before).toBe(1);
|
|
247
|
-
expect(yield* receiver.Total()).toBe(1);
|
|
248
|
-
expect(yield* test.inspect(sender.ref)).toMatchObject({ outbox: 0 });
|
|
249
|
-
yield* test.cleanup;
|
|
250
|
-
expect(yield* test.receiptsFor(receiver.ref, "Receive")).toBe(0);
|
|
251
|
-
})),
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
name: "completes a command admitted before expiry when keepReceipts is shorter than the retry window",
|
|
255
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
256
|
-
const test = yield* ActorTest;
|
|
257
|
-
const brief = yield* Brief.get("short-horizon");
|
|
258
|
-
const pause = yield* test.pauseNext("beforeHandler");
|
|
259
|
-
yield* test.crashNext("beforeCommit");
|
|
260
|
-
const now = yield* databaseTime;
|
|
261
|
-
const id = `v1.${now - 59_500}.${now + 500}.2d1f6a3e-7b9c-4e21-8f0a-5c6d7e8f9a01`;
|
|
262
|
-
const waiter = yield* brief
|
|
263
|
-
.Tally(9)
|
|
264
|
-
.pipe(Actor.commandId(id), Effect.flip, Effect.forkChild);
|
|
265
|
-
yield* pause.reached;
|
|
266
|
-
yield* Effect.sleep("550 millis");
|
|
267
|
-
yield* pause.release;
|
|
268
|
-
expect((yield* Fiber.join(waiter)).reason).toBeInstanceOf(CommandExpired);
|
|
269
|
-
expect(yield* test.inspect(brief.ref)).toMatchObject({ state: { total: 9 }, receipts: 1 });
|
|
270
|
-
yield* test.cleanup;
|
|
271
|
-
expect(yield* test.inspect(brief.ref)).toMatchObject({ receipts: 1 });
|
|
272
|
-
})),
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
name: "prunes only an actor's oldest events, never resets the sequence, and reports the gap",
|
|
276
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
277
|
-
const test = yield* ActorTest;
|
|
278
|
-
const journal = yield* Journal.get("events");
|
|
279
|
-
const chronicle = yield* Chronicle.get("events");
|
|
280
|
-
yield* journal.Note("a");
|
|
281
|
-
yield* journal.Note("b");
|
|
282
|
-
yield* chronicle.Note("kept");
|
|
283
|
-
yield* test.advance("2 days");
|
|
284
|
-
yield* journal.Note("c");
|
|
285
|
-
yield* test.cleanup;
|
|
286
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ events: 1 });
|
|
287
|
-
expect(yield* test.inspect(chronicle.ref)).toMatchObject({ events: 1 });
|
|
288
|
-
expect(yield* eventSequence("events")).toBe("3");
|
|
289
|
-
expect(yield* journal.History({}).pipe(Effect.flip)).toEqual(RetentionGap.make({ cursor: "0" }));
|
|
290
|
-
expect(yield* journal.History({ after: "1" }).pipe(Effect.flip)).toEqual(RetentionGap.make({ cursor: "1" }));
|
|
291
|
-
expect(yield* journal.History({ after: "2" })).toEqual([{ cursor: "3", body: "c" }]);
|
|
292
|
-
yield* journal.Note("d");
|
|
293
|
-
expect(cursors(yield* journal.History({ after: "2" }))).toEqual(["3", "4"]);
|
|
294
|
-
expect(yield* eventSequence("events")).toBe("4");
|
|
295
|
-
})),
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
name: "prunes events as a prefix even when a later event carries an older timestamp",
|
|
299
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
300
|
-
const test = yield* ActorTest;
|
|
301
|
-
const sql = yield* SqlClient.SqlClient;
|
|
302
|
-
const journal = yield* Journal.get("skewed");
|
|
303
|
-
for (const body of ["a", "b", "c", "d"])
|
|
304
|
-
yield* journal.Note(body);
|
|
305
|
-
yield* sql `UPDATE actor_events SET emitted_at_ms = emitted_at_ms - 172800000
|
|
306
|
-
WHERE tenant_id = ${test.tenant} AND actor_type = 'Journal' AND actor_id = 'skewed' AND sequence = 3`;
|
|
307
|
-
yield* test.cleanup;
|
|
308
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ events: 1 });
|
|
309
|
-
expect(yield* journal.History({ after: "3" })).toEqual([{ cursor: "4", body: "d" }]);
|
|
310
|
-
expect(yield* journal.History({ after: "2" }).pipe(Effect.flip)).toEqual(RetentionGap.make({ cursor: "2" }));
|
|
311
|
-
})),
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
name: "sweeps in batches that each leave a whole prefix",
|
|
315
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
316
|
-
const test = yield* ActorTest;
|
|
317
|
-
const journal = yield* Journal.get("batched");
|
|
318
|
-
yield* journal.NoteMany({ count: 2_500, bytes: 1 });
|
|
319
|
-
yield* test.advance("2 days");
|
|
320
|
-
yield* journal.Note("fresh");
|
|
321
|
-
yield* test.cleanup;
|
|
322
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ events: 1 });
|
|
323
|
-
expect(yield* journal.History({ after: "2500" })).toEqual([
|
|
324
|
-
{ cursor: "2501", body: "fresh" },
|
|
325
|
-
]);
|
|
326
|
-
expect(yield* eventSequence("batched")).toBe("2501");
|
|
327
|
-
})),
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
name: "pages event replay by limit and continues after the last cursor",
|
|
331
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
332
|
-
const journal = yield* Journal.get("paged");
|
|
333
|
-
yield* journal.NoteMany({ count: 1_205, bytes: 1 });
|
|
334
|
-
const first = yield* journal.History({});
|
|
335
|
-
expect(first.length).toBe(1_000);
|
|
336
|
-
expect(first.at(-1).cursor).toBe("1000");
|
|
337
|
-
expect((yield* journal.History({ after: "1000" })).length).toBe(205);
|
|
338
|
-
const small = yield* journal.History({ after: "10", limit: 3 });
|
|
339
|
-
expect(cursors(small)).toEqual(["11", "12", "13"]);
|
|
340
|
-
expect(cursors(yield* journal.History({ after: "1203", limit: 3 }))).toEqual([
|
|
341
|
-
"1204",
|
|
342
|
-
"1205",
|
|
343
|
-
]);
|
|
344
|
-
expect(yield* journal.History({ after: "1205", limit: 3 })).toEqual([]);
|
|
345
|
-
for (const limit of [0, 10_001, 1.5])
|
|
346
|
-
expect(defect(yield* journal.History({ limit }).pipe(Effect.exit))).toContain("read.events limit must be an integer from 1 to 10000");
|
|
347
|
-
})),
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
name: "fails a turn whose emits exceed the per-turn byte budget and commits none of them",
|
|
351
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
352
|
-
const test = yield* ActorTest;
|
|
353
|
-
const journal = yield* Journal.get("budget");
|
|
354
|
-
yield* journal.NoteMany({ count: 2, bytes: 500_000 });
|
|
355
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ events: 2, receipts: 1 });
|
|
356
|
-
expect(defect(yield* journal.NoteMany({ count: 3, bytes: 500_000 }).pipe(Effect.exit))).toContain("Events emitted in one turn exceed 1048576 bytes");
|
|
357
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ events: 2, receipts: 1 });
|
|
358
|
-
expect(yield* eventSequence("budget")).toBe("2");
|
|
359
|
-
})),
|
|
360
|
-
},
|
|
361
|
-
{
|
|
362
|
-
name: "refuses blob writes past policy.maxBlobBytes and leaves the entry whole",
|
|
363
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
364
|
-
const test = yield* ActorTest;
|
|
365
|
-
const journal = yield* Journal.get("quota");
|
|
366
|
-
yield* journal.Put({ name: "a", bytes: 600 });
|
|
367
|
-
yield* journal.Grow({ name: "b", bytes: 200 });
|
|
368
|
-
yield* journal.Grow({ name: "b", bytes: 200 });
|
|
369
|
-
expect(defect(yield* journal.Grow({ name: "b", bytes: 25 }).pipe(Effect.exit))).toContain("One actor's blobs hold at most 1024 bytes (policy.maxBlobBytes)");
|
|
370
|
-
expect(defect(yield* journal.Put({ name: "c", bytes: 25 }).pipe(Effect.exit))).toContain("policy.maxBlobBytes");
|
|
371
|
-
yield* journal.Put({ name: "a", bytes: 624 });
|
|
372
|
-
expect(yield* journal.Size("a")).toBe(624);
|
|
373
|
-
expect(yield* journal.PutCaught({ name: "b", bytes: 500 })).toBe(false);
|
|
374
|
-
expect(yield* journal.Size("b")).toBe(400);
|
|
375
|
-
expect(yield* journal.PutCaught({ name: "b", bytes: 100 })).toBe(true);
|
|
376
|
-
expect(yield* journal.Size("b")).toBe(100);
|
|
377
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ blobs: { files: 2 } });
|
|
378
|
-
})),
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
name: "refuses a new blob entry past policy.maxBlobEntries and frees one on delete",
|
|
382
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
383
|
-
const test = yield* ActorTest;
|
|
384
|
-
const journal = yield* Journal.get("entries");
|
|
385
|
-
yield* journal.Put({ name: "a", bytes: 0 });
|
|
386
|
-
yield* journal.Put({ name: "b", bytes: 0 });
|
|
387
|
-
yield* journal.Grow({ name: "c", bytes: 0 });
|
|
388
|
-
expect(defect(yield* journal.Put({ name: "d", bytes: 0 }).pipe(Effect.exit))).toContain("One actor's blobs hold at most 3 entries (policy.maxBlobEntries)");
|
|
389
|
-
expect(defect(yield* journal.Grow({ name: "d", bytes: 1 }).pipe(Effect.exit))).toContain("policy.maxBlobEntries");
|
|
390
|
-
expect(yield* journal.PutCaught({ name: "d", bytes: 1 })).toBe(false);
|
|
391
|
-
expect(yield* journal.Size("d")).toBe(-1);
|
|
392
|
-
yield* journal.Put({ name: "a", bytes: 10 });
|
|
393
|
-
yield* journal.Grow({ name: "b", bytes: 5 });
|
|
394
|
-
yield* journal.Grow({ name: "b", bytes: 5 });
|
|
395
|
-
expect([yield* journal.Size("a"), yield* journal.Size("b")]).toEqual([10, 10]);
|
|
396
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ blobs: { files: 3 } });
|
|
397
|
-
yield* journal.Drop("b");
|
|
398
|
-
yield* journal.Drop("missing");
|
|
399
|
-
expect(yield* journal.Size("b")).toBe(-1);
|
|
400
|
-
yield* journal.Put({ name: "d", bytes: 4 });
|
|
401
|
-
expect(yield* journal.Size("d")).toBe(4);
|
|
402
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ blobs: { files: 3 } });
|
|
403
|
-
})),
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
name: "rejects a retry admitted before expiry when cleanup pruned the receipt before its turn",
|
|
407
|
-
requiresFreshDatabase: true,
|
|
408
|
-
requiresIndependentConnections: true,
|
|
409
|
-
timeoutMs: 20_000,
|
|
410
|
-
run: ({ expect, environment, fixture }) => isolated(environment, Effect.gen(function* () {
|
|
411
|
-
const test = yield* ActorTest;
|
|
412
|
-
const journal = yield* Journal.get("race");
|
|
413
|
-
const before = fixture.adds;
|
|
414
|
-
const now = yield* databaseTime;
|
|
415
|
-
const id = `v1.${now - 58_500}.${now + 1_500}.0c2f3a55-5b8e-4d53-9a51-1f7b3d9f0c11`;
|
|
416
|
-
const committing = yield* test.pauseNext("beforeCommit");
|
|
417
|
-
const first = yield* journal.Add(1).pipe(Actor.commandId(id), Effect.forkChild);
|
|
418
|
-
yield* committing.reached;
|
|
419
|
-
const delivering = yield* test.pauseNext("beforeDelivery");
|
|
420
|
-
const retry = yield* journal
|
|
421
|
-
.Add(1)
|
|
422
|
-
.pipe(Actor.commandId(id), Effect.flip, Effect.forkChild);
|
|
423
|
-
yield* delivering.reached;
|
|
424
|
-
yield* committing.release;
|
|
425
|
-
expect(yield* Fiber.join(first)).toBe(1);
|
|
426
|
-
yield* test.advance("3 days");
|
|
427
|
-
yield* test.cleanup;
|
|
428
|
-
expect(yield* test.inspect(journal.ref)).toMatchObject({ receipts: 0 });
|
|
429
|
-
yield* delivering.release;
|
|
430
|
-
const failure = yield* Fiber.join(retry);
|
|
431
|
-
expect(Schema.is(ActorError)(failure) && failure.reason).toBeInstanceOf(CommandExpired);
|
|
432
|
-
expect(fixture.adds - before).toBe(1);
|
|
433
|
-
expect(yield* journal.Total()).toBe(1);
|
|
434
|
-
})),
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
name: "cancels a query read past commandTimeout on the server",
|
|
438
|
-
requiresIndependentConnections: true,
|
|
439
|
-
timeoutMs: 20_000,
|
|
440
|
-
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
441
|
-
const journal = yield* Journal.get("slow-read");
|
|
442
|
-
yield* journal.Add(1);
|
|
443
|
-
const connect = environment.connect;
|
|
444
|
-
const locker = yield* connect;
|
|
445
|
-
yield* locker.query("BEGIN");
|
|
446
|
-
yield* locker.query("LOCK TABLE actor_state IN ACCESS EXCLUSIVE MODE");
|
|
447
|
-
const started = yield* Clock.currentTimeMillis;
|
|
448
|
-
const failure = yield* journal.Total().pipe(Effect.flip);
|
|
449
|
-
expect(failure.reason).toBeInstanceOf(Timeout);
|
|
450
|
-
expect((yield* Clock.currentTimeMillis) - started < 10_000).toBe(true);
|
|
451
|
-
const waiting = yield* locker.query(`SELECT count(*)::int AS waiting FROM pg_stat_activity
|
|
452
|
-
WHERE datname = current_database() AND wait_event_type = 'Lock' AND pid <> pg_backend_pid()`);
|
|
453
|
-
expect(waiting).toEqual([{ waiting: 0 }]);
|
|
454
|
-
yield* locker.query("ROLLBACK");
|
|
455
|
-
expect(yield* journal.Total()).toBe(1);
|
|
456
|
-
})),
|
|
457
|
-
},
|
|
458
|
-
];
|
|
459
|
-
/** Retention actors. */
|
|
460
|
-
export const retentionSuite = {
|
|
461
|
-
fixture: retentionFixture,
|
|
462
|
-
layer: retentionLayer,
|
|
463
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { Crypto, Effect, Redacted, Scope } from "effect";
|
|
3
|
-
import type { ConformanceCase, ConformanceEnvironment } from "../conformance.ts";
|
|
4
|
-
/**
|
|
5
|
-
* The operator script of the row-level security guide: a tenant role the
|
|
6
|
-
* runtime takes for tenant-scoped transactions, and a separate view-owner role
|
|
7
|
-
* the policies bind, which the tenant role can't act as.
|
|
8
|
-
*/
|
|
9
|
-
export declare const grants: ({ role, viewOwner, }: {
|
|
10
|
-
readonly role: string;
|
|
11
|
-
readonly viewOwner: string;
|
|
12
|
-
}) => string[];
|
|
13
|
-
type Target = Redacted.Redacted<string> | {
|
|
14
|
-
readonly liveClient: PGlite;
|
|
15
|
-
};
|
|
16
|
-
/** A fresh database for `environment`, migrated with the owned tables and a new tenant role, as row-level security cases need. */
|
|
17
|
-
export declare const preparedForRowLevelSecurity: (environment: ConformanceEnvironment) => Effect.Effect<{
|
|
18
|
-
target: Target;
|
|
19
|
-
role: string;
|
|
20
|
-
viewOwner: string;
|
|
21
|
-
}, never, Crypto.Crypto | Scope.Scope>;
|
|
22
|
-
/** Row-level-security cases: every framework and owned table carries the tenant policy, and runners serving two tenants each see only their own rows. */
|
|
23
|
-
export declare const rlsConformance: ReadonlyArray<ConformanceCase>;
|
|
24
|
-
export {};
|