@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,13 @@
1
+ import { Effect } from "effect";
2
+ import { dual } from "effect/Function";
3
+ import { SqlClient } from "effect/sql";
4
+ /**
5
+ * Runs an application's own schema setup on the ambient `SqlClient` while
6
+ * holding advisory lock `lock`, so processes starting together apply it one
7
+ * at a time. The lock and every statement share one transaction, so a failed
8
+ * setup rolls back together and releases its lock.
9
+ */
10
+ export const schemaChange = dual(2, (effect, lock) => Effect.gen(function* () {
11
+ const sql = yield* SqlClient.SqlClient;
12
+ return yield* sql.withTransaction(Effect.andThen(sql `SELECT pg_advisory_xact_lock(${lock})`, effect));
13
+ }));
@@ -1,21 +1,39 @@
1
1
  import { Context, Effect } from "effect";
2
- import { SqlClient } from "effect/sql";
3
- /** One inclusive bucket range on one database shard, independent of actor compute ownership. */
2
+ import type { SqlClient } from "effect/sql";
3
+ /** One inclusive scheduling range, independent of actor compute ownership. */
4
4
  export interface BucketRange {
5
5
  readonly first: number;
6
6
  readonly last: number;
7
- /** Neki's shard UID; absent on an ordinary single database. */
8
- readonly shard?: string | undefined;
9
7
  }
10
- /** The data ranges this runner may scan; ordinary databases have one range and no session setting. */
8
+ /**
9
+ * Bucket ranges on one database. The default covers the whole key space;
10
+ * tests partition it to verify range boundaries and shared claim capacity.
11
+ */
11
12
  export declare const ShardMap: Context.Reference<readonly BucketRange[]>;
13
+ /** Refuses ranges that overlap or leave the bucket space. */
14
+ export declare const checkRanges: (ranges: ReadonlyArray<BucketRange>) => boolean;
15
+ /** The runtime's scheduling ranges, checked before any scan runs. */
16
+ export declare const currentRanges: Effect.Effect<readonly BucketRange[], never, never>;
17
+ /** The ranges as scans read them, in bucket order. */
18
+ export declare const shardRanges: Effect.Effect<BucketRange[], never, never>;
19
+ /**
20
+ * Captures the runtime's scheduling ranges for calls from outside fibers,
21
+ * such as a holder transport's, so every scan uses the same range boundaries.
22
+ */
23
+ export declare const locatedHere: Effect.Effect<<A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, never>>, never, never>;
24
+ /**
25
+ * Runs a scan once per scheduling range. `withinRange` keeps each statement
26
+ * within its inclusive buckets so no row is visited twice.
27
+ */
28
+ export declare const forEachRange: <A, E, R>(effect: (range: BucketRange) => Effect.Effect<A, E, R>) => Effect.Effect<A[], E, R>;
12
29
  /**
13
- * Gives each targeted range its own session for the lifetime of the caller's
14
- * scope. A shard setting cannot leak into the shared off-turn pool, and a
15
- * failed setup closes the session instead of returning it to another user.
16
- * Untargeted ranges reuse the existing client without a setting or a lease.
30
+ * `AND <column> BETWEEN` the routing keys of `range`'s buckets, or nothing for
31
+ * the whole key space, so an unsharded database runs the statement unchanged.
32
+ * `WHERE` starts the predicate for a statement that has none.
17
33
  */
18
- export declare const shardClients: Effect.Effect<{
19
- range: BucketRange;
20
- sql: SqlClient.SqlClient;
21
- }[], import("effect/sql/SqlError").SqlError, import("effect/Scope").Scope | SqlClient.SqlClient>;
34
+ export declare const withinRange: ({ sql, range, column, keyword, }: {
35
+ readonly sql: SqlClient.SqlClient;
36
+ readonly range: BucketRange;
37
+ readonly column: string;
38
+ readonly keyword?: "AND" | "WHERE";
39
+ }) => import("effect/sql/Statement").Fragment;
@@ -1,37 +1,49 @@
1
- import { PgClient } from "@effect/sql-pg";
2
- import { Context, Effect, Layer } from "effect";
3
- import { SqlClient } from "effect/sql";
1
+ import { Context, Effect } from "effect";
4
2
  import { BUCKETS } from "../turn/outbox.js";
