@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
@@ -0,0 +1,65 @@
1
+ import { type Crypto, Layer, Schema } from "effect";
2
+ import type { SqlClient } from "effect/sql";
3
+ import type { layer } from "./layer.ts";
4
+ export declare const TypeId: unique symbol;
5
+ /** What `Actors.layer` provides: the actor runtime every handler layer registers with. */
6
+ type RuntimeServices = Layer.Success<ReturnType<typeof layer>>;
7
+ /**
8
+ * Every service a hosted app's layer may require. The host builds
9
+ * `Actors.layer` over its own database and provides it with the SQL client and
10
+ * `Crypto`, so an app never constructs a runtime and a requirement outside
11
+ * this union is a compile error at `App.make`.
12
+ */
13
+ export type AppServices = RuntimeServices | SqlClient.SqlClient | Crypto.Crypto;
14
+ declare const AppLayerFailed_base: Schema.Class<AppLayerFailed, Schema.TaggedStruct<"AppLayerFailed", {
15
+ readonly cause: Schema.Defect;
16
+ }>, import("effect/Cause").YieldableError>;
17
+ /** Building the app's layer failed; `cause` is what the layer failed with. */
18
+ export declare class AppLayerFailed extends AppLayerFailed_base {
19
+ }
20
+ /**
21
+ * An actor definition as an app declares it: its name, which `Actors.serve`
22
+ * routes by, and its declared members, which `checkWorkflows` compares with
23
+ * stored executions before a rollout.
24
+ */
25
+ export interface AppActor {
26
+ readonly name: string;
27
+ readonly api: object;
28
+ }
29
+ /**
30
+ * A hosted app: the actor definitions to serve and one layer that registers
31
+ * their handlers and builds any services they need from `AppServices` alone.
32
+ */
33
+ export interface App {
34
+ readonly [TypeId]: typeof TypeId;
35
+ readonly actors: ReadonlyArray<AppActor>;
36
+ readonly layer: Layer.Layer<never, AppLayerFailed, AppServices>;
37
+ }
38
+ /**
39
+ * Declares a hosted app, the default export of `src/app.ts`. Any handler
40
+ * requirement the layer leaves unprovided fails to compile here, which is why
41
+ * the host accepts only values this constructor branded: a layer's
42
+ * requirements cannot be checked at run time. A failure building the layer
43
+ * becomes `AppLayerFailed`, so a host that loads the app knows its error type.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * export default App.make({ actors: [Counter], layer: CounterLive })
48
+ * ```
49
+ */
50
+ declare const make: <E = never>(app: {
51
+ readonly actors: ReadonlyArray<AppActor>;
52
+ readonly layer: Layer.Layer<never, E, AppServices>;
53
+ }) => App;
54
+ /**
55
+ * Whether `value` is an app `App.make` built: branded, with actors that each
56
+ * have a name and an `api` object, and a layer. A loader uses it on a module's default export before providing the
57
+ * layer.
58
+ */
59
+ declare const is: (value: unknown) => value is App;
60
+ /** Hosted app declaration and the loader's check of a module's default export. */
61
+ export declare const App: {
62
+ make: typeof make;
63
+ is: typeof is;
64
+ };
65
+ export {};
@@ -0,0 +1,38 @@
1
+ import { Effect, Layer, Predicate, Schema } from "effect";
2
+ export const TypeId = Symbol.for("@rikalabs/akter/App");
3
+ /** Building the app's layer failed; `cause` is what the layer failed with. */
4
+ export class AppLayerFailed extends Schema.TaggedError()("AppLayerFailed", {
5
+ cause: Schema.Defect(),
6
+ }) {
7
+ }
8
+ const DeclaredActors = Schema.Array(Schema.Struct({ name: Schema.String, api: Schema.ObjectKeyword }));
9
+ /**
10
+ * Declares a hosted app, the default export of `src/app.ts`. Any handler
11
+ * requirement the layer leaves unprovided fails to compile here, which is why
12
+ * the host accepts only values this constructor branded: a layer's
13
+ * requirements cannot be checked at run time. A failure building the layer
14
+ * becomes `AppLayerFailed`, so a host that loads the app knows its error type.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * export default App.make({ actors: [Counter], layer: CounterLive })
19
+ * ```
20
+ */
21
+ const make = (app) => ({
22
+ [TypeId]: TypeId,
23
+ actors: [...app.actors],
24
+ layer: Layer.catch(app.layer, (cause) => Layer.effectDiscard(Effect.fail(AppLayerFailed.make({ cause })))),
25
+ });
26
+ /**
27
+ * Whether `value` is an app `App.make` built: branded, with actors that each
28
+ * have a name and an `api` object, and a layer. A loader uses it on a module's default export before providing the
29
+ * layer.
30
+ */
31
+ const is = (value) => Predicate.hasProperty(value, TypeId) &&
32
+ value[TypeId] === TypeId &&
33
+ Predicate.hasProperty(value, "actors") &&
34
+ Schema.is(DeclaredActors)(value.actors) &&
35
+ Predicate.hasProperty(value, "layer") &&
36
+ Layer.isLayer(value.layer);
37
+ /** Hosted app declaration and the loader's check of a module's default export. */
38
+ export const App = { make, is };
@@ -213,9 +213,19 @@ export declare const connectionHolder: (options: HolderOptions) => Effect.Effect
213
213
  }, ActorError | OpenRejected, never>;
