@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.
Files changed (308) hide show
  1. package/CHANGELOG.md +46 -3
  2. package/README.md +2 -2
  3. package/dist/actor/definition.d.ts +13 -9
  4. package/dist/actor/descriptor.d.ts +2 -2
  5. package/dist/actor/descriptor.js +4 -4
  6. package/dist/client/calls.js +1 -1
  7. package/dist/client/index.d.ts +1 -1
  8. package/dist/client/index.js +1 -1
  9. package/dist/errors/actor.d.ts +12 -42
  10. package/dist/errors/actor.js +13 -42
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/members/payload.d.ts +1 -1
  14. package/dist/protocol/inspection.d.ts +221 -1
  15. package/dist/protocol/inspection.js +151 -2
  16. package/dist/protocol/wire.d.ts +6 -49
  17. package/dist/protocol/wire.js +1 -10
  18. package/dist/runtime/actors.d.ts +3 -0
  19. package/dist/runtime/adoption/backfill.js +1 -1
  20. package/dist/runtime/adoption/enforce.js +2 -2
  21. package/dist/runtime/adoption/observe.js +1 -1
  22. package/dist/runtime/adoption/plan.d.ts +1 -1
  23. package/dist/runtime/adoption/startup.js +1 -1
  24. package/dist/runtime/adoption/status.d.ts +1 -1
  25. package/dist/runtime/app.d.ts +65 -0
  26. package/dist/runtime/app.js +38 -0
  27. package/dist/runtime/connections/holder.d.ts +11 -1
  28. package/dist/runtime/connections/holder.js +8 -4
  29. package/dist/runtime/connections/streams.d.ts +4 -4
  30. package/dist/runtime/content/store.js +15 -11
  31. package/dist/runtime/database/bounded.d.ts +4 -18
  32. package/dist/runtime/database/bounded.js +36 -22
  33. package/dist/runtime/database/coordination.d.ts +12 -3
  34. package/dist/runtime/database/coordination.js +29 -36
  35. package/dist/runtime/database/migrations.d.ts +61 -29
  36. package/dist/runtime/database/migrations.js +375 -139
  37. package/dist/runtime/database/replica.d.ts +4 -1
  38. package/dist/runtime/database/replica.js +4 -1
  39. package/dist/runtime/database/schema.d.ts +13 -0
  40. package/dist/runtime/database/schema.js +13 -0
  41. package/dist/runtime/database/shards.d.ts +31 -13
  42. package/dist/runtime/database/shards.js +38 -26
  43. package/dist/runtime/entity/register.d.ts +8 -1
  44. package/dist/runtime/entity/register.js +58 -7
  45. package/dist/runtime/fleet/checks.js +4 -4
  46. package/dist/runtime/fleet/maintainer.d.ts +1 -1
  47. package/dist/runtime/fleet/maintainer.js +1 -1
  48. package/dist/runtime/index.d.ts +2 -0
  49. package/dist/runtime/index.js +1 -0
  50. package/dist/runtime/inspector/layer.d.ts +20 -0
  51. package/dist/runtime/inspector/layer.js +224 -5
  52. package/dist/runtime/inspector/queries.d.ts +54 -8
  53. package/dist/runtime/inspector/queries.js +84 -15
  54. package/dist/runtime/layer.d.ts +48 -39
  55. package/dist/runtime/layer.js +124 -56
  56. package/dist/runtime/operators/audit.d.ts +3 -0
  57. package/dist/runtime/operators/audit.js +6 -2
  58. package/dist/runtime/operators/repair.js +30 -10
  59. package/dist/runtime/operators/seed.js +4 -2
  60. package/dist/runtime/payloads/versions.d.ts +4 -4
  61. package/dist/runtime/payloads/versions.js +24 -15
  62. package/dist/runtime/queries.js +4 -2
  63. package/dist/runtime/registration.d.ts +5 -2
  64. package/dist/runtime/registration.js +3 -3
  65. package/dist/runtime/runner.d.ts +12 -0
  66. package/dist/runtime/runner.js +12 -0
  67. package/dist/runtime/storage/codec.d.ts +19 -7
  68. package/dist/runtime/storage/codec.js +23 -7
  69. package/dist/runtime/storage/placements.d.ts +6 -1
  70. package/dist/runtime/storage/placements.js +98 -6
  71. package/dist/runtime/storage/retention.d.ts +6 -0
  72. package/dist/runtime/storage/retention.js +25 -13
  73. package/dist/runtime/subscriptions/relay.js +47 -44
  74. package/dist/runtime/subscriptions/storage.d.ts +3 -2
  75. package/dist/runtime/subscriptions/storage.js +9 -6
  76. package/dist/runtime/telemetry/live.d.ts +255 -0
  77. package/dist/runtime/telemetry/live.js +540 -0
  78. package/dist/runtime/telemetry/metrics.d.ts +2 -0
  79. package/dist/runtime/telemetry/metrics.js +4 -1
  80. package/dist/runtime/telemetry/sampler.js +33 -13
  81. package/dist/runtime/telemetry/spans.d.ts +1 -1
  82. package/dist/runtime/telemetry/spans.js +1 -1
  83. package/dist/runtime/topology/locks.d.ts +6 -3
  84. package/dist/runtime/topology/locks.js +27 -4
  85. package/dist/runtime/turn/execute.d.ts +2 -0
  86. package/dist/runtime/turn/execute.js +5 -2
  87. package/dist/runtime/turn/pipeline.d.ts +5 -12
  88. package/dist/runtime/turn/pipeline.js +11 -11
  89. package/dist/runtime/turn/relay.d.ts +13 -3
  90. package/dist/runtime/turn/relay.js +27 -14
  91. package/dist/runtime/turn/rows.js +4 -4
  92. package/dist/runtime/workflows/compatibility.d.ts +1 -1
  93. package/dist/runtime/workflows/compatibility.js +85 -31
  94. package/dist/runtime/workflows/engine.js +20 -17
  95. package/dist/serve/layer.js +3 -2
  96. package/dist/tables/owned.d.ts +1 -1
  97. package/dist/tables/owned.js +1 -1
  98. package/dist/testing/actor-test.d.ts +2 -24
  99. package/dist/testing/actor-test.js +1 -27
  100. package/dist/testing/index.d.ts +0 -12
  101. package/dist/testing/index.js +0 -6
  102. package/package.json +1 -1
  103. package/dist/runtime/database/neki/migrations.d.ts +0 -31
  104. package/dist/runtime/database/neki/migrations.js +0 -276
  105. package/dist/runtime/database/neki/session.d.ts +0 -23
  106. package/dist/runtime/database/neki/session.js +0 -31
  107. package/dist/testing/cluster.d.ts +0 -93
  108. package/dist/testing/cluster.js +0 -296
  109. package/dist/testing/conformance/access.d.ts +0 -5
  110. package/dist/testing/conformance/access.js +0 -531
  111. package/dist/testing/conformance/admission.d.ts +0 -11
  112. package/dist/testing/conformance/admission.js +0 -182
  113. package/dist/testing/conformance/adoption.d.ts +0 -2
  114. package/dist/testing/conformance/adoption.js +0 -1019
  115. package/dist/testing/conformance/assertions.d.ts +0 -202
  116. package/dist/testing/conformance/assertions.js +0 -849
  117. package/dist/testing/conformance/batches.d.ts +0 -41
  118. package/dist/testing/conformance/batches.js +0 -388
  119. package/dist/testing/conformance/blobs.d.ts +0 -14
  120. package/dist/testing/conformance/blobs.js +0 -405
  121. package/dist/testing/conformance/capacity.d.ts +0 -8
  122. package/dist/testing/conformance/capacity.js +0 -433
  123. package/dist/testing/conformance/client.d.ts +0 -5
  124. package/dist/testing/conformance/client.js +0 -719
  125. package/dist/testing/conformance/cold-serve.d.ts +0 -16
  126. package/dist/testing/conformance/cold-serve.js +0 -254
  127. package/dist/testing/conformance/connections/actors.d.ts +0 -1143
  128. package/dist/testing/conformance/connections/actors.js +0 -180
  129. package/dist/testing/conformance/connections/harness.d.ts +0 -1754
  130. package/dist/testing/conformance/connections/harness.js +0 -142
  131. package/dist/testing/conformance/connections/holders.d.ts +0 -4
  132. package/dist/testing/conformance/connections/holders.js +0 -165
  133. package/dist/testing/conformance/connections/lifecycle.d.ts +0 -4
  134. package/dist/testing/conformance/connections/lifecycle.js +0 -113
  135. package/dist/testing/conformance/connections/limits.d.ts +0 -4
  136. package/dist/testing/conformance/connections/limits.js +0 -127
  137. package/dist/testing/conformance/connections/reauthorization.d.ts +0 -4
  138. package/dist/testing/conformance/connections/reauthorization.js +0 -170
  139. package/dist/testing/conformance/connections/resync.d.ts +0 -4
  140. package/dist/testing/conformance/connections/resync.js +0 -360
  141. package/dist/testing/conformance/connections/takeover.d.ts +0 -4
  142. package/dist/testing/conformance/connections/takeover.js +0 -287
  143. package/dist/testing/conformance/connections.d.ts +0 -4
  144. package/dist/testing/conformance/connections.js +0 -15
  145. package/dist/testing/conformance/content-blobs.d.ts +0 -16
  146. package/dist/testing/conformance/content-blobs.js +0 -534
  147. package/dist/testing/conformance/counter.d.ts +0 -16
  148. package/dist/testing/conformance/counter.js +0 -813
  149. package/dist/testing/conformance/cron.d.ts +0 -5
  150. package/dist/testing/conformance/cron.js +0 -665
  151. package/dist/testing/conformance/drain.d.ts +0 -26
  152. package/dist/testing/conformance/drain.js +0 -540
  153. package/dist/testing/conformance/effect-control.d.ts +0 -38
  154. package/dist/testing/conformance/effect-control.js +0 -1059
  155. package/dist/testing/conformance/effects.d.ts +0 -23
  156. package/dist/testing/conformance/effects.js +0 -432
  157. package/dist/testing/conformance/events.d.ts +0 -28
  158. package/dist/testing/conformance/events.js +0 -335
  159. package/dist/testing/conformance/export.d.ts +0 -2
  160. package/dist/testing/conformance/export.js +0 -368
  161. package/dist/testing/conformance/fleet.d.ts +0 -444
  162. package/dist/testing/conformance/fleet.js +0 -1009
  163. package/dist/testing/conformance/groups.d.ts +0 -7
  164. package/dist/testing/conformance/groups.js +0 -353
  165. package/dist/testing/conformance/heap.d.ts +0 -3
  166. package/dist/testing/conformance/heap.js +0 -149
  167. package/dist/testing/conformance/http.d.ts +0 -2809
  168. package/dist/testing/conformance/http.js +0 -1234
  169. package/dist/testing/conformance/inspection-views.d.ts +0 -8
  170. package/dist/testing/conformance/inspection-views.js +0 -272
  171. package/dist/testing/conformance/inspector.d.ts +0 -7
  172. package/dist/testing/conformance/inspector.js +0 -650
  173. package/dist/testing/conformance/mint.d.ts +0 -58
  174. package/dist/testing/conformance/mint.js +0 -692
  175. package/dist/testing/conformance/multi-runner.d.ts +0 -8
  176. package/dist/testing/conformance/multi-runner.js +0 -403
  177. package/dist/testing/conformance/neki/plan.d.ts +0 -19
  178. package/dist/testing/conformance/neki/plan.js +0 -30
  179. package/dist/testing/conformance/observability.d.ts +0 -3
  180. package/dist/testing/conformance/observability.js +0 -343
  181. package/dist/testing/conformance/offline.d.ts +0 -4
  182. package/dist/testing/conformance/offline.js +0 -474
  183. package/dist/testing/conformance/operator-harness.d.ts +0 -57
  184. package/dist/testing/conformance/operator-harness.js +0 -78
  185. package/dist/testing/conformance/operator.d.ts +0 -3
  186. package/dist/testing/conformance/operator.js +0 -379
  187. package/dist/testing/conformance/outbox-cross-shard.d.ts +0 -14
  188. package/dist/testing/conformance/outbox-cross-shard.js +0 -160
  189. package/dist/testing/conformance/outbox.d.ts +0 -36
  190. package/dist/testing/conformance/outbox.js +0 -437
  191. package/dist/testing/conformance/payload-migrations.d.ts +0 -3
  192. package/dist/testing/conformance/payload-migrations.js +0 -612
  193. package/dist/testing/conformance/pipeline.d.ts +0 -3
  194. package/dist/testing/conformance/pipeline.js +0 -1040
  195. package/dist/testing/conformance/placement.d.ts +0 -22
  196. package/dist/testing/conformance/placement.js +0 -539
  197. package/dist/testing/conformance/platform.d.ts +0 -40
  198. package/dist/testing/conformance/platform.js +0 -101
  199. package/dist/testing/conformance/progress.d.ts +0 -39
  200. package/dist/testing/conformance/progress.js +0 -666
  201. package/dist/testing/conformance/properties.d.ts +0 -6
  202. package/dist/testing/conformance/properties.js +0 -295
  203. package/dist/testing/conformance/protocols.d.ts +0 -4
  204. package/dist/testing/conformance/protocols.js +0 -618
  205. package/dist/testing/conformance/provider/advertise.d.ts +0 -21
  206. package/dist/testing/conformance/provider/advertise.js +0 -44
  207. package/dist/testing/conformance/read-your-writes.d.ts +0 -20
  208. package/dist/testing/conformance/read-your-writes.js +0 -192
  209. package/dist/testing/conformance/reducers.d.ts +0 -8
  210. package/dist/testing/conformance/reducers.js +0 -258
  211. package/dist/testing/conformance/relay.d.ts +0 -42
  212. package/dist/testing/conformance/relay.js +0 -1115
  213. package/dist/testing/conformance/restore.d.ts +0 -43
  214. package/dist/testing/conformance/restore.js +0 -341
  215. package/dist/testing/conformance/retention.d.ts +0 -14
  216. package/dist/testing/conformance/retention.js +0 -463
  217. package/dist/testing/conformance/rls.d.ts +0 -24
  218. package/dist/testing/conformance/rls.js +0 -576
  219. package/dist/testing/conformance/simulation.d.ts +0 -31
  220. package/dist/testing/conformance/simulation.js +0 -167
  221. package/dist/testing/conformance/single-shard.d.ts +0 -2
  222. package/dist/testing/conformance/single-shard.js +0 -125
  223. package/dist/testing/conformance/singleton.d.ts +0 -3
  224. package/dist/testing/conformance/singleton.js +0 -293
  225. package/dist/testing/conformance/statements.d.ts +0 -60
  226. package/dist/testing/conformance/statements.js +0 -108
  227. package/dist/testing/conformance/streams.d.ts +0 -7
  228. package/dist/testing/conformance/streams.js +0 -280
  229. package/dist/testing/conformance/subscriptions/access.d.ts +0 -4
  230. package/dist/testing/conformance/subscriptions/access.js +0 -98
  231. package/dist/testing/conformance/subscriptions/actors.d.ts +0 -4910
  232. package/dist/testing/conformance/subscriptions/actors.js +0 -372
  233. package/dist/testing/conformance/subscriptions/cluster.d.ts +0 -4
  234. package/dist/testing/conformance/subscriptions/cluster.js +0 -89
  235. package/dist/testing/conformance/subscriptions/delivery.d.ts +0 -4
  236. package/dist/testing/conformance/subscriptions/delivery.js +0 -253
  237. package/dist/testing/conformance/subscriptions/deploys.d.ts +0 -4
  238. package/dist/testing/conformance/subscriptions/deploys.js +0 -162
  239. package/dist/testing/conformance/subscriptions/epochs.d.ts +0 -4
  240. package/dist/testing/conformance/subscriptions/epochs.js +0 -169
  241. package/dist/testing/conformance/subscriptions/failures.d.ts +0 -4
  242. package/dist/testing/conformance/subscriptions/failures.js +0 -108
  243. package/dist/testing/conformance/subscriptions/feeds.d.ts +0 -4
  244. package/dist/testing/conformance/subscriptions/feeds.js +0 -218
  245. package/dist/testing/conformance/subscriptions/harness.d.ts +0 -124
  246. package/dist/testing/conformance/subscriptions/harness.js +0 -143
  247. package/dist/testing/conformance/subscriptions/operator.d.ts +0 -4
  248. package/dist/testing/conformance/subscriptions/operator.js +0 -128
  249. package/dist/testing/conformance/subscriptions/retention.d.ts +0 -4
  250. package/dist/testing/conformance/subscriptions/retention.js +0 -288
  251. package/dist/testing/conformance/subscriptions/routing.d.ts +0 -4
  252. package/dist/testing/conformance/subscriptions/routing.js +0 -179
  253. package/dist/testing/conformance/subscriptions.d.ts +0 -4
  254. package/dist/testing/conformance/subscriptions.js +0 -19
  255. package/dist/testing/conformance/tables.d.ts +0 -125
  256. package/dist/testing/conformance/tables.js +0 -664
  257. package/dist/testing/conformance/transports/actors.d.ts +0 -1907
  258. package/dist/testing/conformance/transports/actors.js +0 -163
  259. package/dist/testing/conformance/transports/client.d.ts +0 -3
  260. package/dist/testing/conformance/transports/client.js +0 -237
  261. package/dist/testing/conformance/transports/feeds.d.ts +0 -3
  262. package/dist/testing/conformance/transports/feeds.js +0 -232
  263. package/dist/testing/conformance/transports/harness.d.ts +0 -93
  264. package/dist/testing/conformance/transports/harness.js +0 -84
  265. package/dist/testing/conformance/transports/streams.d.ts +0 -3
  266. package/dist/testing/conformance/transports/streams.js +0 -96
  267. package/dist/testing/conformance/transports/websocket.d.ts +0 -3
  268. package/dist/testing/conformance/transports/websocket.js +0 -384
  269. package/dist/testing/conformance/transports/wire.d.ts +0 -141
  270. package/dist/testing/conformance/transports/wire.js +0 -204
  271. package/dist/testing/conformance/transports.d.ts +0 -3
  272. package/dist/testing/conformance/transports.js +0 -11
  273. package/dist/testing/conformance/watch.d.ts +0 -59
  274. package/dist/testing/conformance/watch.js +0 -879
  275. package/dist/testing/conformance/workflow-engine.d.ts +0 -97
  276. package/dist/testing/conformance/workflow-engine.js +0 -253
  277. package/dist/testing/conformance/workflow-versions.d.ts +0 -3
  278. package/dist/testing/conformance/workflow-versions.js +0 -545
  279. package/dist/testing/conformance/workflows/access.d.ts +0 -4
  280. package/dist/testing/conformance/workflows/access.js +0 -147
  281. package/dist/testing/conformance/workflows/actors.d.ts +0 -2317
  282. package/dist/testing/conformance/workflows/actors.js +0 -305
  283. package/dist/testing/conformance/workflows/basics.d.ts +0 -4
  284. package/dist/testing/conformance/workflows/basics.js +0 -144
  285. package/dist/testing/conformance/workflows/engine.d.ts +0 -4
  286. package/dist/testing/conformance/workflows/engine.js +0 -80
  287. package/dist/testing/conformance/workflows/guards.d.ts +0 -4
  288. package/dist/testing/conformance/workflows/guards.js +0 -172
  289. package/dist/testing/conformance/workflows/harness.d.ts +0 -14
  290. package/dist/testing/conformance/workflows/harness.js +0 -44
  291. package/dist/testing/conformance/workflows/recovery.d.ts +0 -4
  292. package/dist/testing/conformance/workflows/recovery.js +0 -181
  293. package/dist/testing/conformance/workflows/starts.d.ts +0 -4
  294. package/dist/testing/conformance/workflows/starts.js +0 -57
  295. package/dist/testing/conformance/workflows/suspension.d.ts +0 -4
  296. package/dist/testing/conformance/workflows/suspension.js +0 -106
  297. package/dist/testing/conformance/workflows/waits.d.ts +0 -4
  298. package/dist/testing/conformance/workflows/waits.js +0 -284
  299. package/dist/testing/conformance/workflows.d.ts +0 -4
  300. package/dist/testing/conformance/workflows.js +0 -19
  301. package/dist/testing/conformance.d.ts +0 -330
  302. package/dist/testing/conformance.js +0 -364
  303. package/dist/testing/foundation.d.ts +0 -41
  304. package/dist/testing/foundation.js +0 -517
  305. package/dist/testing/simulate-cluster.d.ts +0 -112
  306. package/dist/testing/simulate-cluster.js +0 -257
  307. package/dist/testing/simulate.d.ts +0 -90
  308. package/dist/testing/simulate.js +0 -127