5
- /** The data ranges this runner may scan; ordinary databases have one range and no session setting. */
3
+ /**
4
+ * Bucket ranges on one database. The default covers the whole key space;
5
+ * tests partition it to verify range boundaries and shared claim capacity.
6
+ */
6
7
  export const ShardMap = Context.Reference("akter/ShardMap", {
7
8
  defaultValue: () => [BUCKETS],
8
9
  });
9
- /**
10
- * Gives each targeted range its own session for the lifetime of the caller's
11
- * scope. A shard setting cannot leak into the shared off-turn pool, and a
12
- * failed setup closes the session instead of returning it to another user.
13
- * Untargeted ranges reuse the existing client without a setting or a lease.
14
- */
15
- export const shardClients = Effect.gen(function* () {
16
- const sql = yield* SqlClient.SqlClient;
17
- const ranges = yield* ShardMap;
10
+ /** Refuses ranges that overlap or leave the bucket space. */
11
+ export const checkRanges = (ranges) => {
18
12
  const sorted = [...ranges].sort((a, b) => a.first - b.first);
19
- for (const [index, range] of sorted.entries()) {
13
+ for (const [index, range] of sorted.entries())
20
14
  if (!Number.isInteger(range.first) ||
21
15
  !Number.isInteger(range.last) ||
22
16
  range.first < BUCKETS.first ||
23
17
  range.last > BUCKETS.last ||
24
18
  range.first > range.last ||
25
- (index > 0 && sorted[index - 1].last >= range.first) ||
26
- range.shard === "")
27
- return yield* Effect.die(new Error("Invalid or overlapping shard bucket ranges"));
28
- }
29
- return yield* Effect.forEach(ranges, (range) => Effect.gen(function* () {
30
- if (range.shard === undefined)
31
- return { range, sql };
32
- const postgres = yield* PgClient.PgClient;
33
- const client = Context.get(yield* Layer.build(PgClient.layerFrom(PgClient.makeClient(postgres.config))), SqlClient.SqlClient);
34
- yield* client `SET __neki.shard = ${client.literal(`'${range.shard.replaceAll("'", "''")}'`)}`;
35
- return { range, sql: client };
36
- }));
19
+ (index > 0 && sorted[index - 1].last >= range.first))
20
+ return false;
21
+ return true;
22
+ };
23
+ /** The runtime's scheduling ranges, checked before any scan runs. */
24
+ export const currentRanges = Effect.gen(function* () {
25
+ const ranges = yield* ShardMap;
26
+ if (!checkRanges(ranges))
27
+ return yield* Effect.die(new Error("Invalid or overlapping shard bucket ranges"));
28
+ return ranges;
37
29
  });
30
+ /** The ranges as scans read them, in bucket order. */
31
+ export const shardRanges = Effect.map(currentRanges, (ranges) => [...ranges].sort((a, b) => a.first - b.first));
32
+ /**
33
+ * Captures the runtime's scheduling ranges for calls from outside fibers,
34
+ * such as a holder transport's, so every scan uses the same range boundaries.
35
+ */
36
+ export const locatedHere = Effect.map(ShardMap, (map) => (effect) => Effect.provideService(effect, ShardMap, map));
37
+ /**
38
+ * Runs a scan once per scheduling range. `withinRange` keeps each statement
39
+ * within its inclusive buckets so no row is visited twice.
40
+ */
41
+ export const forEachRange = (effect) => Effect.flatMap(shardRanges, (ranges) => Effect.forEach(ranges, effect));
42
+ /**
43
+ * `AND <column> BETWEEN` the routing keys of `range`'s buckets, or nothing for
44
+ * the whole key space, so an unsharded database runs the statement unchanged.
45
+ * `WHERE` starts the predicate for a statement that has none.
46
+ */
47
+ export const withinRange = ({ sql, range, column, keyword = "AND", }) => range.first === BUCKETS.first && range.last === BUCKETS.last
48
+ ? sql.literal("")
49
+ : sql `${sql.literal(keyword)} ${sql.literal(column)} BETWEEN ${BigInt(range.first) << 56n} AND ${(BigInt(range.last + 1) << 56n) - 1n}`;
@@ -9,6 +9,7 @@ import { ActorRef } from "../../identity/caller.ts";
9
9
  import type { Authorize } from "../connections/streams.ts";
10
10
  import type { Transport } from "../connections/transport.ts";
11
11
  import { DefectLog } from "../telemetry/defects.ts";
12
+ import type { LiveRecorder } from "../telemetry/live.ts";
12
13
  /**
13
14
  * The commands an activation holds, waiting and in its current batch, when its
14
15
  * policy declares no `mailboxCapacity`. Cluster refuses the next, which
@@ -297,7 +298,7 @@ export declare const registerActor: (registration: Registration, transport: Tran
297
298
  readonly open: boolean;
298
299
  close: Effect.Effect<void, never, never>;
299
300
  expire: Effect.Effect<number, never, never>;
300
- }, writable: Effect.Effect<void, ActorError, never>) => Effect.Effect<{
301
+ }, writable: Effect.Effect<void, ActorError, never>, recorder: () => LiveRecorder | undefined) => Effect.Effect<{
301
302
  isResident: (entityId: string) => boolean;
302
303
  owner: {
303
304
  activations: Map<string, import("../connections/owner.ts").Activation>;
@@ -419,6 +420,12 @@ export declare const registerActor: (registration: Registration, transport: Tran
419
420
  };
420
421
  /** This type's view of one activation, for a delivery that timed out. */
421
422
  diagnose: (entityId: string) => ActivationDiagnosis;
423
+ /** The activations of this type resident now, with the commands waiting in each mailbox. */
424
+ resident: () => {
425
+ tenant: string;
426
+ id: string;
427
+ mailbox: number;
428
+ }[];
422
429
  /** Activations of this type being rebuilt or restarted in place now. */
423
430
  restarting: () => string[];
424
431
  }, never, Crypto.Crypto | DefectLog | Scope.Scope | Sharding.Sharding | SqlClient.SqlClient>;