214
214
  deliver: (message: Deliver) => Effect.Effect<Delivered>;
215
215
  size: () => number;
216
+ /** The open sessions this holder holds for `tenant`: each one's actor, member and feed tags. */
217
+ census: (tenant: string) => {
218
+ ref: {
219
+ readonly tenant: string;
220
+ readonly actor: string;
221
+ readonly id: string;
222
+ };
223
+ member: string;
224
+ feed: readonly string[];
225
+ }[];
216
226
  /** The clock authorization and credential expiry are measured on, in epoch milliseconds. */
217
227
  now: Effect.Effect<number, never, never>;
218
- }, import("effect/sql/SqlError").SqlError, Crypto.Crypto | Scope.Scope | SqlClient.SqlClient>;
228
+ }, never, Crypto.Crypto | Scope.Scope | SqlClient.SqlClient>;
219
229
  /** The holder service `connectionHolder` builds. */
220
230
  export type Holder = Effect.Success<ReturnType<typeof connectionHolder>>;
221
231
  export type { Scope };
@@ -3,7 +3,7 @@ import { SqlClient } from "effect/sql";
3
3
  import { ActorError, ActorUnavailable, SessionEnded, Unauthorized } from "../../errors/actor.js";
4
4
  import { connectionSecret } from "../../identity/connection.js";
5
5
  import { FrameworkClock } from "../turn/admission.js";
6
- import { shardClients } from "../database/shards.js";
6
+ import { locatedHere, shardRanges } from "../database/shards.js";
7
7
  import { ClientMessage, HolderItem, isWatchMember, watchedQuery, } from "./protocol.js";
8
8
  /** The largest encoded inbound frame or open params a holder accepts. */
9
9
  export const MAX_INBOUND_BYTES = 65_536;
