@effect-agent/platform-cloudflare 0.1.0-beta.114 → 0.1.0-beta.115
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/Alarm.d.mts +30 -47
- package/dist/Alarm.mjs +165 -170
- package/dist/Alarm.mjs.map +1 -1
- package/dist/CloudflareThreadClient.d.mts +27 -12
- package/dist/ProtectedBrowser.mjs +2 -1
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-BPt5uByD.mjs → ThreadObject-CEWwifrL.mjs} +10 -29
- package/dist/ThreadObject-CEWwifrL.mjs.map +1 -0
- package/dist/{ThreadObject-B97Se7j5.d.mts → ThreadObject-HwODpdru.d.mts} +27 -27
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +278 -340
- package/src/ThreadObject.ts +4 -0
- package/src/internal/layers.ts +8 -0
- package/src/internal/message-delivery.ts +4 -64
- package/src/protected-browser/inspect-frame.ts +2 -1
- package/dist/ThreadObject-BPt5uByD.mjs.map +0 -1
package/dist/Alarm.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DurableObjectContext } from "./CloudflareBindings.mjs";
|
|
2
2
|
import { s as CloudflareDurableRuntimeConfig } from "./CloudflareConfig-DKXGo8L3.mjs";
|
|
3
|
-
import { Cause, Context,
|
|
3
|
+
import { Cause, Context, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
4
4
|
import { DurableBindingFailure } from "effect-agent/agent-registration";
|
|
5
5
|
import { DurableAgentRuntime, DurableRuntimeConfig, DurableWorkerFailure, RecoveryFailure } from "effect-agent/durable-agent-runtime";
|
|
6
6
|
import { ThreadId } from "effect-agent/identifiers";
|
|
@@ -10,7 +10,7 @@ import { ThreadProjectionError, ThreadProjectionMaintenance } from "effect-agent
|
|
|
10
10
|
import { WakeScheduler } from "effect-agent/wake-scheduler";
|
|
11
11
|
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
12
12
|
declare namespace Alarm_d_exports {
|
|
13
|
-
export { DurableAlarmError, DurableAlarmService, MaintenancePassFailure, MaintenancePassReport, ThreadHostMaintenance,
|
|
13
|
+
export { DurableAlarmError, DurableAlarmService, MaintenancePassFailure, MaintenancePassReport, ThreadHostMaintenance, ThreadHostMaintenanceLane, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMaintenanceFailpointHandler, ThreadMaintenanceFailpointLocation, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, ThreadPublicationService, ThreadRecoveryFault, publishCommitted };
|
|
14
14
|
}
|
|
15
15
|
declare const DurableAlarmError_base: Schema.Class<DurableAlarmError, Schema.TaggedStruct<"DurableAlarmError", {
|
|
16
16
|
readonly operation: Schema.String;
|
|
@@ -114,61 +114,44 @@ declare const ThreadPublication_base: Context.ServiceClass<ThreadPublication, "@
|
|
|
114
114
|
export declare class ThreadPublication extends ThreadPublication_base {
|
|
115
115
|
static readonly layer: Layer.Layer<ThreadPublication, never, never>;
|
|
116
116
|
}
|
|
117
|
-
declare const ThreadMaintenanceActivity_base: Context.ServiceClass<ThreadMaintenanceActivity, "@effect-agent/platform-cloudflare/ThreadMaintenanceActivity", {
|
|
118
|
-
/**
|
|
119
|
-
* Account for one finite selection/dispatch, through joined completion or interruption.
|
|
120
|
-
* Register before reading/claiming work. After dispatch closes the body is not started.
|
|
121
|
-
* Host waves retain their declared allowance from this call; message Claims keep the driver's
|
|
122
|
-
* own deadline. Waiting for notifications belongs outside this bracket.
|
|
123
|
-
*/
|
|
124
|
-
readonly run: <R>(wave: Effect.Effect<void, DurableAlarmError, R>) => Effect.Effect<void, DurableAlarmError, R>;
|
|
125
|
-
/** Acknowledge all initial subscriptions/scans. Composite hosts use `all` below. */
|
|
126
|
-
readonly ready: Effect.Effect<void>;
|
|
127
|
-
/**
|
|
128
|
-
* Acquire an independent scoped subscription before the initial scan and `ready`.
|
|
129
|
-
* The returned wait observes coalesced native scan hints, including those published before
|
|
130
|
-
* its first execution. Each hint requests a current local due-work check.
|
|
131
|
-
*/
|
|
132
|
-
readonly subscribeChanges: Effect.Effect<Effect.Effect<void>, never, Scope.Scope>;
|
|
133
|
-
}>;
|
|
134
|
-
/** Event-local accounting shared by the existing maintenance pumps and native scheduler. */
|
|
135
|
-
export declare class ThreadMaintenanceActivity extends ThreadMaintenanceActivity_base {
|
|
136
|
-
/** Join child pumps; acknowledge the parent only after every child is ready or has exited. */
|
|
137
|
-
static readonly all: <A, E, R>(lanes: readonly Effect.Effect<A, E, R>[]) => Effect.Effect<readonly A[], E, R | ThreadMaintenanceActivity>;
|
|
138
|
-
}
|
|
139
117
|
/**
|
|
140
|
-
* Host-assembled native message recovery.
|
|
141
|
-
*
|
|
142
|
-
*
|
|
118
|
+
* Host-assembled native message recovery. Preparation is a bounded local selection; the
|
|
119
|
+
* driver bounds each actual Claim and persists its timeout/retry before `run` returns.
|
|
120
|
+
* The alarm must not add a timer starting at selection: Claim setup and retry commits belong
|
|
121
|
+
* to the driver. The prepared allowance decides only whether a wave fits this event.
|
|
143
122
|
*/
|
|
144
123
|
export declare const ThreadMessageDelivery: Context.Reference<{
|
|
145
|
-
readonly
|
|
124
|
+
readonly prepare: Effect.Effect<{
|
|
125
|
+
readonly timeoutMillis: number;
|
|
126
|
+
readonly run: Effect.Effect<void, DurableAlarmError>;
|
|
127
|
+
}, DurableAlarmError>;
|
|
146
128
|
readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
|
|
147
129
|
}>;
|
|
148
130
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* Passive subscriptions do not count as active work. Closure stops both new waves and native
|
|
154
|
-
* Attempts; local admission/control subscriptions belong to the event Scope until teardown.
|
|
155
|
-
* No deadline sleeps or automatic retry loops. Return after already-admitted waves finish.
|
|
131
|
+
* One independently schedulable application obligation. `run` selects and joins one finite
|
|
132
|
+
* wave, including its retry/receipt commits. The alarm gives every lane an initial opportunity,
|
|
133
|
+
* then checks its local pending deadline on completion, native progress, wakes and bounded scans.
|
|
134
|
+
* No subscriptions, readiness acknowledgements, deadline sleeps or dispatch loops are needed.
|
|
156
135
|
*
|
|
157
|
-
* Declare a
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
136
|
+
* Declare a whole-wave allowance (1..300000ms), including selection, local commits and cleanup. A wave
|
|
137
|
+
* starts only if that allowance fits the event and later arrivals cannot renew it. A failed
|
|
138
|
+
* lane is not retried within the event. The alarm joins admitted work and closes its Scope
|
|
139
|
+
* before reading durable deadlines under the shared ThreadMutationGate.
|
|
161
140
|
*
|
|
162
|
-
*
|
|
163
|
-
* dispatch;
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* alarm slot. Required native publication gates belong to ThreadPublication.
|
|
141
|
+
* `pendingDeadline` is bounded local control state, never a retained-history scan. Persist
|
|
142
|
+
* envelopes/claims before dispatch; cancellation is local, not remote rollback. Network waits
|
|
143
|
+
* and finalizers must be interruptible; short atomic commits may be uninterruptible. Mutations
|
|
144
|
+
* use the shared gate and producers notify the existing WakeScheduler after commit. Hooks never
|
|
145
|
+
* write the raw alarm slot. Required native publication gates belong to ThreadPublication.
|
|
167
146
|
*/
|
|
168
|
-
export
|
|
147
|
+
export interface ThreadHostMaintenanceLane {
|
|
169
148
|
readonly dispatchTimeoutMillis: number;
|
|
170
|
-
readonly
|
|
149
|
+
readonly run: Effect.Effect<void, DurableAlarmError, Scope.Scope>;
|
|
171
150
|
readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
|
|
151
|
+
}
|
|
152
|
+
/** Independent lanes share the existing alarm; compose hosts by concatenating their lanes. */
|
|
153
|
+
export declare const ThreadHostMaintenance: Context.Reference<{
|
|
154
|
+
readonly lanes: ReadonlyArray<ThreadHostMaintenanceLane>;
|
|
172
155
|
}>;
|
|
173
156
|
/** @internal A committed source operation must not become a failed operation because delivery failed. */
|
|
174
157
|
export declare const publishCommitted: Effect.Effect<void, never, ThreadPublication>;
|
|
@@ -244,7 +227,7 @@ declare const ThreadMaintenance_base: Context.ServiceClass<ThreadMaintenance, "@
|
|
|
244
227
|
* retain their durable backoff.
|
|
245
228
|
* 3. Keep native and delivery admission open together while finite waves remain active, so
|
|
246
229
|
* fresh replies and abort controls can progress during unrelated cleanup. Close atomically
|
|
247
|
-
* at quiescence, or at the original ten-minute yield deadline,
|
|
230
|
+
* at quiescence, or at the original ten-minute yield deadline, then join admitted waves.
|
|
248
231
|
* 4. Native message delivery retains its driver-owned Claim deadline. Host/backfill waves are
|
|
249
232
|
* bounded independently; incoming native work never restarts or cancels their attempts.
|
|
250
233
|
* Auxiliary failures are reported after the current native opportunity.
|
package/dist/Alarm.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
|
2
2
|
import { DurableObjectContext } from "./CloudflareBindings.mjs";
|
|
3
3
|
import { AuxiliaryDispatchMillis, CloudflareDurableRuntimeConfig } from "./CloudflareConfig.mjs";
|
|
4
4
|
import { r as safeCauseMessage } from "./boundary-BguazVkh.mjs";
|
|
5
|
-
import { Cause, Clock, Context, DateTime, Deferred, Effect, Exit, Fiber, Layer, Option,
|
|
5
|
+
import { Cause, Clock, Context, DateTime, Deferred, Effect, Exit, Fiber, Layer, Option, Random, Ref, Result, Schema, Scope, Semaphore, Stream, Struct } from "effect";
|
|
6
6
|
import "effect-agent/agent-registration";
|
|
7
7
|
import { DurableAgentRuntime, DurableRuntimeConfig, RecoveryBlocked, RecoveryFailure, RecoverySweepResult, isolateRecovery } from "effect-agent/durable-agent-runtime";
|
|
8
8
|
import { SubmissionId, ThreadId } from "effect-agent/identifiers";
|
|
@@ -18,7 +18,6 @@ var Alarm_exports = /* @__PURE__ */ __exportAll({
|
|
|
18
18
|
MaintenancePassReport: () => MaintenancePassReport,
|
|
19
19
|
ThreadHostMaintenance: () => ThreadHostMaintenance,
|
|
20
20
|
ThreadMaintenance: () => ThreadMaintenance,
|
|
21
|
-
ThreadMaintenanceActivity: () => ThreadMaintenanceActivity,
|
|
22
21
|
ThreadMaintenanceFailpoint: () => ThreadMaintenanceFailpoint,
|
|
23
22
|
ThreadMessageDelivery: () => ThreadMessageDelivery,
|
|
24
23
|
ThreadMutationGate: () => ThreadMutationGate,
|
|
@@ -112,61 +111,21 @@ var ThreadPublication = class extends Context.Service()("@effect-agent/platform-
|
|
|
112
111
|
pendingDeadline: Effect.succeed(Option.none())
|
|
113
112
|
});
|
|
114
113
|
};
|
|
115
|
-
/** Event-local accounting shared by the existing maintenance pumps and native scheduler. */
|
|
116
|
-
var ThreadMaintenanceActivity = class ThreadMaintenanceActivity extends Context.Service()("@effect-agent/platform-cloudflare/ThreadMaintenanceActivity") {
|
|
117
|
-
/** Join child pumps; acknowledge the parent only after every child is ready or has exited. */
|
|
118
|
-
static all = Effect.fnUntraced(function* (lanes) {
|
|
119
|
-
const activity = yield* ThreadMaintenanceActivity;
|
|
120
|
-
let remaining = lanes.length;
|
|
121
|
-
if (remaining === 0) return yield* activity.ready.pipe(Effect.as([]));
|
|
122
|
-
return yield* Effect.forEach(lanes, (lane) => {
|
|
123
|
-
let ready = false;
|
|
124
|
-
const child = {
|
|
125
|
-
...activity,
|
|
126
|
-
ready: Effect.suspend(() => {
|
|
127
|
-
if (ready) return Effect.void;
|
|
128
|
-
ready = true;
|
|
129
|
-
return --remaining === 0 ? activity.ready : Effect.void;
|
|
130
|
-
}).pipe(Effect.uninterruptible)
|
|
131
|
-
};
|
|
132
|
-
return lane.pipe(Effect.provideService(ThreadMaintenanceActivity, child), Effect.onExit(() => child.ready));
|
|
133
|
-
}, { concurrency: "unbounded" });
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
114
|
/**
|
|
137
|
-
* Host-assembled native message recovery.
|
|
138
|
-
*
|
|
139
|
-
*
|
|
115
|
+
* Host-assembled native message recovery. Preparation is a bounded local selection; the
|
|
116
|
+
* driver bounds each actual Claim and persists its timeout/retry before `run` returns.
|
|
117
|
+
* The alarm must not add a timer starting at selection: Claim setup and retry commits belong
|
|
118
|
+
* to the driver. The prepared allowance decides only whether a wave fits this event.
|
|
140
119
|
*/
|
|
141
120
|
const ThreadMessageDelivery = Context.Reference("@effect-agent/platform-cloudflare/ThreadMessageDelivery", { defaultValue: () => ({
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
* Application obligations sharing this Object's alarm. Admit one initial external wave even on
|
|
147
|
-
* a caught-up pass, then respond to wakes and ThreadMaintenanceActivity.subscribeChanges until
|
|
148
|
-
* dispatchClosed. Yield the event's ThreadMaintenanceActivity to bracket each finite lane with
|
|
149
|
-
* run and acknowledge initial setup with ready.
|
|
150
|
-
* Passive subscriptions do not count as active work. Closure stops both new waves and native
|
|
151
|
-
* Attempts; local admission/control subscriptions belong to the event Scope until teardown.
|
|
152
|
-
* No deadline sleeps or automatic retry loops. Return after already-admitted waves finish.
|
|
153
|
-
*
|
|
154
|
-
* Declare a finite whole-wave allowance (1..300000ms): maximum for parallel lanes, sum for
|
|
155
|
-
* sequential operations. Admit a wave only if its allowance fits before dispatchUntil. Later
|
|
156
|
-
* arrivals cannot renew an admitted wave's allowance. Maintenance bounds each registered wave,
|
|
157
|
-
* interrupts and joins event Scope, then reads local deadlines under the mutation gate.
|
|
158
|
-
*
|
|
159
|
-
* Setup and pendingDeadline are bounded local operations. Persist claims/envelopes before
|
|
160
|
-
* dispatch; interrupted waits leave exact retries/receipts recoverable. Cancellation is local,
|
|
161
|
-
* not remote rollback. Retry accounting is unchanged. Network waits/finalizers must be
|
|
162
|
-
* interruptible; short local atomic commits may be uninterruptible. Hooks never write the raw
|
|
163
|
-
* alarm slot. Required native publication gates belong to ThreadPublication.
|
|
164
|
-
*/
|
|
165
|
-
const ThreadHostMaintenance = Context.Reference("@effect-agent/platform-cloudflare/ThreadHostMaintenance", { defaultValue: () => ({
|
|
166
|
-
dispatchTimeoutMillis: 1,
|
|
167
|
-
drainUntil: () => Effect.void,
|
|
121
|
+
prepare: Effect.succeed({
|
|
122
|
+
timeoutMillis: 1,
|
|
123
|
+
run: Effect.void
|
|
124
|
+
}),
|
|
168
125
|
pendingDeadline: Effect.succeed(Option.none())
|
|
169
126
|
}) });
|
|
127
|
+
/** Independent lanes share the existing alarm; compose hosts by concatenating their lanes. */
|
|
128
|
+
const ThreadHostMaintenance = Context.Reference("@effect-agent/platform-cloudflare/ThreadHostMaintenance", { defaultValue: () => ({ lanes: [] }) });
|
|
170
129
|
const earliestDeadline = (left, right) => Option.isSome(left) ? Option.isSome(right) ? Option.some(Math.min(left.value, right.value)) : left : right;
|
|
171
130
|
/** @internal A committed source operation must not become a failed operation because delivery failed. */
|
|
172
131
|
const publishCommitted = Effect.gen(function* () {
|
|
@@ -313,7 +272,7 @@ var ThreadMutationGate = class ThreadMutationGate extends Context.Service()("@ef
|
|
|
313
272
|
* retain their durable backoff.
|
|
314
273
|
* 3. Keep native and delivery admission open together while finite waves remain active, so
|
|
315
274
|
* fresh replies and abort controls can progress during unrelated cleanup. Close atomically
|
|
316
|
-
* at quiescence, or at the original ten-minute yield deadline,
|
|
275
|
+
* at quiescence, or at the original ten-minute yield deadline, then join admitted waves.
|
|
317
276
|
* 4. Native message delivery retains its driver-owned Claim deadline. Host/backfill waves are
|
|
318
277
|
* bounded independently; incoming native work never restarts or cancels their attempts.
|
|
319
278
|
* Auxiliary failures are reported after the current native opportunity.
|
|
@@ -338,7 +297,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
338
297
|
const authorizer = yield* OperationAuthorizer;
|
|
339
298
|
const projectionDeadline = projection.pendingDeadline.pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Effect.logError("Thread projection deadline unavailable", cause).pipe(Effect.as(Option.some(0)))));
|
|
340
299
|
const pendingDeadline = Effect.gen(function* () {
|
|
341
|
-
return earliestDeadline(earliestDeadline(yield* publication.pendingDeadline, yield* messages.pendingDeadline), earliestDeadline(yield* projectionDeadline, yield* host.pendingDeadline));
|
|
300
|
+
return earliestDeadline(earliestDeadline(yield* publication.pendingDeadline, yield* messages.pendingDeadline), earliestDeadline(yield* projectionDeadline, (yield* Effect.forEach(host.lanes, (lane) => lane.pendingDeadline)).reduce(earliestDeadline, Option.none())));
|
|
342
301
|
});
|
|
343
302
|
const maintenancePassGate = yield* Semaphore.make(1);
|
|
344
303
|
const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));
|
|
@@ -664,47 +623,17 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
664
623
|
const notified = (yield* Stream.toPull(wakes.wakes)).pipe(Effect.asVoid, Effect.catch(() => Effect.never));
|
|
665
624
|
const started = yield* beginNative(observed);
|
|
666
625
|
const auxiliaryScope = yield* Effect.acquireRelease(Scope.make("parallel"), (scope, exit) => Scope.close(scope, exit));
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const initialized = yield* Deferred.make();
|
|
679
|
-
let active = 0;
|
|
680
|
-
let failed = false;
|
|
681
|
-
return {
|
|
682
|
-
activity: {
|
|
683
|
-
ready: Deferred.succeed(initialized, void 0).pipe(Effect.andThen(signalActivity), Effect.asVoid, Effect.uninterruptible),
|
|
684
|
-
subscribeChanges: PubSub.subscribe(checks).pipe(Effect.map(PubSub.take)),
|
|
685
|
-
run: (wave) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
686
|
-
if (!dispatchOpen) return false;
|
|
687
|
-
active++;
|
|
688
|
-
return true;
|
|
689
|
-
}), (admitted) => !admitted ? Effect.void : (allowance === void 0 ? wave : wave.pipe(Effect.timeoutOrElse({
|
|
690
|
-
duration: allowance,
|
|
691
|
-
orElse: () => DurableAlarmError.make({
|
|
692
|
-
operation: "host dispatch allowance",
|
|
693
|
-
message: "The admitted host wave exceeded its allowance; durable work remains pending"
|
|
694
|
-
})
|
|
695
|
-
}))).pipe(Effect.onExit((exit) => Effect.sync(() => {
|
|
696
|
-
if (Exit.isFailure(exit)) failed = true;
|
|
697
|
-
}))), (admitted) => Effect.sync(() => {
|
|
698
|
-
if (admitted) active--;
|
|
699
|
-
}).pipe(Effect.andThen(signalActivity)))
|
|
700
|
-
},
|
|
701
|
-
initialized: Deferred.await(initialized),
|
|
702
|
-
quiet: () => active === 0 && Deferred.isDoneUnsafe(initialized),
|
|
703
|
-
failed: () => failed
|
|
704
|
-
};
|
|
705
|
-
});
|
|
706
|
-
const messageActivity = yield* makeActivity();
|
|
707
|
-
const hostActivity = yield* makeActivity(host.dispatchTimeoutMillis);
|
|
626
|
+
const fork = (work) => Effect.forkIn(work, auxiliaryScope);
|
|
627
|
+
const lanes = host.lanes.map((lane) => ({
|
|
628
|
+
...lane,
|
|
629
|
+
initial: true,
|
|
630
|
+
check: true,
|
|
631
|
+
exhausted: false,
|
|
632
|
+
fiber: void 0
|
|
633
|
+
}));
|
|
634
|
+
let messageExhausted = false;
|
|
635
|
+
let delivery;
|
|
636
|
+
let failure;
|
|
708
637
|
const recovery = {
|
|
709
638
|
queue: yield* Deferred.make(),
|
|
710
639
|
pending: /* @__PURE__ */ new Set(),
|
|
@@ -716,7 +645,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
716
645
|
recovered: 0,
|
|
717
646
|
repaired: false
|
|
718
647
|
};
|
|
719
|
-
const recoveryFiber = yield*
|
|
648
|
+
const recoveryFiber = yield* fork(Effect.gen(function* () {
|
|
720
649
|
for (const threadId of yield* Deferred.await(recovery.queue)) {
|
|
721
650
|
yield* failpoint.hit("maintenance:select:before");
|
|
722
651
|
yield* runTransaction("select old recovery lane", () => ctx.storage.transaction(async (transaction) => {
|
|
@@ -731,90 +660,156 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
731
660
|
recovery.pending.delete(threadId);
|
|
732
661
|
yield* wakes.notify(threadId);
|
|
733
662
|
}
|
|
734
|
-
})
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
operation: "host initialization",
|
|
746
|
-
message: "The host did not account for initial maintenance before its allowance; durable work remains pending"
|
|
747
|
-
})
|
|
748
|
-
}), Effect.andThen(Effect.never));
|
|
749
|
-
yield* Effect.raceFirst(host.drainUntil(stopDispatch, dispatchUntil).pipe(Effect.provideService(ThreadMaintenanceActivity, hostActivity.activity)), initialization);
|
|
750
|
-
}).pipe(Effect.onExit(() => hostActivity.activity.ready))), auxiliaryScope);
|
|
751
|
-
const backfill = yield* Effect.forkIn(drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection), Effect.timeoutOption(config.projectionDispatchTimeoutMillis), Effect.onExit(() => signalActivity)), auxiliaryScope);
|
|
752
|
-
let result = yield* advance(started, yieldAfter, observed, recovery);
|
|
753
|
-
recovery.started = true;
|
|
754
|
-
yield* Deferred.succeed(recovery.queue, []);
|
|
663
|
+
}));
|
|
664
|
+
const backfill = yield* fork(drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection), Effect.timeoutOption(config.projectionDispatchTimeoutMillis)));
|
|
665
|
+
let backfillObserved = false;
|
|
666
|
+
let recoveryObserved = false;
|
|
667
|
+
let native = yield* fork(advance(started, yieldAfter, observed, recovery));
|
|
668
|
+
let result = {
|
|
669
|
+
phase: "caught-up",
|
|
670
|
+
settled: 0,
|
|
671
|
+
nonterminal: started.nonterminal,
|
|
672
|
+
nextAttemptAt: void 0
|
|
673
|
+
};
|
|
755
674
|
let phase = result.phase;
|
|
756
|
-
let settled =
|
|
757
|
-
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
}), auxiliaryScope);
|
|
764
|
-
const retired = yield* Effect.forkChild(Fiber.joinAll([
|
|
765
|
-
deliveryFiber,
|
|
766
|
-
hostJoin,
|
|
767
|
-
backfill,
|
|
768
|
-
recoveryFiber
|
|
769
|
-
]));
|
|
675
|
+
let settled = 0;
|
|
676
|
+
let nativeCheck = false;
|
|
677
|
+
const until = DateTime.toEpochMillis(yieldAfter);
|
|
678
|
+
const dispatchEnd = DateTime.toEpochMillis(dispatchUntil);
|
|
679
|
+
const checkLanes = () => {
|
|
680
|
+
for (const lane of lanes) lane.check = true;
|
|
681
|
+
};
|
|
770
682
|
while (true) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
if (
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
683
|
+
if (native?.pollUnsafe() !== void 0) {
|
|
684
|
+
result = yield* Fiber.join(native);
|
|
685
|
+
native = void 0;
|
|
686
|
+
if (result.phase === "actionable") phase = "actionable";
|
|
687
|
+
settled += result.settled;
|
|
688
|
+
observed.nativeOnly = false;
|
|
689
|
+
recovery.started = true;
|
|
690
|
+
yield* Deferred.succeed(recovery.queue, []);
|
|
691
|
+
checkLanes();
|
|
692
|
+
}
|
|
693
|
+
if (!recoveryObserved && recoveryFiber.pollUnsafe() !== void 0) {
|
|
694
|
+
yield* Fiber.join(recoveryFiber);
|
|
695
|
+
recoveryObserved = true;
|
|
696
|
+
nativeCheck = recovery.needsCheckpoint;
|
|
697
|
+
}
|
|
698
|
+
if (!backfillObserved) {
|
|
699
|
+
const exit = backfill.pollUnsafe();
|
|
700
|
+
if (exit !== void 0) {
|
|
701
|
+
backfillObserved = true;
|
|
702
|
+
if (Exit.isFailure(exit)) failure ??= exit.cause;
|
|
781
703
|
}
|
|
782
704
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
705
|
+
for (const lane of lanes) {
|
|
706
|
+
const exit = lane.fiber?.pollUnsafe();
|
|
707
|
+
if (exit === void 0) continue;
|
|
708
|
+
lane.fiber = void 0;
|
|
709
|
+
if (Exit.isFailure(exit)) {
|
|
710
|
+
failure ??= exit.cause;
|
|
711
|
+
lane.exhausted = true;
|
|
712
|
+
} else lane.check ||= exit.value;
|
|
713
|
+
}
|
|
714
|
+
const deliveryExit = delivery?.pollUnsafe();
|
|
715
|
+
if (deliveryExit !== void 0) {
|
|
716
|
+
delivery = void 0;
|
|
717
|
+
if (Exit.isFailure(deliveryExit)) {
|
|
718
|
+
failure ??= deliveryExit.cause;
|
|
719
|
+
messageExhausted = true;
|
|
720
|
+
}
|
|
796
721
|
}
|
|
722
|
+
if (failure !== void 0 && native === void 0) return yield* Effect.failCause(failure);
|
|
797
723
|
const now = yield* Clock.currentTimeMillis;
|
|
798
|
-
const until = DateTime.toEpochMillis(yieldAfter);
|
|
799
724
|
if (now >= until) break;
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
725
|
+
for (const lane of lanes) {
|
|
726
|
+
if (lane.fiber !== void 0 || lane.exhausted || !lane.check) continue;
|
|
727
|
+
lane.check = false;
|
|
728
|
+
lane.fiber = yield* fork(Effect.gen(function* () {
|
|
729
|
+
yield* Schema.decodeEffect(AuxiliaryDispatchMillis)(lane.dispatchTimeoutMillis).pipe(Effect.mapError((cause) => DurableAlarmError.make({
|
|
730
|
+
operation: "host dispatch allowance",
|
|
731
|
+
message: "Declare an integer whole-wave allowance between 1 and 300000 milliseconds",
|
|
732
|
+
cause
|
|
733
|
+
})));
|
|
734
|
+
if ((yield* Clock.currentTimeMillis) + lane.dispatchTimeoutMillis > dispatchEnd) {
|
|
735
|
+
lane.exhausted = true;
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
return yield* Effect.gen(function* () {
|
|
739
|
+
if (!lane.initial) {
|
|
740
|
+
const deadline = yield* lane.pendingDeadline;
|
|
741
|
+
if (Option.isNone(deadline) || deadline.value > (yield* Clock.currentTimeMillis)) return false;
|
|
742
|
+
}
|
|
743
|
+
const selectedAt = yield* Clock.currentTimeMillis;
|
|
744
|
+
if (selectedAt >= until || selectedAt + lane.dispatchTimeoutMillis > dispatchEnd) {
|
|
745
|
+
lane.exhausted = true;
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
lane.initial = false;
|
|
749
|
+
yield* lane.run;
|
|
750
|
+
return true;
|
|
751
|
+
}).pipe(Effect.scoped, Effect.timeoutOrElse({
|
|
752
|
+
duration: lane.dispatchTimeoutMillis,
|
|
753
|
+
orElse: () => DurableAlarmError.make({
|
|
754
|
+
operation: "host dispatch allowance",
|
|
755
|
+
message: "The admitted host wave exceeded its allowance; durable work remains pending"
|
|
756
|
+
})
|
|
757
|
+
}));
|
|
758
|
+
}));
|
|
759
|
+
}
|
|
760
|
+
if (delivery === void 0 && !messageExhausted) {
|
|
761
|
+
const selected = yield* Effect.gen(function* () {
|
|
762
|
+
const deadline = yield* messages.pendingDeadline;
|
|
763
|
+
return Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis) ? Option.some(yield* messages.prepare) : Option.none();
|
|
764
|
+
}).pipe(Effect.exit);
|
|
765
|
+
if (Exit.isFailure(selected)) {
|
|
766
|
+
failure ??= selected.cause;
|
|
767
|
+
messageExhausted = true;
|
|
768
|
+
} else if (Option.isSome(selected.value)) {
|
|
769
|
+
const wave = selected.value.value;
|
|
770
|
+
const selectedAt = yield* Clock.currentTimeMillis;
|
|
771
|
+
if (selectedAt >= until || selectedAt + wave.timeoutMillis > dispatchEnd) messageExhausted = true;
|
|
772
|
+
else delivery = yield* fork(wave.run);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (native === void 0 && recoveryObserved && !recovery.needsCheckpoint && backfillObserved && delivery === void 0 && lanes.every((lane) => lane.fiber === void 0)) break;
|
|
776
|
+
if (native === void 0 && nativeCheck) {
|
|
777
|
+
nativeCheck = false;
|
|
778
|
+
const checkpoint = recoveryObserved && recovery.needsCheckpoint;
|
|
779
|
+
if (checkpoint) recovery.needsCheckpoint = false;
|
|
780
|
+
native = yield* fork(Effect.gen(function* () {
|
|
781
|
+
const awakened = checkpoint ? started : yield* beginNative(observed);
|
|
782
|
+
return yield* advance(awakened, yieldAfter, observed, recovery, !checkpoint || settled === 0);
|
|
783
|
+
}));
|
|
784
|
+
}
|
|
785
|
+
const next = Math.min(native === void 0 ? result.nextAttemptAt ?? Infinity : Infinity, now + config.wakeScanInterval, until);
|
|
786
|
+
const completing = [
|
|
787
|
+
...native === void 0 ? [] : [native],
|
|
788
|
+
...recoveryObserved ? [] : [recoveryFiber],
|
|
789
|
+
...backfillObserved ? [] : [backfill],
|
|
790
|
+
...delivery === void 0 ? [] : [delivery],
|
|
791
|
+
...lanes.flatMap((lane) => lane.fiber === void 0 ? [] : [lane.fiber])
|
|
792
|
+
];
|
|
793
|
+
if ((yield* Effect.raceAllFirst([...completing.map((fiber) => Fiber.await(fiber).pipe(Effect.as("completed"))), Effect.raceFirst(notified, Effect.sleep(Math.max(0, next - now))).pipe(Effect.as("scan"))])) === "scan") {
|
|
794
|
+
nativeCheck = true;
|
|
795
|
+
checkLanes();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (native !== void 0) {
|
|
799
|
+
result = yield* Fiber.join(native);
|
|
811
800
|
if (result.phase === "actionable") phase = "actionable";
|
|
812
801
|
settled += result.settled;
|
|
813
802
|
observed.nativeOnly = false;
|
|
814
|
-
|
|
803
|
+
recovery.started = true;
|
|
804
|
+
yield* Deferred.succeed(recovery.queue, []);
|
|
815
805
|
}
|
|
816
|
-
yield*
|
|
817
|
-
|
|
806
|
+
yield* Fiber.joinAll([
|
|
807
|
+
recoveryFiber,
|
|
808
|
+
backfill,
|
|
809
|
+
...delivery === void 0 ? [] : [delivery],
|
|
810
|
+
...lanes.flatMap((lane) => lane.fiber === void 0 ? [] : [lane.fiber])
|
|
811
|
+
]);
|
|
812
|
+
if (failure !== void 0) return yield* Effect.failCause(failure);
|
|
818
813
|
if (recovery.needsCheckpoint) {
|
|
819
814
|
result = yield* advance(started, yieldAfter, observed, recovery, false);
|
|
820
815
|
if (result.phase === "actionable") phase = "actionable";
|
|
@@ -875,6 +870,6 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
875
870
|
}));
|
|
876
871
|
};
|
|
877
872
|
//#endregion
|
|
878
|
-
export { DurableAlarmError, DurableAlarmService, MaintenancePassReport, ThreadHostMaintenance, ThreadMaintenance,
|
|
873
|
+
export { DurableAlarmError, DurableAlarmService, MaintenancePassReport, ThreadHostMaintenance, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, ThreadRecoveryFault, publishCommitted, Alarm_exports as t };
|
|
879
874
|
|
|
880
875
|
//# sourceMappingURL=Alarm.mjs.map
|