@moltzap/client 2026.508.0 → 2026.511.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel-core.d.ts +118 -2
- package/dist/channel-core.d.ts.map +1 -1
- package/dist/channel-core.js +195 -27
- package/dist/channel-core.js.map +1 -1
- package/dist/channel-core.test.js +117 -26
- package/dist/channel-core.test.js.map +1 -1
- package/dist/cli/socket-client.d.ts +1 -1
- package/dist/cli/socket-client.d.ts.map +1 -1
- package/dist/cli/socket-client.js +2 -2
- package/dist/cli/socket-client.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/frame.d.ts +6 -10
- package/dist/runtime/frame.d.ts.map +1 -1
- package/dist/runtime/frame.js +4 -28
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +13 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/service.d.ts +18 -3
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +28 -50
- package/dist/service.js.map +1 -1
- package/dist/service.test.js +27 -25
- package/dist/service.test.js.map +1 -1
- package/dist/test/index.d.ts +3 -3
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +3 -3
- package/dist/test/index.js.map +1 -1
- package/dist/test-utils/channel-service-fixture.d.ts +2 -1
- package/dist/test-utils/channel-service-fixture.d.ts.map +1 -1
- package/dist/test-utils/channel-service-fixture.js +8 -0
- package/dist/test-utils/channel-service-fixture.js.map +1 -1
- package/dist/test-utils/conformance-adapter.js +1 -1
- package/dist/test-utils/fake-service.d.ts +2 -2
- package/dist/test-utils/fake-service.d.ts.map +1 -1
- package/dist/test-utils/fake-service.js +1 -1
- package/dist/test-utils/fake-service.js.map +1 -1
- package/dist/ws-client.d.ts +5 -26
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +71 -97
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-client.test.js +40 -12
- package/dist/ws-client.test.js.map +1 -1
- package/package.json +7 -3
- package/dist/internal/__tests__/app-callback-partition-key.test.d.ts +0 -2
- package/dist/internal/__tests__/app-callback-partition-key.test.d.ts.map +0 -1
- package/dist/internal/__tests__/app-callback-partition-key.test.js +0 -20
- package/dist/internal/__tests__/app-callback-partition-key.test.js.map +0 -1
- package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts +0 -2
- package/dist/internal/__tests__/app-callback-partition-worker.test.d.ts.map +0 -1
- package/dist/internal/__tests__/app-callback-partition-worker.test.js +0 -265
- package/dist/internal/__tests__/app-callback-partition-worker.test.js.map +0 -1
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts +0 -2
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.d.ts.map +0 -1
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.js +0 -228
- package/dist/internal/__tests__/app-callback-partitioned-dispatcher.test.js.map +0 -1
- package/dist/internal/__tests__/app-callback-test-requests.d.ts +0 -22
- package/dist/internal/__tests__/app-callback-test-requests.d.ts.map +0 -1
- package/dist/internal/__tests__/app-callback-test-requests.js +0 -45
- package/dist/internal/__tests__/app-callback-test-requests.js.map +0 -1
- package/dist/internal/app-callback-dispatcher-errors.d.ts +0 -50
- package/dist/internal/app-callback-dispatcher-errors.d.ts.map +0 -1
- package/dist/internal/app-callback-dispatcher-errors.js +0 -36
- package/dist/internal/app-callback-dispatcher-errors.js.map +0 -1
- package/dist/internal/app-callback-partition-key.d.ts +0 -58
- package/dist/internal/app-callback-partition-key.d.ts.map +0 -1
- package/dist/internal/app-callback-partition-key.js +0 -80
- package/dist/internal/app-callback-partition-key.js.map +0 -1
- package/dist/internal/app-callback-partition-worker.d.ts +0 -160
- package/dist/internal/app-callback-partition-worker.d.ts.map +0 -1
- package/dist/internal/app-callback-partition-worker.js +0 -141
- package/dist/internal/app-callback-partition-worker.js.map +0 -1
- package/dist/internal/app-callback-partitioned-dispatcher.d.ts +0 -180
- package/dist/internal/app-callback-partitioned-dispatcher.d.ts.map +0 -1
- package/dist/internal/app-callback-partitioned-dispatcher.js +0 -282
- package/dist/internal/app-callback-partitioned-dispatcher.js.map +0 -1
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-partition worker — bounded queue + forked fiber, owning ordering
|
|
3
|
-
* within one `(taskId, conversationId, hookKind)` tuple.
|
|
4
|
-
*
|
|
5
|
-
* Spec: moltzap#356.
|
|
6
|
-
*
|
|
7
|
-
* Lifecycle is anchored in a per-worker `Scope.CloseableScope` provided
|
|
8
|
-
* by the dispatcher (`app-callback-partitioned-dispatcher.ts`'s
|
|
9
|
-
* `getOrCreatePartitionWorker` calls `Scope.fork(dispatcherScope, …)`
|
|
10
|
-
* for each new worker and hands the child scope in via the config).
|
|
11
|
-
* Closing the worker's scope shuts the queue, runs registered
|
|
12
|
-
* finalizers, and interrupts the drain fiber — cascading finalization
|
|
13
|
-
* with no orphan references on the dispatcher scope. Closing the
|
|
14
|
-
* dispatcher scope still cascades to every worker via the parent-child
|
|
15
|
-
* scope link from `Scope.fork`.
|
|
16
|
-
*
|
|
17
|
-
* Backpressure: the queue is `Queue.bounded(capacity)`. `offer` is
|
|
18
|
-
* **non-blocking** — if the queue is full it fails with
|
|
19
|
-
* `PartitionQueueFullError` rather than suspending the caller. The
|
|
20
|
-
* reader fiber must NOT block; per-partition fullness is surfaced as
|
|
21
|
-
* a typed error and translated to a wire-level error response.
|
|
22
|
-
*
|
|
23
|
-
* Single-producer guarantee: only the dispatcher's reader-fiber path
|
|
24
|
-
* calls `offer` for any given partition. That makes the
|
|
25
|
-
* `Queue.size + offer` pre-check race-free for "full" detection: if
|
|
26
|
-
* size < capacity at the check, the subsequent `offer` cannot suspend
|
|
27
|
-
* because no other producer can fill the queue between check and
|
|
28
|
-
* offer. The drain fiber only consumes; it cannot push us past
|
|
29
|
-
* capacity.
|
|
30
|
-
*
|
|
31
|
-
* Why `idleSince` is a `MutableRef` (synchronous cell) rather than a
|
|
32
|
-
* `Ref`: the dispatcher's reaper inspects `idleSince._tag === "Idle"`
|
|
33
|
-
* inside a `Ref.modify` lambda over the partition map (synchronous
|
|
34
|
-
* lambda body — no `yield`). A `Ref.get` would require an Effect step
|
|
35
|
-
* we cannot take inside the lambda; `MutableRef.get` is synchronous.
|
|
36
|
-
* Producer (offer) and drain-loop sites use `MutableRef.set`. JS's
|
|
37
|
-
* single-threaded execution model makes the `Idle ↔ Busy` transitions
|
|
38
|
-
* race-free between fibers, and the dispatcher's atomic
|
|
39
|
-
* `Ref.modify(partitionsRef)` is the synchronization point that
|
|
40
|
-
* ensures snapshot+remove on idle partitions runs without an offer
|
|
41
|
-
* landing in between.
|
|
42
|
-
*/
|
|
43
|
-
import { Effect, Fiber, MutableRef, Scope } from "effect";
|
|
44
|
-
import { PartitionQueueFullError } from "./app-callback-dispatcher-errors.js";
|
|
45
|
-
import type { PartitionKey, PartitionableRequest } from "./app-callback-partition-key.js";
|
|
46
|
-
import type { WsClientLogger } from "../ws-client.js";
|
|
47
|
-
/**
|
|
48
|
-
* The handler the worker drains its queue against. Mirrors the existing
|
|
49
|
-
* `dispatchInboundServerRequest` signature in `ws-client.ts:912-965` —
|
|
50
|
-
* the dispatcher passes an already-bound function so the worker has no
|
|
51
|
-
* direct knowledge of the registered appCallback handler registry.
|
|
52
|
-
*
|
|
53
|
-
* Defects inside `handle` are caught and logged by the worker; they
|
|
54
|
-
* never escape into the per-connection scope's failure channel.
|
|
55
|
-
*/
|
|
56
|
-
export type PartitionHandler = (request: PartitionableRequest) => Effect.Effect<void, never>;
|
|
57
|
-
/**
|
|
58
|
-
* Idle / busy / retiring sum-type tracked per worker. See
|
|
59
|
-
* `PartitionWorker.idleSince`.
|
|
60
|
-
*
|
|
61
|
-
* - `Idle` — drain queue empty, last handler returned at `sinceMs`.
|
|
62
|
-
* Eligible for reap once `sinceMs` ages past `idlePartitionTtlMs`.
|
|
63
|
-
* - `Busy` — handler running OR producer has reserved this worker
|
|
64
|
-
* for a pending `Queue.offer`. Reaper skips Busy workers.
|
|
65
|
-
* - `Retiring` — reaper has claimed this worker for teardown
|
|
66
|
-
* (transition done atomically inside the reaper's `Ref.modify`
|
|
67
|
-
* lambda over the partition map). Producer's `worker.offer` MUST
|
|
68
|
-
* refuse to enqueue when it observes `Retiring`; otherwise the
|
|
69
|
-
* reaper's `Scope.close` would silently drop the just-enqueued
|
|
70
|
-
* request, leaving the server-side `Deferred.await` to time out.
|
|
71
|
-
*/
|
|
72
|
-
export type IdleSince = {
|
|
73
|
-
readonly _tag: "Idle";
|
|
74
|
-
readonly sinceMs: number;
|
|
75
|
-
} | {
|
|
76
|
-
readonly _tag: "Busy";
|
|
77
|
-
} | {
|
|
78
|
-
readonly _tag: "Retiring";
|
|
79
|
-
};
|
|
80
|
-
/**
|
|
81
|
-
* One partition's runtime state. Constructed by `makePartitionWorker`;
|
|
82
|
-
* owned by the dispatcher's partition map.
|
|
83
|
-
*/
|
|
84
|
-
export interface PartitionWorker {
|
|
85
|
-
/** Identity. Read-only after construction. */
|
|
86
|
-
readonly key: PartitionKey;
|
|
87
|
-
/**
|
|
88
|
-
* Non-blocking offer. Fails with `PartitionQueueFullError` if the
|
|
89
|
-
* bounded queue is at capacity; never suspends. Idempotent under
|
|
90
|
-
* shutdown — post-shutdown offers fail with the same tag (queue is
|
|
91
|
-
* shut down, equivalent to "no capacity").
|
|
92
|
-
*/
|
|
93
|
-
readonly offer: (request: PartitionableRequest) => Effect.Effect<void, PartitionQueueFullError>;
|
|
94
|
-
/**
|
|
95
|
-
* Worker fiber draining the queue via `Stream.runForEach(handle)`.
|
|
96
|
-
* Surfaced for observability + tests; the worker's `Scope.close` is
|
|
97
|
-
* the canonical teardown path (it triggers the queue.shutdown
|
|
98
|
-
* finalizer and interrupts the fiber via the scope cascade).
|
|
99
|
-
*/
|
|
100
|
-
readonly fiber: Fiber.RuntimeFiber<void, never>;
|
|
101
|
-
/**
|
|
102
|
-
* Idle-since tracker. `Busy` while the worker has a request queued
|
|
103
|
-
* or a handler running; `Idle{sinceMs}` once the queue drains and
|
|
104
|
-
* the handler returns. Stored in a `MutableRef` so the dispatcher's
|
|
105
|
-
* reaper can read it synchronously inside its `Ref.modify` lambda
|
|
106
|
-
* over the partition map (the atomic check that prevents a reaped
|
|
107
|
-
* worker from receiving a concurrent offer between snapshot and
|
|
108
|
-
* removal).
|
|
109
|
-
*/
|
|
110
|
-
readonly idleSince: MutableRef.MutableRef<IdleSince>;
|
|
111
|
-
/**
|
|
112
|
-
* Live queue size for `DispatcherStats`. Reaper does not consult
|
|
113
|
-
* this — it gates strictly on `idleSince._tag === "Idle"` to avoid
|
|
114
|
-
* races where the queue snapshot misses an in-flight item.
|
|
115
|
-
*/
|
|
116
|
-
readonly queueSize: Effect.Effect<number>;
|
|
117
|
-
/**
|
|
118
|
-
* The closeable scope owning this worker's finalizers (queue
|
|
119
|
-
* shutdown, drain fiber). Forked from the dispatcher's scope by
|
|
120
|
-
* `getOrCreatePartitionWorker`; closing it cleanly retires the
|
|
121
|
-
* worker without orphaning finalizers on the parent dispatcher
|
|
122
|
-
* scope.
|
|
123
|
-
*/
|
|
124
|
-
readonly scope: Scope.CloseableScope;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Construction parameters for one worker. Capacity and the handler are
|
|
128
|
-
* dispatcher-level config; `scope` is the per-worker closeable scope
|
|
129
|
-
* the dispatcher allocated via `Scope.fork`; logger is optional and
|
|
130
|
-
* matches the existing `WsClientLogger` shape used elsewhere in the
|
|
131
|
-
* client package.
|
|
132
|
-
*/
|
|
133
|
-
export interface PartitionWorkerConfig {
|
|
134
|
-
readonly key: PartitionKey;
|
|
135
|
-
readonly capacity: number;
|
|
136
|
-
readonly handle: PartitionHandler;
|
|
137
|
-
/**
|
|
138
|
-
* Per-worker scope owning every finalizer this worker registers
|
|
139
|
-
* (queue shutdown + drain fiber). The dispatcher's reaper closes
|
|
140
|
-
* this scope to retire the worker. Closing the parent dispatcher
|
|
141
|
-
* scope cascades through it via `Scope.fork`.
|
|
142
|
-
*/
|
|
143
|
-
readonly scope: Scope.CloseableScope;
|
|
144
|
-
readonly logger?: WsClientLogger;
|
|
145
|
-
/** Monotonic clock for tests; defaults to `Date.now`. */
|
|
146
|
-
readonly clock?: () => number;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Build one worker. Allocates a bounded queue, forks a draining fiber
|
|
150
|
-
* via `Stream.fromQueue` + `Stream.runForEach`, and registers the
|
|
151
|
-
* scope finalizer that shuts the queue. Defects in `handle` are
|
|
152
|
-
* caught + logged; the fiber never fails.
|
|
153
|
-
*
|
|
154
|
-
* Returns `Effect<PartitionWorker, never>` — no `Scope.Scope`
|
|
155
|
-
* requirement. All scoped allocations (queue finalizer, forked drain
|
|
156
|
-
* fiber) are bound to the caller-provided `config.scope` via
|
|
157
|
-
* `Scope.extend`.
|
|
158
|
-
*/
|
|
159
|
-
export declare function makePartitionWorker(config: PartitionWorkerConfig): Effect.Effect<PartitionWorker, never>;
|
|
160
|
-
//# sourceMappingURL=app-callback-partition-worker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-callback-partition-worker.d.ts","sourceRoot":"","sources":["../../src/internal/app-callback-partition-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAS,MAAM,EAAE,KAAK,EAAE,UAAU,EAAS,KAAK,EAAU,MAAM,QAAQ,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,oBAAoB,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAEhC;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,OAAO,EAAE,oBAAoB,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;IAClD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACrD;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC;CACtC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,qBAAqB,GAC5B,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAmIvC"}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-partition worker — bounded queue + forked fiber, owning ordering
|
|
3
|
-
* within one `(taskId, conversationId, hookKind)` tuple.
|
|
4
|
-
*
|
|
5
|
-
* Spec: moltzap#356.
|
|
6
|
-
*
|
|
7
|
-
* Lifecycle is anchored in a per-worker `Scope.CloseableScope` provided
|
|
8
|
-
* by the dispatcher (`app-callback-partitioned-dispatcher.ts`'s
|
|
9
|
-
* `getOrCreatePartitionWorker` calls `Scope.fork(dispatcherScope, …)`
|
|
10
|
-
* for each new worker and hands the child scope in via the config).
|
|
11
|
-
* Closing the worker's scope shuts the queue, runs registered
|
|
12
|
-
* finalizers, and interrupts the drain fiber — cascading finalization
|
|
13
|
-
* with no orphan references on the dispatcher scope. Closing the
|
|
14
|
-
* dispatcher scope still cascades to every worker via the parent-child
|
|
15
|
-
* scope link from `Scope.fork`.
|
|
16
|
-
*
|
|
17
|
-
* Backpressure: the queue is `Queue.bounded(capacity)`. `offer` is
|
|
18
|
-
* **non-blocking** — if the queue is full it fails with
|
|
19
|
-
* `PartitionQueueFullError` rather than suspending the caller. The
|
|
20
|
-
* reader fiber must NOT block; per-partition fullness is surfaced as
|
|
21
|
-
* a typed error and translated to a wire-level error response.
|
|
22
|
-
*
|
|
23
|
-
* Single-producer guarantee: only the dispatcher's reader-fiber path
|
|
24
|
-
* calls `offer` for any given partition. That makes the
|
|
25
|
-
* `Queue.size + offer` pre-check race-free for "full" detection: if
|
|
26
|
-
* size < capacity at the check, the subsequent `offer` cannot suspend
|
|
27
|
-
* because no other producer can fill the queue between check and
|
|
28
|
-
* offer. The drain fiber only consumes; it cannot push us past
|
|
29
|
-
* capacity.
|
|
30
|
-
*
|
|
31
|
-
* Why `idleSince` is a `MutableRef` (synchronous cell) rather than a
|
|
32
|
-
* `Ref`: the dispatcher's reaper inspects `idleSince._tag === "Idle"`
|
|
33
|
-
* inside a `Ref.modify` lambda over the partition map (synchronous
|
|
34
|
-
* lambda body — no `yield`). A `Ref.get` would require an Effect step
|
|
35
|
-
* we cannot take inside the lambda; `MutableRef.get` is synchronous.
|
|
36
|
-
* Producer (offer) and drain-loop sites use `MutableRef.set`. JS's
|
|
37
|
-
* single-threaded execution model makes the `Idle ↔ Busy` transitions
|
|
38
|
-
* race-free between fibers, and the dispatcher's atomic
|
|
39
|
-
* `Ref.modify(partitionsRef)` is the synchronization point that
|
|
40
|
-
* ensures snapshot+remove on idle partitions runs without an offer
|
|
41
|
-
* landing in between.
|
|
42
|
-
*/
|
|
43
|
-
import { Cause, Effect, Fiber, MutableRef, Queue, Scope, Stream } from "effect";
|
|
44
|
-
import { PartitionQueueFullError } from "./app-callback-dispatcher-errors.js";
|
|
45
|
-
/**
|
|
46
|
-
* Build one worker. Allocates a bounded queue, forks a draining fiber
|
|
47
|
-
* via `Stream.fromQueue` + `Stream.runForEach`, and registers the
|
|
48
|
-
* scope finalizer that shuts the queue. Defects in `handle` are
|
|
49
|
-
* caught + logged; the fiber never fails.
|
|
50
|
-
*
|
|
51
|
-
* Returns `Effect<PartitionWorker, never>` — no `Scope.Scope`
|
|
52
|
-
* requirement. All scoped allocations (queue finalizer, forked drain
|
|
53
|
-
* fiber) are bound to the caller-provided `config.scope` via
|
|
54
|
-
* `Scope.extend`.
|
|
55
|
-
*/
|
|
56
|
-
export function makePartitionWorker(config) {
|
|
57
|
-
const clock = config.clock ?? Date.now;
|
|
58
|
-
return Scope.extend(Effect.gen(function* () {
|
|
59
|
-
const queue = yield* Queue.bounded(config.capacity);
|
|
60
|
-
const idleSince = MutableRef.make({
|
|
61
|
-
_tag: "Idle",
|
|
62
|
-
sinceMs: clock(),
|
|
63
|
-
});
|
|
64
|
-
// Drain loop: take → mark Busy → run handler (catching defects)
|
|
65
|
-
// → mark Idle if queue is now empty. `Stream.fromQueue` ends
|
|
66
|
-
// naturally when the queue is shut down (Scope finalizer below),
|
|
67
|
-
// so the fiber returns cleanly without an explicit interrupt for
|
|
68
|
-
// the happy path.
|
|
69
|
-
const drainEffect = Stream.fromQueue(queue).pipe(Stream.runForEach((request) => Effect.gen(function* () {
|
|
70
|
-
MutableRef.set(idleSince, { _tag: "Busy" });
|
|
71
|
-
yield* config.handle(request).pipe(Effect.catchAllCause((cause) => Effect.sync(() => {
|
|
72
|
-
config.logger?.warn(`appCallback partition worker handler defected (key=${config.key})`, Cause.pretty(cause));
|
|
73
|
-
})));
|
|
74
|
-
const remaining = yield* Queue.size(queue).pipe(Effect.catchAllCause(() => Effect.succeed(0)));
|
|
75
|
-
if (remaining === 0) {
|
|
76
|
-
MutableRef.set(idleSince, {
|
|
77
|
-
_tag: "Idle",
|
|
78
|
-
sinceMs: clock(),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
})), Effect.catchAllCause((cause) => Effect.sync(() => {
|
|
82
|
-
config.logger?.warn(`appCallback partition worker fiber exited (key=${config.key})`, Cause.pretty(cause));
|
|
83
|
-
})), Effect.asVoid);
|
|
84
|
-
const fiber = yield* Effect.forkScoped(drainEffect);
|
|
85
|
-
// Shut the queue when the worker scope closes. `Effect.forkScoped`
|
|
86
|
-
// already covers fiber interruption; queue.shutdown is the
|
|
87
|
-
// belt-and-braces guarantee that the running stream observes the
|
|
88
|
-
// shutdown and returns even if the fiber is mid-handler.
|
|
89
|
-
yield* Effect.addFinalizer(() => Queue.shutdown(queue).pipe(Effect.catchAllCause(() => Effect.void)));
|
|
90
|
-
const offer = (request) => Effect.gen(function* () {
|
|
91
|
-
const size = yield* Queue.size(queue).pipe(
|
|
92
|
-
// `Queue.size` on a shut-down queue surfaces interrupt; treat
|
|
93
|
-
// every failure as "queue unavailable" → full-error tag.
|
|
94
|
-
Effect.catchAllCause(() => Effect.succeed(config.capacity)));
|
|
95
|
-
if (size >= config.capacity) {
|
|
96
|
-
return yield* Effect.fail(new PartitionQueueFullError({
|
|
97
|
-
key: config.key,
|
|
98
|
-
capacity: config.capacity,
|
|
99
|
-
requestId: request.id,
|
|
100
|
-
}));
|
|
101
|
-
}
|
|
102
|
-
// Atomic CAS: transition Idle/Busy → Busy, but refuse if the
|
|
103
|
-
// dispatcher's reaper has already claimed this worker for
|
|
104
|
-
// teardown (`Retiring`). The reaper sets `Retiring`
|
|
105
|
-
// synchronously inside its `Ref.modify(partitionsRef, …)`
|
|
106
|
-
// lambda, so once we observe `Retiring` here, the worker has
|
|
107
|
-
// already been removed from the partition map and its scope
|
|
108
|
-
// is being closed. Failing without enqueueing is the correct
|
|
109
|
-
// and only safe move — a queued item would be silently
|
|
110
|
-
// dropped when `Scope.close` fires `Queue.shutdown`.
|
|
111
|
-
const claim = MutableRef.updateAndGet(idleSince, (current) => current._tag === "Retiring" ? current : { _tag: "Busy" });
|
|
112
|
-
if (claim._tag === "Retiring") {
|
|
113
|
-
return yield* Effect.fail(new PartitionQueueFullError({
|
|
114
|
-
key: config.key,
|
|
115
|
-
capacity: config.capacity,
|
|
116
|
-
requestId: request.id,
|
|
117
|
-
}));
|
|
118
|
-
}
|
|
119
|
-
const accepted = yield* Queue.offer(queue, request).pipe(Effect.catchAllCause(() => Effect.succeed(false)));
|
|
120
|
-
if (!accepted) {
|
|
121
|
-
// Queue shut down between size-check and offer — surface as
|
|
122
|
-
// `PartitionQueueFullError` so the reader's tag-discrimination
|
|
123
|
-
// path treats it identically to "queue full".
|
|
124
|
-
return yield* Effect.fail(new PartitionQueueFullError({
|
|
125
|
-
key: config.key,
|
|
126
|
-
capacity: config.capacity,
|
|
127
|
-
requestId: request.id,
|
|
128
|
-
}));
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
return {
|
|
132
|
-
key: config.key,
|
|
133
|
-
offer,
|
|
134
|
-
fiber,
|
|
135
|
-
idleSince,
|
|
136
|
-
queueSize: Queue.size(queue).pipe(Effect.catchAllCause(() => Effect.succeed(0))),
|
|
137
|
-
scope: config.scope,
|
|
138
|
-
};
|
|
139
|
-
}), config.scope);
|
|
140
|
-
}
|
|
141
|
-
//# sourceMappingURL=app-callback-partition-worker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-callback-partition-worker.js","sourceRoot":"","sources":["../../src/internal/app-callback-partition-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Partitioned appCallback dispatcher.
|
|
3
|
-
*
|
|
4
|
-
* Spec: moltzap#356.
|
|
5
|
-
*
|
|
6
|
-
* Replaces the single `Stream.runForEach(handler)` over `appCallbackInboundQueue`
|
|
7
|
-
* (`packages/client/src/ws-client.ts:658-687@f0df363`) with a
|
|
8
|
-
* partition-router that owns a `Ref<HashMap<PartitionKey,
|
|
9
|
-
* PartitionWorker>>`. For each inbound request:
|
|
10
|
-
*
|
|
11
|
-
* 1. The reader fiber calls `dispatcher.offer(request)`.
|
|
12
|
-
* 2. The dispatcher extracts the partition key (pure, no schema decode).
|
|
13
|
-
* 3. If a worker exists for that key, `worker.offer(request)`.
|
|
14
|
-
* 4. Otherwise, allocate a worker (subject to soft cap) and offer.
|
|
15
|
-
*
|
|
16
|
-
* The dispatcher itself does NOT run a long-lived router fiber: routing
|
|
17
|
-
* is synchronous-up-to-effect on the reader's call thread. The only
|
|
18
|
-
* dispatcher-owned fiber is the **idle reaper**, which periodically
|
|
19
|
-
* walks the partition map and finalizes any worker whose
|
|
20
|
-
* `idleSince._tag === "Idle"` exceeds `idlePartitionTtlMs`.
|
|
21
|
-
*
|
|
22
|
-
* Single-producer guarantee: `dispatcher.offer` is only called from
|
|
23
|
-
* the per-connection reader fiber's `Stream.runForEach` loop in
|
|
24
|
-
* `ws-client.ts:1052-1086`. There is no second producer; concurrent
|
|
25
|
-
* `offer` calls within one dispatcher cannot occur. The actual
|
|
26
|
-
* concurrency property a router would otherwise be defending against
|
|
27
|
-
* is route-vs-drain: the route step (one `Ref.modify` against the
|
|
28
|
-
* partition map + a non-blocking `worker.offer`) is wait-free against
|
|
29
|
-
* every running worker fiber's drain loop. No router fiber means no
|
|
30
|
-
* intermediate buffer between the reader and the per-key worker
|
|
31
|
-
* queue, and no scheduling overhead per route decision.
|
|
32
|
-
*
|
|
33
|
-
* Lifetime: `make` returns `Effect<…, never, Scope.Scope>`. The caller
|
|
34
|
-
* (`ws-client.ts` inside `connectEffect`) calls `Scope.make()` to
|
|
35
|
-
* produce a **dedicated dispatcher Scope** that is NOT bound to the
|
|
36
|
-
* socket Scope, then provides that Scope to `makePartitionedDispatcher`.
|
|
37
|
-
* The dispatcher Scope is held in `ConnState`; teardown from
|
|
38
|
-
* `close()` / `disconnectSync()` is
|
|
39
|
-
* `runtime.runFork(Scope.close(dispatcherScope, Exit.void))`, mirroring
|
|
40
|
-
* today's `runFork(Queue.shutdown(appCallbackInboundQueue))` +
|
|
41
|
-
* `runFork(Fiber.interrupt(appCallbackDispatcherFiber))` pair.
|
|
42
|
-
*
|
|
43
|
-
* Why off-Scope from the socket: `close()` is invoked via `runSync` at
|
|
44
|
-
* `packages/openclaw-channel/src/__tests__/reconnection.integration.test.ts:14`;
|
|
45
|
-
* binding the dispatcher to the socket Scope would make `Scope.close`
|
|
46
|
-
* yield through the runtime (queue.shutdown + fiber.interrupt are
|
|
47
|
-
* non-synchronous), breaking the regression gate at
|
|
48
|
-
* `packages/client/src/ws-client.test.ts:1233-1259`.
|
|
49
|
-
* The `runSync(client.close())` contract is load-bearing — see the
|
|
50
|
-
* test commentary there.
|
|
51
|
-
*
|
|
52
|
-
* Worker scopes are forked children of the dispatcher Scope
|
|
53
|
-
* (`getOrCreatePartitionWorker` calls `Scope.fork(dispatcherScope, …)`
|
|
54
|
-
* for each new worker and provides the child scope to
|
|
55
|
-
* `makePartitionWorker`). Closing the dispatcher Scope cascades to
|
|
56
|
-
* every worker; closing one worker's child scope retires only that
|
|
57
|
-
* worker without leaking finalizers onto the dispatcher scope.
|
|
58
|
-
*/
|
|
59
|
-
import { Effect, Scope } from "effect";
|
|
60
|
-
import { type OfferRejected } from "./app-callback-dispatcher-errors.js";
|
|
61
|
-
import { type PartitionKey, type PartitionableRequest } from "./app-callback-partition-key.js";
|
|
62
|
-
import { type PartitionHandler } from "./app-callback-partition-worker.js";
|
|
63
|
-
import type { WsClientLogger } from "../ws-client.js";
|
|
64
|
-
/**
|
|
65
|
-
* Tunable knobs. Defaults named in the design doc; downstream picks
|
|
66
|
-
* exact values at the `MoltZapWsClient` constructor edge.
|
|
67
|
-
*/
|
|
68
|
-
export interface PartitionedDispatcherConfig {
|
|
69
|
-
/**
|
|
70
|
-
* Soft cap on simultaneously-active partitions per connection.
|
|
71
|
-
* Default 256. Hit → new partitions fail with `PartitionLimitError`
|
|
72
|
-
* until the idle reaper retires one.
|
|
73
|
-
*/
|
|
74
|
-
readonly maxPartitions: number;
|
|
75
|
-
/**
|
|
76
|
-
* Bounded queue depth per partition. Default 32. Hit → `offer` fails
|
|
77
|
-
* with `PartitionQueueFullError` for THIS partition only.
|
|
78
|
-
*/
|
|
79
|
-
readonly partitionQueueCapacity: number;
|
|
80
|
-
/**
|
|
81
|
-
* Idle reaper threshold: a worker whose queue has been continuously
|
|
82
|
-
* empty for at least this many milliseconds is finalized on the next
|
|
83
|
-
* reaper tick. Default 60_000.
|
|
84
|
-
*/
|
|
85
|
-
readonly idlePartitionTtlMs: number;
|
|
86
|
-
/**
|
|
87
|
-
* Idle reaper tick period. Default 5_000. Lower = tighter reclamation,
|
|
88
|
-
* higher CPU cost; higher = looser reclamation, lower overhead.
|
|
89
|
-
*/
|
|
90
|
-
readonly idleReaperIntervalMs: number;
|
|
91
|
-
}
|
|
92
|
-
/** Defaults. Documented as the named knob values for the implementation. */
|
|
93
|
-
export declare const DEFAULT_PARTITIONED_DISPATCHER_CONFIG: PartitionedDispatcherConfig;
|
|
94
|
-
/**
|
|
95
|
-
* Snapshot of dispatcher state for tests + observability. NOT stable
|
|
96
|
-
* across releases; consumers must treat as best-effort instrumentation.
|
|
97
|
-
*/
|
|
98
|
-
export interface DispatcherStats {
|
|
99
|
-
readonly activePartitions: number;
|
|
100
|
-
readonly totalOffered: number;
|
|
101
|
-
readonly totalRejected: {
|
|
102
|
-
readonly partitionLimit: number;
|
|
103
|
-
readonly partitionQueueFull: number;
|
|
104
|
-
};
|
|
105
|
-
readonly partitions: ReadonlyArray<{
|
|
106
|
-
readonly key: PartitionKey;
|
|
107
|
-
readonly queueSize: number;
|
|
108
|
-
readonly idle: boolean;
|
|
109
|
-
}>;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Public surface used by `ws-client.ts`. Construction is scoped — the
|
|
113
|
-
* caller's `Scope` owns every partition fiber.
|
|
114
|
-
*/
|
|
115
|
-
export interface PartitionedDispatcher {
|
|
116
|
-
/**
|
|
117
|
-
* Route one inbound request. Non-blocking. Failure modes are
|
|
118
|
-
* exhaustive in `OfferRejected`; the reader fiber `Effect.match`es
|
|
119
|
-
* the tag set to pick a wire-error code.
|
|
120
|
-
*
|
|
121
|
-
* Per-tuple ordering: two `offer` calls with the same `PartitionKey`
|
|
122
|
-
* land on the same `PartitionWorker.offer` (FIFO).
|
|
123
|
-
* Cross-tuple concurrency: two `offer` calls with different keys
|
|
124
|
-
* produce no ordering relation.
|
|
125
|
-
*/
|
|
126
|
-
readonly offer: (request: PartitionableRequest) => Effect.Effect<void, OfferRejected>;
|
|
127
|
-
/**
|
|
128
|
-
* Best-effort stats snapshot. Not used in the hot path; called by
|
|
129
|
-
* tests + the optional `client.stats()` debug surface.
|
|
130
|
-
*/
|
|
131
|
-
readonly stats: Effect.Effect<DispatcherStats>;
|
|
132
|
-
/**
|
|
133
|
-
* Explicit shutdown. Equivalent to `Scope.close(dispatcherScope,
|
|
134
|
-
* Exit.void)`; exists as a typed handle so callsites that already
|
|
135
|
-
* hold the dispatcher (not the Scope) can tear it down without
|
|
136
|
-
* threading the Scope through `ConnState`. Idempotent.
|
|
137
|
-
*
|
|
138
|
-
* Callsites that invoke this from a `runSync` context (`close()`,
|
|
139
|
-
* `disconnectSync()`) MUST wrap with `runtime.runFork`. The Effect
|
|
140
|
-
* itself yields through the runtime (queue shutdown + fiber
|
|
141
|
-
* interrupt are non-synchronous); calling `runSync` on it would throw
|
|
142
|
-
* `AsyncFiberException`. See the regression gate at
|
|
143
|
-
* `packages/client/src/ws-client.test.ts:1233-1259@f0df363`.
|
|
144
|
-
*/
|
|
145
|
-
readonly shutdown: Effect.Effect<void>;
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Construction params. `handle` is the per-request work — passed in so
|
|
149
|
-
* the dispatcher has no direct dependency on the handler-registry
|
|
150
|
-
* `Ref<HashMap<string, ServerRpcHandler>>` (`appCallbackHandlersRef`); the
|
|
151
|
-
* caller (`ws-client.ts`) closes over the registry and supplies a
|
|
152
|
-
* pre-bound function. This preserves the wire-edge schema-decoding
|
|
153
|
-
* boundary already established in `dispatchInboundServerRequest`.
|
|
154
|
-
*
|
|
155
|
-
* `scope` is the `Scope.CloseableScope` that owns every per-partition
|
|
156
|
-
* worker fiber + the idle reaper. The caller (`ws-client.ts`)
|
|
157
|
-
* allocates it via `Scope.make()` outside the per-connect socket
|
|
158
|
-
* Scope so `runSync(client.close())` can `runFork(Scope.close(scope,
|
|
159
|
-
* Exit.void))` without yielding through the runtime.
|
|
160
|
-
*/
|
|
161
|
-
export interface MakePartitionedDispatcherParams {
|
|
162
|
-
readonly handle: PartitionHandler;
|
|
163
|
-
readonly scope: Scope.CloseableScope;
|
|
164
|
-
readonly config?: Partial<PartitionedDispatcherConfig>;
|
|
165
|
-
readonly logger?: WsClientLogger;
|
|
166
|
-
/** Monotonic clock for tests. Defaults to `Date.now`. */
|
|
167
|
-
readonly clock?: () => number;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Construct a partitioned dispatcher. The caller-provided
|
|
171
|
-
* `params.scope` owns every per-partition fiber + the idle-reaper
|
|
172
|
-
* fiber; closing it cascades teardown via `Scope` finalizers.
|
|
173
|
-
*
|
|
174
|
-
* Returns `Effect<…, never>` — neither construction nor partition
|
|
175
|
-
* allocation can fail. The Effect itself does not require a `Scope`
|
|
176
|
-
* because every scoped allocation is provided to `params.scope`
|
|
177
|
-
* explicitly via `Scope.extend`.
|
|
178
|
-
*/
|
|
179
|
-
export declare function makePartitionedDispatcher(params: MakePartitionedDispatcherParams): Effect.Effect<PartitionedDispatcher, never>;
|
|
180
|
-
//# sourceMappingURL=app-callback-partitioned-dispatcher.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-callback-partitioned-dispatcher.d.ts","sourceRoot":"","sources":["../../src/internal/app-callback-partitioned-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,OAAO,EAGL,MAAM,EAQN,KAAK,EACN,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAC1B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAED,4EAA4E;AAC5E,eAAO,MAAM,qCAAqC,EAAE,2BAMjD,CAAC;AAEJ;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE;QACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;KACrC,CAAC;IACF,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;QACjC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;KACxB,CAAC,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,OAAO,EAAE,oBAAoB,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC/C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACxC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAoCD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,GACtC,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAgH7C"}
|