@@ -87,7 +87,7 @@ const ended = (cause, resync, retryAfterMs) => ActorError.make({
87
87
  */
88
88
  export const connectionHolder = Effect.fnUntraced(function* (options) {
89
89
  const sql = yield* SqlClient.SqlClient;
90
- const shards = yield* shardClients;
90
+ const located = yield* locatedHere;
91
91
  const crypto = yield* Crypto.Crypto;
92
92
  const clock = yield* FrameworkClock;
93
93
  const scope = yield* Effect.scope;
@@ -129,7 +129,7 @@ export const connectionHolder = Effect.fnUntraced(function* (options) {
129
129
  const deleteRow = (connection) => sql `DELETE FROM actor_connections WHERE routing_key = ${connection.type.routingKey(connection.ref)}
130
130
  AND tenant_id = ${connection.ref.tenant} AND actor_type = ${connection.ref.actor}
131
131
  AND actor_id = ${connection.ref.id} AND connection_id = ${connection.id}
132
- AND holder_epoch = ${options.transport().epoch}`.pipe(Effect.ignore);
132
+ AND holder_epoch = ${options.transport().epoch}`.pipe(located, Effect.ignore);
133
133
  const release = (connection) => {
134
134
  heldBytes -= connection.outBytes + connection.inBytes + (connection.resync?.deferredBytes ?? 0);
135
135
  if (connection.resync !== undefined)
@@ -524,7 +524,7 @@ export const connectionHolder = Effect.fnUntraced(function* (options) {
524
524
  const liveness = (at) => Effect.gen(function* () {
525
525
  const transport = options.transport();
526
526
  const checked = [...held.values()].filter((connection) => connection.open);
527
- const rows = (yield* Effect.forEach(shards, ({ sql, range }) => sql `
527
+ const rows = (yield* Effect.forEach(yield* shardRanges, (range) => sql `
528
528
  SELECT c.connection_id
529
529
  FROM generate_series(${range.first}::int, ${range.last}::int) AS b(bucket)
530
530
  CROSS JOIN LATERAL (
@@ -810,6 +810,10 @@ export const connectionHolder = Effect.fnUntraced(function* (options) {
810
810
  open,
811
811
  deliver,
812
812
  size: () => held.size,
813
+ /** The open sessions this holder holds for `tenant`: each one's actor, member and feed tags. */
814
+ census: (tenant) => [...held.values()].flatMap((connection) => connection.open && !connection.ended && connection.ref.tenant === tenant
815
+ ? [{ ref: connection.ref, member: connection.member, feed: connection.feed ?? [] }]
816
+ : []),
813
817
  /** The clock authorization and credential expiry are measured on, in epoch milliseconds. */
814
818
  now,
815
819
  };
@@ -45,14 +45,14 @@ export declare const streamSubscriptions: ({ registration, activations, transpor
45
45
  readonly input: string;
46
46
  readonly authorizedUntil: number;
47
47
  }) => Stream.Stream<{
48
+ readonly _tag: "Started";
49
+ readonly owner: string;
50
+ readonly ownerEpoch: string;
51
+ } | {
48
52
  readonly _tag: "Done";
49
53
  } | {
50
54
  readonly _tag: "Element";
51
55
  readonly value: string;
52
- } | {
53
- readonly _tag: "Started";
54
- readonly owner: string;
55
- readonly ownerEpoch: string;
56
56
  }, ActorError | {
57
57
  readonly _tag: "StreamFailed";
58
58
  readonly value: string;
@@ -3,6 +3,7 @@ import { sha256Bytes, sha256Hasher } from "../../identity/digest.js";
3
3
  import { SqlClient } from "effect/sql";
4
4
  import { ContentTooLarge } from "../../errors/content.js";
5
5
  import { tenantRoutingKey } from "../storage/codec.js";
6
+ import { forEachRange, withinRange } from "../database/shards.js";
6
7
  import { textArray } from "../turn/outbox.js";
7
8
  import { GRANT_LIFETIME_MS } from "./grant.js";
8
9
  /** Bytes one content may hold. */
@@ -157,9 +158,9 @@ export const tenantContent = (settings) => {
157
158
  const key = tenantRoutingKey(tenant);
158
159
  yield* row.inserted
159
160
  ? sql `UPDATE tenant_content_chunks SET hash = ${hash}
160
- WHERE routing_key = ${key} AND tenant_id = ${tenant} AND hash = ${pending}`
161
+ WHERE routing_key = ${key} AND tenant_id = ${tenant} AND hash = ${pending}`
161
162
  : sql `DELETE FROM tenant_content_chunks
162
- WHERE routing_key = ${key} AND tenant_id = ${tenant} AND hash = ${pending}`;
163
+ WHERE routing_key = ${key} AND tenant_id = ${tenant} AND hash = ${pending}`;
163
164
  return { hash, size, expiresAt: row.expiresAt };
164
165
  }));
165
166
  return yield* refOf(tenant, stored.hash, stored.size, stored.expiresAt);
@@ -233,16 +234,17 @@ export const tenantContent = (settings) => {
233
234
  const sweep = Effect.fnUntraced(function* (force) {
234
235
  const sql = yield* SqlClient.SqlClient;
235
236
  const started = yield* now;
236
- const tenants = force
237
- ? yield* sql `
238
- SELECT routing_key::text AS routing_key, tenant_id FROM tenant_content_sweeps`
239
- : yield* sql `
237
+ const tenants = (yield* forEachRange((range) => force
238
+ ? sql `
239
+ SELECT routing_key::text AS routing_key, tenant_id FROM tenant_content_sweeps
240
+ ${withinRange({ sql, range, column: "routing_key", keyword: "WHERE" })}`
241
+ : sql `
240
242
  UPDATE tenant_content_sweeps SET swept_at_ms = ${started}
241
243
  WHERE (routing_key, tenant_id) IN (
242
244
  SELECT routing_key, tenant_id FROM tenant_content_sweeps
243
- WHERE swept_at_ms <= ${started - SWEEP_INTERVAL_MS}
245
+ WHERE swept_at_ms <= ${started - SWEEP_INTERVAL_MS} ${withinRange({ sql, range, column: "routing_key" })}
244
246
  LIMIT ${SWEEP_TENANTS} FOR UPDATE SKIP LOCKED)
245
- RETURNING routing_key::text AS routing_key, tenant_id`;
247
+ RETURNING routing_key::text AS routing_key, tenant_id`)).flat();
246
248
  const [longest] = yield* sql `
247
249
  SELECT COALESCE(max(turn_ms), 0)::float8 AS turn_ms FROM actor_content_types`;
248
250
  const turnMs = longest.turn_ms;
@@ -264,10 +266,12 @@ export const tenantContent = (settings) => {
264
266
  break;
265
267
  const last = candidates.at(-1);
266
268
  after = { granted: Number(last.granted_until_ms), hash: last.hash };
267
- const referenced = new Set((yield* sql `
269
+ const referenced = new Set((yield* forEachRange((range) => sql `
268
270
  SELECT DISTINCT hash FROM actor_content_refs
269
- WHERE tenant_id = ${tenant}
270
- AND hash = ANY(${textArray({ sql, values: candidates.map((row) => row.hash) })})`).map((row) => row.hash));
271
+ WHERE tenant_id = ${tenant} ${withinRange({ sql, range, column: "routing_key" })}
272
+ AND hash = ANY(${textArray({ sql, values: candidates.map((row) => row.hash) })})`))
273
+ .flat()
274
+ .map((row) => row.hash));
271
275
  yield* hooks.at("afterReferenceScan");
272
276
  const unreferenced = candidates
273
277
  .map((row) => row.hash)
@@ -1,5 +1,6 @@
1
1
  import { PgClient } from "@effect/sql-pg";
2
- import { Effect } from "effect";
2
+ import type { PgConnection } from "@effect/sql-pg";
3
+ import { Effect, Scope } from "effect";
3
4
  import { SqlClient, SqlError, Statement } from "effect/sql";
4
5
  /** Extra checkouts each pool holds beyond its connections before refusing further work. */
5
6
  export declare const POOL_WAITERS = 64;
@@ -16,28 +17,13 @@ export declare const retryPoolRefusal: <A, E, R>(effect: Effect.Effect<A, E, R>)
16
17
  * transactions, streams and listeners; bounding only command dispatch would
17
18
  * leave queries and background callers able to grow the pool's waiter list.
18
19
  * A refused checkout sent no statement.
19
- *
20
- * A checkout passes three stages in order: the bounded admission slot,
21
- * refused at once past the limit; a first-come, first-served gate with one
22
- * slot per connection, so a caller never waits behind one that asked after
23
- * it, which Effect's pool allows because it hands a freed connection to
24
- * whichever fiber asks next; then the pool, which therefore never has a
25
- * waiter of its own. Both slots belong to the checkout's scope, or to the
26
- * borrowed statement, and return with the connection, so cancellation never
27
- * leaks either. A statement inside a transaction reuses its connection and
28
- * takes neither.
29
- *
30
- * Apart from the gates it is `PgClient.make`: the same compiler, transforms,
31
- * span attributes, commit check, savepoint release, JSON fragments, and
32
- * notifications, which use a checkout of their own rather than the caller's
33
- * transaction.
34
20
  */
35
21
  export declare const boundedPool: (options: PgClient.PgPoolConfig) => Effect.Effect<SqlClient.SqlClient & {
36
22
  "~@effect/sql-pg/PgClient": "~@effect/sql-pg/PgClient";
37
23
  config: PgClient.PgPoolConfig;
38
24
  json: (value: unknown) => Statement.Fragment;
39
- listen: (channel: string) => Effect.Effect<import("effect/Queue").Dequeue<import("@effect/sql-pg/PgConnection").Notification, SqlError.SqlError>, SqlError.SqlError, import("effect/Scope").Scope>;
25
+ listen: (channel: string) => Effect.Effect<import("effect/Queue").Dequeue<PgConnection.Notification, SqlError.SqlError>, SqlError.SqlError, Scope.Scope>;
40
26
  notify: (channel: string, payload: string) => Effect.Effect<void, SqlError.SqlError, never>;
41
- }, SqlError.SqlError, import("effect/reactivity/Reactivity").Reactivity | import("effect/Scope").Scope>;
27
+ }, SqlError.SqlError, import("effect/reactivity/Reactivity").Reactivity | Scope.Scope>;
42
28
  /** Provides the bounded primary/off-turn client without changing its connection configuration. */
43
29
  export declare const boundedLayer: (options: PgClient.PgPoolConfig) => import("effect/Layer").Layer<PgClient.PgClient | SqlClient.SqlClient, SqlError.SqlError, never>;
@@ -1,5 +1,5 @@
1
1
  import { PgClient, PgPool } from "@effect/sql-pg";
2
- import { Duration, Effect, Schedule, Schema } from "effect";
2
+ import { Duration, Effect, Schedule, Schema, Scope } from "effect";
3
3
  import { SqlClient, SqlError, Statement } from "effect/sql";
4
4
  import { admissionLimit, isOverloaded } from "../admission.js";
5
5
  import { ActorError } from "../../errors/actor.js";
@@ -33,13 +33,6 @@ const commit = (connection) => Effect.flatMap(connection.executeRaw("COMMIT", []
33
33
  const PgJson = Statement.custom("PgJson");
34
34
  const direct = (statement) => statement;
35
35
  /**
36
- * The native Postgres client with at most 64 queued checkouts beyond its
37
- * connection capacity, handing its connections out first come, first served.
38
- * All SQL entry points share admission, including reservations,
39
- * transactions, streams and listeners; bounding only command dispatch would
40
- * leave queries and background callers able to grow the pool's waiter list.
41
- * A refused checkout sent no statement.
42
- *
43
36
  * A checkout passes three stages in order: the bounded admission slot,
44
37
  * refused at once past the limit; a first-come, first-served gate with one
45
38
  * slot per connection, so a caller never waits behind one that asked after
@@ -47,28 +40,38 @@ const direct = (statement) => statement;
47
40
  * whichever fiber asks next; then the pool, which therefore never has a
48
41
  * waiter of its own. Both slots belong to the checkout's scope, or to the
49
42
  * borrowed statement, and return with the connection, so cancellation never
50
- * leaks either. A statement inside a transaction reuses its connection and
51
- * takes neither.
52
- *
53
- * Apart from the gates it is `PgClient.make`: the same compiler, transforms,
54
- * span attributes, commit check, savepoint release, JSON fragments, and
55
- * notifications, which use a checkout of their own rather than the caller's
56
- * transaction.
43
+ * leaks either.
57
44
  */
58
- export const boundedPool = Effect.fnUntraced(function* (options) {
45
+ const boundedSessions = Effect.fnUntraced(function* (options) {
59
46
  const pool = yield* PgPool.make(options);
60
47
  const slots = options.maxConnections ?? 10;
61
48
  const admission = admissionLimit({ limit: slots + POOL_WAITERS, wait: Duration.zero });
62
49
  const fair = fairGate(slots);
63
50
  const enter = Effect.andThen(admission.take.pipe(Effect.mapError(poolRefusal)), fair.take);
64
51
  const connection = (session) => asSqlConnection({ connection: session, send: direct });
65
- const acquire = Effect.andThen(enter, Effect.map(pool.get, connection));
66
- const sql = yield* SqlClient.make({
67
- acquirer: acquire,
68
- transactionAcquirer: Effect.andThen(enter, Effect.map(pool.reserve, connection)),
69
- borrower: (use) => admission
52
+ return {
53
+ acquire: Effect.andThen(enter, Effect.map(pool.get, connection)),
54
+ reserve: Effect.andThen(enter, Effect.map(pool.reserve, connection)),
55
+ borrow: (use) => admission
70
56
  .admit(fair.use(pool.use((session) => use(connection(session)))))
71
57
  .pipe(Effect.mapError((error) => (Schema.is(ActorError)(error) ? poolRefusal(error) : error))),
58
+ listen: (channel) => Effect.andThen(enter, Effect.flatMap(pool.reserve, (session) => session.listen(channel))),
59
+ };
60
+ });
61
+ /**
62
+ * `PgClient.make` over `sessions`: the same compiler, transforms, span
63
+ * attributes, commit check, savepoint release, JSON fragments, and
64
+ * notifications, which use a checkout of their own rather than the caller's
65
+ * transaction. `sessions` is read at each checkout, so one client can reach
66
+ * a different pool per caller. A statement inside a transaction reuses its
67
+ * connection and takes no checkout.
68
+ */
69
+ const clientOver = Effect.fnUntraced(function* (options, sessions) {
70
+ const acquire = Effect.flatMap(sessions, (pool) => pool.acquire);
71
+ const sql = yield* SqlClient.make({
72
+ acquirer: acquire,
73
+ transactionAcquirer: Effect.flatMap(sessions, (pool) => pool.reserve),
74
+ borrower: (use) => Effect.flatMap(sessions, (pool) => pool.borrow(use)),
72
75
  compiler: PgClient.makeCompiler(options.transformQueryNames, options.transformJson),
73
76
  spanAttributes: [
74
77
  ...(options.spanAttributes === undefined ? [] : Object.entries(options.spanAttributes)),
@@ -88,7 +91,7 @@ export const boundedPool = Effect.fnUntraced(function* (options) {
88
91
  [PgClient.TypeId]: PgClient.TypeId,
89
92
  config: options,
90
93
  json: ((value) => Statement.fragment([PgJson(value)])),
91
- listen: (channel) => Effect.andThen(enter, Effect.flatMap(pool.reserve, (session) => session.listen(channel))),
94
+ listen: (channel) => Effect.flatMap(sessions, (pool) => pool.listen(channel)),
92
95
  notify: (channel, payload) => {
93
96
  if (new TextEncoder().encode(channel).byteLength > 63) {
94
97
  const message = "PostgreSQL channel names must not exceed 63 UTF-8 bytes";
@@ -104,5 +107,16 @@ export const boundedPool = Effect.fnUntraced(function* (options) {
104
107
  },
105
108
  });
106
109
  });
110
+ /**
111
+ * The native Postgres client with at most 64 queued checkouts beyond its
112
+ * connection capacity, handing its connections out first come, first served.
113
+ * All SQL entry points share admission, including reservations,
114
+ * transactions, streams and listeners; bounding only command dispatch would
115
+ * leave queries and background callers able to grow the pool's waiter list.
116
+ * A refused checkout sent no statement.
117
+ */
118
+ export const boundedPool = Effect.fnUntraced(function* (options) {
119
+ return yield* clientOver(options, Effect.succeed(yield* boundedSessions(options)));
120
+ });
107
121
  /** Provides the bounded primary/off-turn client without changing its connection configuration. */
108
122
  export const boundedLayer = (options) => PgClient.layerFrom(boundedPool(options));
@@ -1,8 +1,14 @@
1
1
  import { PgClient } from "@effect/sql-pg";
2
- import { Context, Effect, Layer } from "effect";
2
+ import { Context, Effect, Layer, type Scope } from "effect";
3
3
  import { SqlClient } from "effect/sql";
4
- /** The deployment's unsharded authority, shared by every runner regardless of its data shard. */
4
+ /** The deployment's coordination database, shared by every runner. */
5
5
  export declare const Coordination: Context.Reference<SqlClient.SqlClient | undefined>;
6
+ /** Deployment registries belong to the data database. */
7
+ export declare const registry: Context.Service<SqlClient.SqlClient, SqlClient.SqlClient>;
8
+ /** The client deployment-wide locks are taken on: the designated coordination pool, else `registry`. */
9
+ export declare const authority: Effect.Effect<SqlClient.SqlClient, never, SqlClient.SqlClient>;
10
+ /** Holds startup coordination before even the history table is created, on one leased session. */
11
+ export declare const withMigrationCoordination: <A, E>(effect: Effect.Effect<A, E, SqlClient.SqlClient | Scope.Scope>) => Effect.Effect<A, E | import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
6
12
  /** Supplies an independent client so coordination transactions never borrow a data transaction's connection. */
7
13
  export declare const coordinationLayer: (options: PgClient.PgPoolConfig | undefined) => Layer.Layer<never, import("effect/sql/SqlError").SqlError, never>;
8
14
  /**
@@ -12,8 +18,11 @@ export declare const coordinationLayer: (options: PgClient.PgPoolConfig | undefi
12
18
  * Row identities are retained so deleting an idle resource cannot split its lock.
13
19
  * The local fence has its own namespace because independent clients may point
14
20
  * to the same database and must not wait on their own authority transaction.
21
+ * Work that writes no actor data, such as accepting a deployment, passes
22
+ * `writesData: false` and holds no data transaction.
15
23
  */
16
- export declare const coordinated: <A, E, R>({ resource, work, }: {
24
+ export declare const coordinated: <A, E, R>({ resource, work, writesData, }: {
17
25
  readonly resource: string;
18
26
  readonly work: Effect.Effect<A, E, R>;
27
+ readonly writesData?: boolean;
19
28
  }) => Effect.Effect<A, E | import("effect/sql/SqlError").SqlError, R | SqlClient.SqlClient>;
@@ -3,10 +3,12 @@ import { Context, Effect, Layer } from "effect";
3
3
  import { Reactivity } from "effect/reactivity";
4
4
  import { Migrator, SqlClient } from "effect/sql";
5
5
  import { boundedPool } from "./bounded.js";
6
- import { NekiTurnSessions } from "./neki/session.js";
7
- import { MigrationBarrier, MigrationBoundary, withMigrationCoordination, } from "./neki/migrations.js";
8
- /** The deployment's unsharded authority, shared by every runner regardless of its data shard. */
6
+ /** The deployment's coordination database, shared by every runner. */
9
7
  export const Coordination = Context.Reference("@rikalabs/akter/runtime/database/coordination/Coordination", { defaultValue: () => undefined });
8
+ /** Deployment registries belong to the data database. */
9
+ export const registry = SqlClient.SqlClient;
10
+ /** The client deployment-wide locks are taken on: the designated coordination pool, else `registry`. */
11
+ export const authority = Coordination.pipe(Effect.filterOrElse((designated) => designated !== undefined, () => registry));
10
12
  /** Creates the transaction-owned coordination rows on the authoritative database. */
11
13
  const prepareCoordination = Effect.gen(function* () {
12
14
  const sql = yield* SqlClient.SqlClient;
@@ -14,41 +16,30 @@ const prepareCoordination = Effect.gen(function* () {
14
16
  resource text PRIMARY KEY
15
17
  )`;
16
18
  });
17
- /** Fixed, existence-guarded bootstrap steps can be replayed after any interrupted propagation. */
18
- const prepareNekiCoordination = Effect.gen(function* () {
19
- const sql = yield* SqlClient.SqlClient;
20
- const connection = yield* sql.reserve;
21
- const barrier = yield* MigrationBarrier;
22
- const boundary = yield* MigrationBoundary;
23
- for (const [name, ddl] of [
24
- [
25
- "history",
26
- "CREATE TABLE IF NOT EXISTS actor_coordination_migrations (migration_id integer PRIMARY KEY, created_at timestamptz NOT NULL DEFAULT now(), name text NOT NULL)",
27
- ],
28
- ["resources", "CREATE TABLE IF NOT EXISTS actor_coordination (resource text PRIMARY KEY)"],
29
- ]) {
30
- yield* barrier(connection);
31
- yield* sql.unsafe(ddl);
32
- yield* boundary(`coordination:${name}:ddl`);
33
- yield* barrier(connection);
34
- yield* boundary(`coordination:${name}:propagated`);
35
- }
36
- yield* sql `INSERT INTO actor_coordination_migrations (migration_id, name)
37
- VALUES (1, 'coordination') ON CONFLICT (migration_id) DO NOTHING`;
38
- yield* boundary("coordination:recorded");
39
- });
19
+ /** Holds startup coordination before even the history table is created, on one leased session. */
20
+ export const withMigrationCoordination = (effect) => Effect.scoped(Effect.gen(function* () {
21
+ const source = yield* SqlClient.SqlClient;
22
+ const connection = yield* source.reserve;
23
+ yield* Effect.acquireRelease(connection.execute("SELECT pg_advisory_lock(1935764837, 487)", [], undefined), () => connection
24
+ .execute("SELECT pg_advisory_unlock(1935764837, 487)", [], undefined)
25
+ .pipe(Effect.orDie));
26
+ const reactivity = yield* Reactivity.make;
27
+ const client = yield* SqlClient.make({
28
+ acquirer: Effect.succeed(connection),
29
+ compiler: PgClient.makeCompiler(),
30
+ spanAttributes: [],
31
+ }).pipe(Effect.provideService(Reactivity.Reactivity, reactivity));
32
+ return yield* Effect.provideService(effect, SqlClient.SqlClient, client);
33
+ }));
40
34
  /** Supplies an independent client so coordination transactions never borrow a data transaction's connection. */
41
35
  export const coordinationLayer = (options) => Layer.effect(Coordination, Effect.gen(function* () {
42
36
  if (options === undefined)
43
37
  return undefined;
44
38
  const sql = yield* boundedPool(options);
45
- const neki = yield* NekiTurnSessions;
46
- yield* withMigrationCoordination(neki
47
- ? prepareNekiCoordination
48
- : Migrator.make({})({
49
- table: "actor_coordination_migrations",
50
- loader: Migrator.fromRecord({ "0001_coordination": prepareCoordination }),
51
- })).pipe(Effect.provideService(SqlClient.SqlClient, sql), Effect.orDie);
39
+ yield* withMigrationCoordination(Migrator.make({})({
40
+ table: "actor_coordination_migrations",
41
+ loader: Migrator.fromRecord({ "0001_coordination": prepareCoordination }),
42
+ })).pipe(Effect.provideService(SqlClient.SqlClient, sql), Effect.orDie);
52
43
  return sql;
53
44
  })).pipe(Layer.provide(Reactivity.layer));
54
45
  /**
@@ -58,11 +49,13 @@ export const coordinationLayer = (options) => Layer.effect(Coordination, Effect.
58
49
  * Row identities are retained so deleting an idle resource cannot split its lock.
59
50
  * The local fence has its own namespace because independent clients may point
60
51
  * to the same database and must not wait on their own authority transaction.
52
+ * Work that writes no actor data, such as accepting a deployment, passes
53
+ * `writesData: false` and holds no data transaction.
61
54
  */
62
- export const coordinated = ({ resource, work, }) => Effect.gen(function* () {
55
+ export const coordinated = ({ resource, work, writesData = true, }) => Effect.gen(function* () {
63
56
  const data = yield* SqlClient.SqlClient;
64
- const sql = (yield* Coordination) ?? data;
65
- const guarded = sql === data
57
+ const sql = yield* authority;
58
+ const guarded = sql === data || !writesData
66
59
  ? work
67
60
  : data.withTransaction(data `INSERT INTO actor_coordination (resource) VALUES (${`local/${resource}`})
68
61
  ON CONFLICT (resource) DO UPDATE SET resource = EXCLUDED.resource`.pipe(Effect.andThen(work), Effect.tap(() => sql `SELECT 1`)));