@@ -1,8 +0,0 @@
1
- import { Schema } from "effect";
2
- import type { ConformanceCase } from "../conformance.ts";
3
- /** A commutative reducer: calls already waiting on the owner merge into one turn. */
4
- export declare const Tick: import("../../index.ts").Reducer<"Tick", {
5
- readonly count: Schema.withDecodingDefault<Schema.Finite, never>;
6
- }, Schema.Finite, Schema.Void, Schema.Never>;
7
- /** Multi-runner cases on three runners: placement on exactly one runner, merging of commutative calls into one turn, and retry on the next owner after a kill. */
8
- export declare const multiRunnerConformance: ReadonlyArray<ConformanceCase>;
@@ -1,403 +0,0 @@
1
- import { Effect, Fiber, Layer, Result, Schedule, Schema } from "effect";
2
- import { SqlClient } from "effect/sql";
3
- import { Actor, Actors, System, User } from "../../index.js";
4
- import { InternalActors } from "../../runtime/actors.js";
5
- import { ActorTest } from "../actor-test.js";
6
- import { ActorCluster } from "../cluster.js";
7
- const Tallied = Actor.event("Tallied", { amount: Schema.Finite });
8
- const Add = Actor.command("Add", { payload: Schema.Finite, success: Schema.Finite });
9
- const Whoami = Actor.command("Whoami", { success: Schema.String });
10
- const Log = Actor.query("Log", {
11
- success: Schema.Array(Schema.Struct({ cursor: Schema.String, commandId: Schema.String })),
12
- });
13
- const TallyState = Actor.state({
14
- count: Schema.Finite.pipe(Schema.withDecodingDefault(Effect.succeed(0))),
15
- });
16
- const tickReductions = { count: 0 };
17
- /** Runs of the `Add` handler, committed or not, across every runner. */
18
- const addRuns = { count: 0 };
19
- /** A commutative reducer: calls already waiting on the owner merge into one turn. */
20
- export const Tick = Actor.reducer("Tick", {
21
- state: TallyState,
22
- payload: Schema.Finite,
23
- reduce: (state, amount) => {
24
- tickReductions.count += 1;
25
- return Result.succeed({ count: state.count + amount });
26
- },
27
- batch: { combine: (first, second) => first + second },
28
- });
29
- const Tally = Actor.make("Tally", {
30
- key: Schema.String,
31
- state: TallyState,
32
- events: [Tallied],
33
- api: { Add, Whoami, Log, Tick },
34
- });
35
- const TallyLive = Layer.mergeAll(Tally.toLayer(Effect.succeed({
36
- Add: Effect.fnUntraced(function* (amount) {
37
- addRuns.count += 1;
38
- const turn = yield* Tally.Turn;
39
- yield* turn.state.set({ count: turn.state.count + amount });
40
- yield* turn.emit(Tallied.make({ amount }));
41
- return turn.state.count;
42
- }),
43
- Whoami: Effect.fnUntraced(function* () {
44
- const turn = yield* Tally.Turn;
45
- return Schema.is(User)(turn.caller) ? turn.caller.subject : turn.caller._tag;
46
- }),
47
- })), Tally.toQueryLayer(Effect.succeed({
48
- Log: Effect.fnUntraced(function* () {
49
- const entries = yield* (yield* Tally.Read).events(Tallied);
50
- return entries.map(({ cursor, commandId }) => ({ cursor, commandId }));
51
- }, Effect.orDie),
52
- })));
53
- const Dispatch = Actor.command("Dispatch", { payload: Schema.String });
54
- const Hear = Actor.command("Hear");
55
- const Heard = Actor.query("Heard", { success: Schema.String });
56
- /** Hears the principal a relay-delivered intent carries: the sender's caller as `onBehalfOf`. */
57
- const Envoy = Actor.make("Envoy", {
58
- key: Schema.String,
59
- state: Actor.state({
60
- heard: Schema.String.pipe(Schema.withDecodingDefault(Effect.succeed(""))),
61
- }),
62
- api: { Dispatch, Heard },
63
- internal: { Hear },
64
- });
65
- const EnvoyLive = Layer.mergeAll(Envoy.toLayer(Effect.succeed({
66
- Dispatch: Effect.fnUntraced(function* (target) {
67
- yield* Envoy.Turn;
68
- yield* (yield* Envoy.intents(target)).Hear();
69
- }),
70
- Hear: Effect.fnUntraced(function* () {
71
- const turn = yield* Envoy.Turn;
72
- yield* turn.state.set({
73
- heard: Schema.is(System)(turn.caller) ? (turn.caller.onBehalfOf?.subject ?? "") : "",
74
- });
75
- }),
76
- })), Envoy.toQueryLayer(Effect.succeed({
77
- Heard: Effect.fnUntraced(function* () {
78
- return (yield* Envoy.Read).state.heard;
79
- }),
80
- })));
81
- const EXPIRATION_SECONDS = 3;
82
- /** Builds a fresh database and a cluster of `runners` on it for one case. */
83
- const withCluster = (environment, runners, body) => environment.run(Effect.gen(function* () {
84
- const database = yield* environment.freshDatabase;
85
- const context = yield* Layer.build(ActorTest.cluster({
86
- database,
87
- runners,
88
- shardLockExpiration: `${EXPIRATION_SECONDS} seconds`,
89
- actors: Layer.merge(TallyLive, EnvoyLive),
90
- as: User.make({ subject: "alice" }),
91
- authorize: () => Effect.succeed(true),
92
- }));
93
- return yield* body.pipe(Effect.provideContext(context));
94
- }));
95
- const add = (runner, id, amount) => Effect.gen(function* () {
96
- const cluster = yield* ActorCluster;
97
- return yield* cluster.on(runner)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Add(amount))));
98
- });
99
- const refOf = (id) => Effect.gen(function* () {
100
- const cluster = yield* ActorCluster;
101
- return (yield* cluster.on(0)(Tally.get(id))).ref;
102
- });
103
- const inspect = (runner, ref) => Effect.gen(function* () {
104
- const cluster = yield* ActorCluster;
105
- return yield* cluster.on(runner)(ActorTest.use((test) => test.inspect(ref)));
106
- });
107
- /** Polls until `ref`'s shard lock is held by a runner that `accept`s it. */
108
- const awaitOwner = (ref, accept) => Effect.gen(function* () {
109
- const cluster = yield* ActorCluster;
110
- return yield* cluster.owner(ref).pipe(Effect.repeat({ schedule: Schedule.spaced("25 millis"), until: accept }), Effect.timeoutOrElse({
111
- duration: "30 seconds",
112
- orElse: () => Effect.die(new Error("The actor's shard did not move")),
113
- }));
114
- });
115
- /** A shard lock row with database-clock times in epoch milliseconds. */
116
- const lockOf = (runner, ref) => Effect.gen(function* () {
117
- const cluster = yield* ActorCluster;
118
- return yield* cluster.on(runner)(Effect.gen(function* () {
119
- const sql = yield* SqlClient.SqlClient;
120
- const shard = yield* (yield* InternalActors).shardId(ref);
121
- const [row] = yield* sql `
122
- SELECT address,
123
- (EXTRACT(EPOCH FROM acquired_at) * 1000)::float8 AS acquired,
124
- (EXTRACT(EPOCH FROM LOCALTIMESTAMP) * 1000)::float8 AS now
125
- FROM cluster_locks WHERE shard_id = ${shard}`.pipe(Effect.orDie);
126
- return row;
127
- }));
128
- });
129
- /**
130
- * Kills an actor's owner while a command from another runner is paused at
131
- * `point` on it, then checks that no survivor took the shard before the dead
132
- * runner's lock expired and that the caller's in-flight command, under its
133
- * original id, committed exactly once.
134
- */
135
- const killDuringTurn = (expect, point) => Effect.gen(function* () {
136
- const cluster = yield* ActorCluster;
137
- const id = `killed-${point}`;
138
- const ref = yield* refOf(id);
139
- const owner = (yield* cluster.owner(ref));
140
- const caller = (owner + 1) % 3;
141
- expect(yield* add(caller, id, 1)).toBe(1);
142
- const commandId = yield* cluster.on(caller)(Effect.gen(function* () {
143
- return yield* (yield* Actors).mintCommandId;
144
- }));
145
- const pause = yield* cluster.on(owner)(ActorTest.use((test) => test.pauseNext(point)));
146
- const retried = yield* cluster
147
- .on(caller)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Add(2).pipe(Actor.commandId(commandId)))))
148
- .pipe(Effect.forkChild);
149
- yield* pause.reached;
150
- const killed = (yield* lockOf(caller, ref)).now;
151
- yield* cluster.kill(owner);
152
- const held = yield* lockOf(caller, ref);
153
- yield* pause.release;
154
- expect(yield* inspect(caller, ref)).toMatchObject(point === "beforeCommit"
155
- ? { state: { count: 1 }, receipts: 1, events: 1 }
156
- : { state: { count: 3 }, receipts: 2, events: 2 });
157
- const taken = yield* lockOf(caller, ref).pipe(Effect.repeat({
158
- schedule: Schedule.spaced("10 millis"),
159
- until: (lock) => lock.address !== held.address,
160
- }), Effect.timeoutOrElse({
161
- duration: "30 seconds",
162
- orElse: () => Effect.die(new Error("No survivor took the dead runner's shard")),
163
- }));
164
- const next = (yield* cluster.owner(ref));
165
- expect(next === owner).toBe(false);
166
- expect(taken.acquired - held.acquired >= EXPIRATION_SECONDS * 1000).toBe(true);
167
- expect(yield* Fiber.join(retried)).toBe(3);
168
- const resumed = (yield* lockOf(next, ref)).now;
169
- expect(resumed >= taken.acquired && taken.acquired >= killed).toBe(true);
170
- expect(yield* inspect(next, ref)).toMatchObject({
171
- state: { count: 3 },
172
- receipts: 2,
173
- events: 2,
174
- });
175
- const log = yield* cluster.on(next)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Log())));
176
- expect(log.map((entry) => entry.commandId).filter((entry) => entry === commandId).length).toBe(1);
177
- });
178
- /** Multi-runner cases on three runners: placement on exactly one runner, merging of commutative calls into one turn, and retry on the next owner after a kill. */
179
- export const multiRunnerConformance = [
180
- {
181
- name: "merges commutative calls from three runners on the owner into one turn with one receipt per command id",
182
- requiresIndependentConnections: true,
183
- timeoutMs: 60_000,
184
- run: ({ expect, environment }) => withCluster(environment, 3, Effect.gen(function* () {
185
- const cluster = yield* ActorCluster;
186
- const id = "merged-ticks";
187
- const ref = yield* refOf(id);
188
- expect(yield* add(0, id, 1)).toBe(1);
189
- const owner = (yield* cluster.owner(ref));
190
- const onOwner = cluster.on(owner);
191
- const held = yield* onOwner(ActorTest.use((test) => test.pauseNext("beforeCommit")));
192
- const first = yield* add((owner + 1) % 3, id, 1).pipe(Effect.forkChild);
193
- yield* held.reached;
194
- const calls = [];
195
- const ticks = [];
196
- for (let runner = 0; runner < 3; runner++)
197
- for (let call = 0; call < 4; call++) {
198
- const commandId = yield* cluster.on(runner)(Effect.gen(function* () {
199
- return yield* (yield* Actors).mintCommandId;
200
- }));
201
- const queued = yield* onOwner(ActorTest.use((test) => test.pauseNext("queued")));
202
- ticks.push(yield* cluster
203
- .on(runner)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Tick(1).pipe(Actor.commandId(commandId)))))
204
- .pipe(Effect.forkChild));
205
- yield* queued.reached;
206
- yield* queued.release;
207
- yield* Effect.yieldNow;
208
- calls.push({ runner, commandId });
209
- }
210
- const before = tickReductions.count;
211
- yield* held.release;
212
- expect(yield* Fiber.join(first)).toBe(2);
213
- expect(yield* Effect.forEach(ticks, Fiber.join)).toEqual(calls.map(() => undefined));
214
- expect(tickReductions.count - before).toBe(1);
215
- const receipts = yield* cluster.on(owner)(Effect.gen(function* () {
216
- const sql = yield* SqlClient.SqlClient;
217
- return yield* sql `
218
- SELECT command_id, xmin::text AS tx FROM actor_receipts
219
- WHERE tenant_id = ${ref.tenant} AND actor_type = 'Tally' AND actor_id = ${ref.id}
220
- AND command = 'Tick'`;
221
- }).pipe(Effect.orDie));
222
- expect(receipts.map((row) => row.command_id).sort()).toEqual(calls.map((call) => call.commandId).sort());
223
- expect(new Set(receipts.map((row) => row.tx)).size).toBe(1);
224
- expect(yield* inspect((owner + 2) % 3, ref)).toMatchObject({
225
- state: { count: 14 },
226
- receipts: 14,
227
- });
228
- const retried = calls[5];
229
- expect(yield* cluster.on((retried.runner + 1) % 3)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Tick(1).pipe(Actor.commandId(retried.commandId)))))).toBe(undefined);
230
- expect(tickReductions.count - before).toBe(1);
231
- })),
232
- },
233
- {
234
- name: "places each actor on exactly one of three runners, reachable through every runner",
235
- requiresIndependentConnections: true,
236
- timeoutMs: 60_000,
237
- run: ({ expect, environment }) => withCluster(environment, 3, Effect.gen(function* () {
238
- const cluster = yield* ActorCluster;
239
- const ids = Array.from({ length: 24 }, (_, index) => `spread-${index}`);
240
- const owners = new Set();
241
- for (const id of ids) {
242
- yield* add(0, id, 1);
243
- const activated = (yield* inspect(0, yield* refOf(id))).generation;
244
- for (let runner = 1; runner < 3; runner++)
245
- yield* add(runner, id, 1);
246
- expect((yield* inspect(0, yield* refOf(id))).generation).toBe(activated);
247
- const owner = yield* cluster.owner(yield* refOf(id));
248
- expect(owner === undefined).toBe(false);
249
- owners.add(owner);
250
- }
251
- expect(owners.size > 1).toBe(true);
252
- for (const id of ids)
253
- expect(yield* inspect(1, yield* refOf(id))).toMatchObject({
254
- state: { count: 3 },
255
- receipts: 3,
256
- events: 3,
257
- });
258
- })),
259
- },
260
- {
261
- name: "rolls back a killed runner's paused turn and retries it once on the next owner after lock expiry",
262
- requiresIndependentConnections: true,
263
- timeoutMs: 60_000,
264
- run: ({ expect, environment }) => withCluster(environment, 3, killDuringTurn(expect, "beforeCommit")),
265
- },
266
- {
267
- name: "replays a killed runner's committed turn on the next owner instead of running it again",
268
- requiresIndependentConnections: true,
269
- timeoutMs: 60_000,
270
- run: ({ expect, environment }) => withCluster(environment, 3, killDuringTurn(expect, "afterCommit")),
271
- },
272
- {
273
- name: "fails a stale activation's fence once another runner commits for the actor, then reloads",
274
- requiresIndependentConnections: true,
275
- timeoutMs: 60_000,
276
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
277
- const cluster = yield* ActorCluster;
278
- const ref = yield* refOf("stale");
279
- const stale = (yield* cluster.owner(ref));
280
- const rival = 1 - stale;
281
- expect(yield* add(stale, "stale", 1)).toBe(1);
282
- const first = Number((yield* inspect(rival, ref)).generation);
283
- const heartbeat = yield* cluster.pauseHeartbeat(stale);
284
- yield* awaitOwner(ref, (current) => current === rival);
285
- expect(yield* add(rival, "stale", 10)).toBe(11);
286
- expect(yield* add(stale, "stale", 100)).toBe(111);
287
- yield* heartbeat.resume;
288
- const inspection = yield* inspect(rival, ref);
289
- expect(inspection).toMatchObject({ state: { count: 111 }, receipts: 3, events: 3 });
290
- expect(Number(inspection.generation) >= first + 2).toBe(true);
291
- })),
292
- },
293
- {
294
- name: "runs a brand-new actor's first command once when two runners race it under one command id",
295
- requiresIndependentConnections: true,
296
- timeoutMs: 60_000,
297
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
298
- const cluster = yield* ActorCluster;
299
- const ref = yield* refOf("brand-new");
300
- const paused = (yield* cluster.owner(ref));
301
- const rival = 1 - paused;
302
- expect((yield* inspect(rival, ref)).generation).toBe(undefined);
303
- const heartbeat = yield* cluster.pauseHeartbeat(paused);
304
- yield* awaitOwner(ref, (current) => current === rival);
305
- const id = yield* cluster.on(rival)(Effect.gen(function* () {
306
- return yield* (yield* Actors).mintCommandId;
307
- }));
308
- const before = addRuns.count;
309
- const replies = yield* Effect.forEach([paused, rival], (runner) => cluster.on(runner)(Tally.get("brand-new").pipe(Effect.flatMap((tally) => tally.Add(5).pipe(Actor.commandId(id))))), { concurrency: 2 });
310
- yield* heartbeat.resume;
311
- expect(replies).toEqual([5, 5]);
312
- expect(addRuns.count - before).toBe(1);
313
- expect(yield* inspect(rival, ref)).toMatchObject({
314
- state: { count: 5 },
315
- receipts: 1,
316
- events: 1,
317
- });
318
- })),
319
- },
320
- {
321
- name: "orders events gap-free when two real runners race for one actor",
322
- requiresIndependentConnections: true,
323
- timeoutMs: 90_000,
324
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
325
- const cluster = yield* ActorCluster;
326
- const ref = yield* refOf("raced");
327
- const paused = (yield* cluster.owner(ref));
328
- const rival = 1 - paused;
329
- yield* add(paused, "raced", 1);
330
- const first = Number((yield* inspect(rival, ref)).generation);
331
- const heartbeat = yield* cluster.pauseHeartbeat(paused);
332
- yield* awaitOwner(ref, (current) => current === rival);
333
- yield* Effect.forEach(Array.from({ length: 24 }, (_, index) => index), (index) => add(index % 2 === 0 ? paused : rival, "raced", 1), { concurrency: 8, discard: true });
334
- yield* heartbeat.resume;
335
- const log = yield* cluster.on(rival)(Tally.get("raced").pipe(Effect.flatMap((tally) => tally.Log())));
336
- expect(log.map(({ cursor }) => cursor)).toEqual(Array.from({ length: 25 }, (_, index) => String(index + 1)));
337
- expect(new Set(log.map(({ commandId }) => commandId)).size).toBe(25);
338
- const inspection = yield* inspect(rival, ref);
339
- expect(inspection).toMatchObject({ state: { count: 25 }, receipts: 25, events: 25 });
340
- expect(Number(inspection.generation) >= first + 2).toBe(true);
341
- })),
342
- },
343
- {
344
- name: "restarts a killed runner, which serves its shards again",
345
- requiresIndependentConnections: true,
346
- timeoutMs: 60_000,
347
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
348
- const cluster = yield* ActorCluster;
349
- const ref = yield* refOf("restart");
350
- const owner = (yield* cluster.owner(ref));
351
- const other = 1 - owner;
352
- expect(yield* add(other, "restart", 1)).toBe(1);
353
- yield* cluster.kill(owner);
354
- expect(yield* add(other, "restart", 1)).toBe(2);
355
- expect(yield* cluster.owner(ref)).toBe(other);
356
- yield* cluster.restart(owner);
357
- yield* cluster.ready;
358
- expect((yield* cluster.owner(ref)) === undefined).toBe(false);
359
- expect(yield* add(owner, "restart", 1)).toBe(3);
360
- expect(yield* add(other, "restart", 1)).toBe(4);
361
- expect(yield* inspect(owner, ref)).toMatchObject({ state: { count: 4 }, receipts: 4 });
362
- })),
363
- },
364
- {
365
- name: "serializes a 512-byte subject through a cross-runner command",
366
- requiresIndependentConnections: true,
367
- timeoutMs: 60_000,
368
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
369
- const cluster = yield* ActorCluster;
370
- const subject = "é".repeat(256);
371
- const ids = Array.from({ length: 16 }, (_, index) => `principal-${index}`);
372
- for (const id of ids) {
373
- yield* add(0, id, 1);
374
- const owner = yield* cluster.owner(yield* refOf(id));
375
- const caller = owner === 0 ? 1 : 0;
376
- const echoed = yield* cluster.on(caller)(Tally.get(id).pipe(Effect.flatMap((tally) => tally.Whoami()), Actor.as(User.make({ subject }))));
377
- expect(owner === 0 || owner === 1).toBe(true);
378
- expect(echoed).toBe(subject);
379
- }
380
- })),
381
- },
382
- {
383
- name: "carries the largest supported principal as onBehalfOf through a relay-delivered intent between runners",
384
- requiresIndependentConnections: true,
385
- timeoutMs: 90_000,
386
- run: ({ expect, environment }) => withCluster(environment, 2, Effect.gen(function* () {
387
- const cluster = yield* ActorCluster;
388
- const subject = "é".repeat(256);
389
- const receivers = Array.from({ length: 12 }, (_, index) => `receiver-${index}`);
390
- for (const receiver of receivers)
391
- yield* cluster.on(0)(Envoy.get("sender").pipe(Effect.flatMap((envoy) => envoy.Dispatch(receiver)), Actor.as(User.make({ subject }))));
392
- const owners = new Set();
393
- for (const receiver of receivers) {
394
- owners.add(yield* cluster.owner((yield* cluster.on(0)(Envoy.get(receiver))).ref));
395
- const heard = yield* cluster
396
- .on(1)(Envoy.get(receiver).pipe(Effect.flatMap((envoy) => envoy.Heard()), Effect.filterOrFail((value) => value !== "")))
397
- .pipe(Effect.retry({ times: 60, schedule: Schedule.spaced("500 millis") }));
398
- expect(heard).toBe(subject);
399
- }
400
- expect(owners.size).toBe(2);
401
- })),
402
- },
403
- ];
@@ -1,19 +0,0 @@
1
- /** One node of a Neki plan printed with `FORMAT TEXT`. */
2
- export interface NekiPlanNode {
3
- /** `Route`, `Collapse`, `Aggregate`, `Limit`, and any other router operator. */
4
- readonly operator: string;
5
- /** The bracketed routing choice, e.g. `EqualUnique`, `IN` or `Scatter`. */
6
- readonly kind: string | undefined;
7
- }
8
- /**
9
- * The router operators of a plan printed by `EXPLAIN (NEKI_PLAN, FORMAT TEXT)`,
10
- * in print order. Tree glyphs and indentation are dropped.
11
- */
12
- export declare const nekiPlanNodes: (plan: string) => ReadonlyArray<NekiPlanNode>;
13
- /**
14
- * Whether a plan is one route to one shard and nothing else. A router
15
- * operator above the route, a second route, a scatter, an `IN` list, and a
16
- * routing choice not known to be single-shard all fail: an unfamiliar plan is
17
- * reported, never assumed safe.
18
- */
19
- export declare const touchesOneShard: (plan: string) => boolean;
@@ -1,30 +0,0 @@
1
- /**
2
- * A node line is an operator name with an optional bracketed kind. Attribute
3
- * lines such as `Query:` and `ShardGroup:` carry a colon and are not nodes.
4
- */
5
- const NODE = /^([A-Za-z][A-Za-z ]*?)(?: \[([^\]]+)\])?$/;
6
- /**
7
- * The router operators of a plan printed by `EXPLAIN (NEKI_PLAN, FORMAT TEXT)`,
8
- * in print order. Tree glyphs and indentation are dropped.
9
- */
10
- export const nekiPlanNodes = (plan) => plan
11
- .split("\n")
12
- .map((line) => line.replace(/^[│├└─\s]+/u, "").trim())
13
- .flatMap((line) => {
14
- const match = NODE.exec(line);
15
- return match === null || line.includes(":") ? [] : [{ operator: match[1], kind: match[2] }];
16
- });
17
- /** The routing choices that Neki documents as reaching exactly one shard. */
18
- const SINGLE_SHARD_ROUTES = new Set(["EqualUnique"]);
19
- /**
20
- * Whether a plan is one route to one shard and nothing else. A router
21
- * operator above the route, a second route, a scatter, an `IN` list, and a
22
- * routing choice not known to be single-shard all fail: an unfamiliar plan is
23
- * reported, never assumed safe.
24
- */
25
- export const touchesOneShard = (plan) => {
26
- const nodes = nekiPlanNodes(plan);
27
- return (nodes.length === 1 &&
28
- nodes[0].operator === "Route" &&
29
- SINGLE_SHARD_ROUTES.has(nodes[0].kind ?? ""));
30
- };
@@ -1,3 +0,0 @@
1
- import type { ConformanceCase } from "../conformance.ts";
2
- /** Observability cases: turn and admission spans, defect logs, and metrics correlate with the command id. */
3
- export declare const observabilityConformance: ReadonlyArray<ConformanceCase>;