@@ -5,7 +5,7 @@ import { SqlClient, SqlError } from "effect/sql";
5
5
  import { ActorError, ActorUnavailable } from "../../errors/actor.js";
6
6
  import { DeliveryFailed, Executed, Outcome, Request } from "../request.js";
7
7
  import {} from "../members.js";
8
- import { ActorRef } from "../../identity/caller.js";
8
+ import { ActorRef, callerKey } from "../../identity/caller.js";
9
9
  import { bootstrapTicks } from "../cron/schedule.js";
10
10
  import { parentPlacement, routingKey } from "../storage/codec.js";
11
11
  import { ShardLease } from "../topology/locks.js";
@@ -158,7 +158,8 @@ const keepSingletonAwake = Effect.fnUntraced(function* (registration, entity, se
158
158
  actor: registration.name,
159
159
  id: "singleton",
160
160
  });
161
- yield* bootstrapTicks(routingKey({ ref, placement: registration.placement }), ref, registration.cron).pipe(Effect.provideContext(services), Effect.orDie);
161
+ const key = routingKey({ ref, placement: registration.placement });
162
+ yield* bootstrapTicks(key, ref, registration.cron).pipe(Effect.provideContext(services), Effect.orDie);
162
163
  const address = yield* encodeEntityId([registration.tenant, "singleton"]).pipe(Effect.orDie);
163
164
  const client = (yield* sharding.makeClient(entity))(address);
164
165
  const wakeInterval = Duration.min(SINGLETON_WAKE_INTERVAL, Duration.millis(registration.policy.idleMs / 2));
@@ -207,7 +208,9 @@ const keepSingletonAwake = Effect.fnUntraced(function* (registration, entity, se
207
208
  */
208
209
  export const registerActor = Effect.fnUntraced(function* (registration, transport, authorize, gate,
209
210
  /** Fails while this runtime may not start turns, e.g. its payload writer rows are stale. */
210
- writable) {
211
+ writable,
212
+ /** The live recorder, once the inspector enabled one. */
213
+ recorder) {
211
214
  const sharding = yield* Sharding.Sharding;
212
215
  const defects = yield* DefectLog;
213
216
  const typeAttributes = { actor_type: registration.name };
@@ -221,7 +224,8 @@ writable) {
221
224
  });
222
225
  const ownedOf = (entityId) => Effect.flatMap(Effect.orDie(decodeEntityId(entityId)), ([tenant, id]) => {
223
226
  const ref = { actor: registration.name, tenant, id };
224
- return owner.enter(entityId, ref, routingKey({ ref, placement: registration.placement }));
227
+ const key = routingKey({ ref, placement: registration.placement });
228
+ return owner.enter(entityId, ref, key);
225
229
  });
226
230
  const services = yield* Effect.context();
227
231
  const usage = Context.getUnsafe(services, UsageAccounting);
@@ -231,6 +235,7 @@ writable) {
231
235
  const workflowRoutes = workflowCommands({ registration, routingKeyOf, services });
232
236
  const waited = new Set([...registration.workflows.values()].flatMap((workflow) => [...workflow.member.registry.steps.values()].flatMap((step) => step.event === undefined ? [] : [step.event])));
233
237
  const resident = new Map();
238
+ const residentRefs = new Map();
234
239
  const building = new Set();
235
240
  const workers = new Map();
236
241
  const lease = registration.singleton
@@ -240,10 +245,15 @@ writable) {
240
245
  * Counts a handler of `entityId` as resident until its scope closes; a
241
246
  * rebuilt handler can overlap its predecessor, hence the count.
242
247
  */
243
- const residentWhile = (entityId) => Effect.acquireRelease(Effect.sync(() => resident.set(entityId, (resident.get(entityId) ?? 0) + 1)).pipe(Effect.andThen(count(Metrics.activationsStarted, typeAttributes, 1)), Effect.andThen(Metric.modify(activations, 1))), () => Effect.sync(() => {
248
+ const residentWhile = (entityId, tenant, id) => Effect.acquireRelease(Effect.sync(() => {
249
+ resident.set(entityId, (resident.get(entityId) ?? 0) + 1);
250
+ residentRefs.set(entityId, { tenant, id });
251
+ }).pipe(Effect.andThen(count(Metrics.activationsStarted, typeAttributes, 1)), Effect.andThen(Metric.modify(activations, 1))), () => Effect.sync(() => {
244
252
  const left = resident.get(entityId) - 1;
245
- if (left === 0)
253
+ if (left === 0) {
246
254
  resident.delete(entityId);
255
+ residentRefs.delete(entityId);
256
+ }
247
257
  else
248
258
  resident.set(entityId, left);
249
259
  }).pipe(Effect.andThen(Metric.modify(activations, -1))));
@@ -268,6 +278,38 @@ writable) {
268
278
  yield* count(Metrics.outboxStaged, { kind: "intent" }, done.written.intents);
269
279
  yield* count(Metrics.outboxStaged, { kind: "job" }, done.written.jobs);
270
280
  });
281
+ /**
282
+ * Hands each command of a committed batch that wrote a receipt to the live
283
+ * recorder and the tenant's command streams. A replay, a refusal and an
284
+ * acknowledged delivery wrote none. The turn's time is its transaction's on
285
+ * the database clock, from the fenced read to the clock read after `COMMIT`.
286
+ */
287
+ const recordLive = (live, batch, done, nowMs) => {
288
+ const durationMs = Math.max(0, done.endedAtMs - done.startedAtMs);
289
+ for (const [index, settled] of done.settled.entries()) {
290
+ if (Result.isFailure(settled) || done.replays.has(index))
291
+ continue;
292
+ const outcome = settled.success;
293
+ const failure = Outcome.guards.Failure(outcome) ? outcome.value : undefined;
294
+ if (!Outcome.guards.Success(outcome) && failure === undefined)
295
+ continue;
296
+ const { request } = batch[index];
297
+ const failed = failure !== undefined;
298
+ live.record(request.ref.tenant, registration.name, request.command, durationMs, nowMs);
299
+ live.publish(request.ref.tenant, nowMs, () => ({
300
+ commandId: request.commandId,
301
+ atMs: done.endedAtMs,
302
+ durationMs,
303
+ actorType: registration.name,
304
+ actorId: request.ref.id,
305
+ command: request.command,
306
+ callerKey: callerKey(request.caller),
307
+ failed,
308
+ payload: request.payload,
309
+ failure,
310
+ }));
311
+ }
312
+ };
271
313
  /**
272
314
  * Records a deterministic defect in the defect log under its turn span's
273
315
  * name and trace, and marks the span's outcome.
@@ -333,7 +375,7 @@ writable) {
333
375
  let refused;
334
376
  const handler = yield* Scope.fork(activation);
335
377
  memory.handler = handler;
336
- yield* residentWhile(entityId).pipe(Scope.provide(handler));
378
+ yield* residentWhile(entityId, tenant, id).pipe(Scope.provide(handler));
337
379
  const start = Effect.gen(function* () {
338
380
  const scope = yield* Scope.fork(handler);
339
381
  if (lease !== undefined)
@@ -437,6 +479,9 @@ writable) {
437
479
  "turn.outcome": labels[0],
438
480
  });
439
481
  yield* countWritten(done);
482
+ const live = recorder();
483
+ if (live !== undefined && done.written.receipts > 0)
484
+ recordLive(live, batch, done, yield* Clock.currentTimeMillis);
440
485
  if (owner.hasProgress) {
441
486
  for (const [index, settled] of done.settled.entries())
442
487
  if (Result.isSuccess(settled) && !Outcome.guards.Defect(settled.success))
@@ -658,6 +703,12 @@ writable) {
658
703
  building: building.has(entityId),
659
704
  ...(workers.get(entityId)?.() ?? { worker: "none", mailbox: 0, batch: 0 }),
660
705
  }),
706
+ /** The activations of this type resident now, with the commands waiting in each mailbox. */
707
+ resident: () => [...residentRefs].map(([entityId, { tenant, id }]) => ({
708
+ tenant,
709
+ id,
710
+ mailbox: workers.get(entityId)?.().mailbox ?? 0,
711
+ })),
661
712
  /** Activations of this type being rebuilt or restarted in place now. */
662
713
  restarting: () => [
663
714
  ...new Set([
@@ -54,9 +54,9 @@ export const checkFleet = Effect.fnUntraced(function* (views, tenantRole) {
54
54
  if (table === undefined)
55
55
  return yield* refuse(`${view.name} reads ${source}, which does not exist; apply its drizzle-kit migration`);
56
56
  if (!table.published)
57
- return yield* refuse(`${source} is not in publication ${FLEET_PUBLICATION}; run durable fleet setup`);
57
+ return yield* refuse(`${source} is not in publication ${FLEET_PUBLICATION}; run akter fleet setup`);
58
58
  if (table.identity !== "f")
59
- return yield* refuse(`${source} does not have full replica identity; run durable fleet setup (ALTER TABLE ${source} REPLICA IDENTITY FULL)`);
59
+ return yield* refuse(`${source} does not have full replica identity; run akter fleet setup (ALTER TABLE ${source} REPLICA IDENTITY FULL)`);
60
60
  const leading = [view.routingColumn, view.tenantColumn, ...view.groupColumns];
61
61
  const indexes = yield* sql `
62
62
  SELECT array(
@@ -88,9 +88,9 @@ export const checkFleet = Effect.fnUntraced(function* (views, tenantRole) {
88
88
  SELECT wal_status FROM pg_replication_slots
89
89
  WHERE slot_name = ${FLEET_SLOT} AND database = current_database() AND slot_type = 'logical'`;
90
90
  if (slot === undefined)
91
- return yield* refuse(`replication slot ${FLEET_SLOT} does not exist; run durable fleet setup`);
91
+ return yield* refuse(`replication slot ${FLEET_SLOT} does not exist; run akter fleet setup`);
92
92
  if (slot.wal_status === "lost")
93
- return yield* refuse(`replication slot ${FLEET_SLOT} is lost; run durable fleet setup to recreate it, and every view rebuilds`);
93
+ return yield* refuse(`replication slot ${FLEET_SLOT} is lost; run akter fleet setup to recreate it, and every view rebuilds`);
94
94
  const at = yield* Clock.currentTimeMillis;
95
95
  for (const { view, sourceSchema } of resolved) {
96
96
  yield* sql `INSERT INTO actor_fleet_views (view_name, source_schema, source_table, definition_hash,
@@ -31,7 +31,7 @@ export interface ResolvedView {
31
31
  * the two replays a batch whose recompute is idempotent. When no change is
32
32
  * waiting it rebuilds `building` and `stale` views a few tenants at a time.
33
33
  * A view whose recompute fails is marked stale with its error and skipped
34
- * until `durable fleet rebuild`, so it never stops the others. A slot that
34
+ * until `akter fleet rebuild`, so it never stops the others. A slot that
35
35
  * is missing, lost, or moved by anyone else marks every view stale. The
36
36
  * runner that cannot take the lock retries every `LOCK_RETRY`.
37
37
  */
@@ -101,7 +101,7 @@ const innermost = (error) => {
101
101
  * the two replays a batch whose recompute is idempotent. When no change is
102
102
  * waiting it rebuilds `building` and `stale` views a few tenants at a time.
103
103
  * A view whose recompute fails is marked stale with its error and skipped
104
- * until `durable fleet rebuild`, so it never stops the others. A slot that
104
+ * until `akter fleet rebuild`, so it never stops the others. A slot that
105
105
  * is missing, lost, or moved by anyone else marks every view stale. The
106
106
  * runner that cannot take the lock retries every `LOCK_RETRY`.
107
107
  */
@@ -21,6 +21,8 @@ export { ASSERTION_SKEW_MS, AssertionClaims, AssertionKey, AssertionKeySet, KeyR
21
21
  export type { AssertionOptions } from "../serve/assertion/verify.ts";
22
22
  export { actorErrorBody, closeCodeOf, statusOf } from "../protocol/wire.ts";
23
23
  export { Database } from "./layer.ts";
24
+ export { App, AppLayerFailed } from "./app.ts";
25
+ export type { AppActor, AppServices } from "./app.ts";
24
26
  export { Runner } from "./runner.ts";
25
27
  export type { SocketRunnerOptions } from "./runner.ts";
26
28
  export type { MutualTlsOptions } from "./peering/transport.ts";
@@ -10,6 +10,7 @@ export const Auth = { none, make, jwt, assertion };
10
10
  export { ASSERTION_SKEW_MS, AssertionClaims, AssertionKey, AssertionKeySet, KeyRefreshClaims, MAX_ASSERTION_SECONDS, } from "../serve/assertion/verify.js";
11
11
  export { actorErrorBody, closeCodeOf, statusOf } from "../protocol/wire.js";
12
12
  export { Database } from "./layer.js";
13
+ export { App, AppLayerFailed } from "./app.js";
13
14
  export { Runner } from "./runner.js";
14
15
  export { RunnerAuthority } from "./peering/authority.js";
15
16
  export { DataDirLocked, DataDirVersion } from "../errors/database.js";
@@ -11,6 +11,10 @@ export interface InspectorOptions<R> {
11
11
  readonly auth: AuthProvider<R>;
12
12
  /** Prefix for every route. Default `"/inspector"`. */
13
13
  readonly basePath?: `/${string}`;
14
+ /** The name live reads give this runner, such as its host or task; null in them when unset. */
15
+ readonly runner?: string | undefined;
16
+ /** The region live reads say this runner serves; null in them when unset. */
17
+ readonly region?: string | undefined;
14
18
  }
15
19
  /**
16
20
  * Serves read-only JSON over the `durable` inspection views as routes on the
@@ -37,6 +41,22 @@ export interface InspectorOptions<R> {
37
41
  * - `GET /outbox`, `/jobs`, `/dead-letters?afterDeadAtMs&afterJobId`,
38
42
  * `/workflows?status=open|all|running|suspended|finished|completed|failed&afterStartedAtMs&afterExecutionId`:
39
43
  * tenant-wide lists; the paged ones answer the `next` cursor to pass back.
44
+ *
45
+ * Inside a runtime it also serves the schedules and this runner's own memory,
46
+ * each live answer naming the runner, its region, when it began recording and
47
+ * how many other runners the cluster lists:
48
+ *
49
+ * - `GET /schedules`: each declared cron entry with its pending ticks and last committed tick.
50
+ * - `GET /live/overview`: recent rates, resident activations and the deepest mailbox, by type.
51
+ * - `GET /live/activity?type&window`, `/live/latency?type&window`: one type's, or every type's,
52
+ * committed turns over `1h`, `24h` (default) or `7d`.
53
+ * - `GET /live/actors?type&ids`: whether each named actor is resident, its
54
+ * mailbox, sockets and feeds; `ids` is a JSON array of at most 100.
55
+ * - `GET /live/connections`: open sockets and SSE responses, by type.
56
+ * - `GET /commands/stream?type&outcome&after`: SSE of each command this runner
57
+ * commits for the tenant, `id` its position, resuming after `after` while the
58
+ * runner still holds it and sending `gap` when it does not, until the
59
+ * credential expires.
40
60
  */
41
61
  declare const serve: <R = never>(options: InspectorOptions<R>) => import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter | SqlClient.SqlClient | Exclude<R, import("effect/Scope").Scope>>;
42
62
  /** The read-only inspector: `serve` mounts its routes. */