@effect-agent/platform-cloudflare 0.1.0-beta.125 → 0.1.0-beta.129
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 +2 -1
- package/dist/Alarm.mjs +132 -69
- package/dist/Alarm.mjs.map +1 -1
- package/dist/{BrowserSession-DuOUcC6G.mjs → BrowserSession-BplIoNfi.mjs} +3 -3
- package/dist/BrowserSession-BplIoNfi.mjs.map +1 -0
- package/dist/BrowserSession.mjs +2 -2
- package/dist/CloudflareBindings.mjs +1 -1
- package/dist/CloudflareScheduling.mjs +1 -1
- package/dist/CloudflareSubscriptions.mjs +1 -1
- package/dist/CloudflareThreadClient.d.mts +106 -51
- package/dist/CloudflareThreadClient.mjs +1 -1
- package/dist/{InteractiveBrowser-DeOlcu-2.mjs → InteractiveBrowser-D-xxDv_f.mjs} +7 -4
- package/dist/InteractiveBrowser-D-xxDv_f.mjs.map +1 -0
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/{ThreadObject-BTo1jqab.d.mts → ThreadObject-9ho9DTjd.d.mts} +28 -28
- package/dist/{ThreadObject-DaC-IhCm.mjs → ThreadObject-CVhZC-7l.mjs} +2 -2
- package/dist/{ThreadObject-DaC-IhCm.mjs.map → ThreadObject-CVhZC-7l.mjs.map} +1 -1
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/src/Alarm.ts +252 -106
- package/src/BrowserSession.ts +3 -1
- package/src/internal/browser-binding.ts +5 -3
- package/dist/BrowserSession-DuOUcC6G.mjs.map +0 -1
- package/dist/InteractiveBrowser-DeOlcu-2.mjs.map +0 -1
package/dist/Alarm.d.mts
CHANGED
|
@@ -226,7 +226,8 @@ declare const ThreadMaintenance_base: Context.ServiceClass<ThreadMaintenance, "@
|
|
|
226
226
|
* 2. Reconcile before each head Attempt, then checkpoint only the observed generation. A racing
|
|
227
227
|
* producer keeps its generation dirty and immediately eligible. Quiescent native retries
|
|
228
228
|
* retain their durable backoff.
|
|
229
|
-
* 3.
|
|
229
|
+
* 3. Admit at most two independent Thread Attempts, one active head per Thread.
|
|
230
|
+
* Keep native and delivery admission open together while finite waves remain active, so
|
|
230
231
|
* fresh replies and abort controls can progress during unrelated cleanup. Close atomically
|
|
231
232
|
* at quiescence, or at the original ten-minute yield deadline, then join admitted waves.
|
|
232
233
|
* 4. Native message delivery retains its driver-owned Claim deadline. Host/backfill waves are
|
package/dist/Alarm.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { OperationAuthorizationRequest, OperationAuthorizer } from "effect-agent
|
|
|
10
10
|
import { AbortIntentRequest, SubmissionLedger } from "effect-agent/submission-ledger";
|
|
11
11
|
import { ThreadProjectionMaintenance, drainDue } from "effect-agent/thread-projection-maintenance";
|
|
12
12
|
import { WakeScheduler } from "effect-agent/wake-scheduler";
|
|
13
|
+
import { DurableObjectStorage } from "effect-cf";
|
|
13
14
|
import { SqlClient } from "effect/unstable/sql/SqlClient";
|
|
14
15
|
//#region src/Alarm.ts
|
|
15
16
|
var Alarm_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -47,13 +48,14 @@ const alarmFailure = (operation) => (cause) => DurableAlarmError.make({
|
|
|
47
48
|
message: safeCauseMessage(cause, "The Cloudflare alarm API failed without a diagnostic"),
|
|
48
49
|
cause
|
|
49
50
|
});
|
|
50
|
-
const
|
|
51
|
-
const body = Effect.uninterruptible(
|
|
52
|
-
try: execute,
|
|
53
|
-
catch: alarmFailure(operation)
|
|
54
|
-
}));
|
|
51
|
+
const makeStorageEffect = Effect.map(SqlClient, (sql) => (operation, execute) => Effect.flatMap(Effect.serviceOption(sql.transactionService), (current) => {
|
|
52
|
+
const body = Effect.uninterruptible(execute);
|
|
55
53
|
return current._tag === "Some" ? body : Effect.scoped(Effect.andThen(sql.reserve.pipe(Effect.mapError(alarmFailure(operation))), body));
|
|
56
54
|
}));
|
|
55
|
+
const makeStorageOperation = Effect.map(makeStorageEffect, (run) => (operation, execute) => run(operation, Effect.tryPromise({
|
|
56
|
+
try: execute,
|
|
57
|
+
catch: alarmFailure(operation)
|
|
58
|
+
})));
|
|
57
59
|
/** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */
|
|
58
60
|
var DurableAlarmService = class DurableAlarmService extends Context.Service()("@effect-agent/platform-cloudflare/DurableAlarmService") {
|
|
59
61
|
static layer = Layer.effect(DurableAlarmService)(Effect.gen(function* () {
|
|
@@ -164,6 +166,7 @@ const decodeRecoveryFault = (threadId, encoded) => {
|
|
|
164
166
|
return fault;
|
|
165
167
|
};
|
|
166
168
|
const encodeRecoveryFault = Schema.encodeSync(ThreadRecoveryFault);
|
|
169
|
+
const nativeDispatchConcurrency = 2;
|
|
167
170
|
var MaintenanceRetry = class extends Schema.Class("MaintenanceRetry")({
|
|
168
171
|
generation: MaintenanceGeneration,
|
|
169
172
|
notBefore: Schema.Finite,
|
|
@@ -270,7 +273,8 @@ var ThreadMutationGate = class ThreadMutationGate extends Context.Service()("@ef
|
|
|
270
273
|
* 2. Reconcile before each head Attempt, then checkpoint only the observed generation. A racing
|
|
271
274
|
* producer keeps its generation dirty and immediately eligible. Quiescent native retries
|
|
272
275
|
* retain their durable backoff.
|
|
273
|
-
* 3.
|
|
276
|
+
* 3. Admit at most two independent Thread Attempts, one active head per Thread.
|
|
277
|
+
* Keep native and delivery admission open together while finite waves remain active, so
|
|
274
278
|
* fresh replies and abort controls can progress during unrelated cleanup. Close atomically
|
|
275
279
|
* at quiescence, or at the original ten-minute yield deadline, then join admitted waves.
|
|
276
280
|
* 4. Native message delivery retains its driver-owned Claim deadline. Host/backfill waves are
|
|
@@ -288,6 +292,8 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
288
292
|
const alarm = yield* DurableAlarmService;
|
|
289
293
|
const config = yield* CloudflareDurableRuntimeConfig;
|
|
290
294
|
const { ctx } = yield* DurableObjectContext;
|
|
295
|
+
const storage = DurableObjectStorage.fromDurableObjectStorage(ctx.storage);
|
|
296
|
+
const runStorage = yield* makeStorageEffect;
|
|
291
297
|
const failpoint = yield* ThreadMaintenanceFailpoint;
|
|
292
298
|
const mutations = yield* ThreadMutationGate;
|
|
293
299
|
const publication = yield* ThreadPublication;
|
|
@@ -448,7 +454,45 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
448
454
|
};
|
|
449
455
|
}));
|
|
450
456
|
});
|
|
451
|
-
const
|
|
457
|
+
const attemptSelected = Effect.fn("ThreadMaintenance.attemptSelected")(function* (selected, previousRetries, yieldAfter, recovery) {
|
|
458
|
+
yield* recoverThread(selected.threadId, recovery);
|
|
459
|
+
let bindingFailure;
|
|
460
|
+
const settlement = recovery.faults.has(selected.threadId) ? Option.none() : yield* runtime.processThreadHead(selected.threadId, { yieldAfter }).pipe(Effect.catchTag("BindingUnavailable", (failure) => {
|
|
461
|
+
bindingFailure = failure;
|
|
462
|
+
return Effect.succeed(Option.none());
|
|
463
|
+
}));
|
|
464
|
+
const previous = previousRetries.find((retry) => retry.submissionId === selected.submissionId);
|
|
465
|
+
let retry;
|
|
466
|
+
let reportBindingFailure = false;
|
|
467
|
+
if (bindingFailure !== void 0) {
|
|
468
|
+
const now = yield* Clock.currentTimeMillis;
|
|
469
|
+
const attempts = Math.min(30, (previous?.attempts ?? 0) + 1);
|
|
470
|
+
reportBindingFailure = previous === void 0 || now - previous.reportedAt >= 9e5;
|
|
471
|
+
retry = BindingRetry.make({
|
|
472
|
+
threadId: selected.threadId,
|
|
473
|
+
submissionId: selected.submissionId,
|
|
474
|
+
attempts,
|
|
475
|
+
notBefore: now + Math.min(6e4, 5e3 * 2 ** (attempts - 1)),
|
|
476
|
+
reportedAt: reportBindingFailure ? now : previous?.reportedAt ?? now
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
if (retry !== void 0 || previous !== void 0) {
|
|
480
|
+
yield* failpoint.hit("maintenance:binding-retry:before");
|
|
481
|
+
yield* runStorage("record submission binding retry", storage.transaction((transaction) => Effect.gen(function* () {
|
|
482
|
+
const encoded = yield* transaction.get(MAINTENANCE_STATE_KEY);
|
|
483
|
+
const state = encoded === void 0 ? initialMaintenanceState() : yield* Schema.decodeUnknownEffect(ThreadMaintenanceState)(encoded);
|
|
484
|
+
const bindingRetries = [...(state.bindingRetries ?? []).filter((entry) => entry.submissionId !== selected.submissionId), ...retry === void 0 ? [] : [retry]];
|
|
485
|
+
yield* transaction.put(MAINTENANCE_STATE_KEY, yield* Schema.encodeEffect(ThreadMaintenanceState)(ThreadMaintenanceState.make({
|
|
486
|
+
...state,
|
|
487
|
+
bindingRetries
|
|
488
|
+
})));
|
|
489
|
+
})).pipe(Effect.mapError(alarmFailure("record submission binding retry"))));
|
|
490
|
+
yield* failpoint.hit("maintenance:binding-retry:after");
|
|
491
|
+
}
|
|
492
|
+
if (bindingFailure !== void 0) yield* reportBindingFailure ? Effect.logError("Thread awaits a current agent binding; original work remains pending", Cause.fail(bindingFailure)) : Effect.logDebug("Thread binding retry remains pending", Cause.fail(bindingFailure));
|
|
493
|
+
return Option.isSome(settlement) ? 1 : 0;
|
|
494
|
+
});
|
|
495
|
+
const advance = Effect.fn("ThreadMaintenance.advance")(function* (started, yieldAfter, observed, recovery, native, dispatch = true, reserved = new Set(native.active.keys())) {
|
|
452
496
|
const deadline = yield* publication.pendingDeadline;
|
|
453
497
|
if (started._tag === "Actionable" || Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis)) yield* publication.drain;
|
|
454
498
|
const pending = yield* publication.pendingDeadline;
|
|
@@ -462,11 +506,20 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
462
506
|
}));
|
|
463
507
|
return {
|
|
464
508
|
phase: "caught-up",
|
|
465
|
-
settled: 0,
|
|
466
509
|
nonterminal: started.nonterminal,
|
|
467
510
|
nextAttemptAt
|
|
468
511
|
};
|
|
469
512
|
}
|
|
513
|
+
if (native.generation !== started.generation) {
|
|
514
|
+
native.deferred.clear();
|
|
515
|
+
native.generation = started.generation;
|
|
516
|
+
}
|
|
517
|
+
if (native.active.size > 0 && native.scanGeneration === started.generation) return {
|
|
518
|
+
phase: "actionable",
|
|
519
|
+
nonterminal: started.nonterminal,
|
|
520
|
+
nextAttemptAt: void 0
|
|
521
|
+
};
|
|
522
|
+
native.scanGeneration = started.activeAtStart === 0 ? started.generation : void 0;
|
|
470
523
|
observed.nativeOnly = true;
|
|
471
524
|
const observation = recovery.observation ??= {
|
|
472
525
|
generation: started.generation,
|
|
@@ -492,7 +545,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
492
545
|
}
|
|
493
546
|
const stopping = /* @__PURE__ */ new Set();
|
|
494
547
|
for (const head of heads.values()) {
|
|
495
|
-
if (head.state !== "ready" || recovery.pending.has(head.threadId) || recoveryFaults.has(head.threadId)) continue;
|
|
548
|
+
if (head.state !== "ready" || native.active.has(head.threadId) || recovery.pending.has(head.threadId) || recoveryFaults.has(head.threadId)) continue;
|
|
496
549
|
const intent = yield* isolateRecovery(ledger.readAbortIntent(AbortIntentRequest.make({ submissionId: head.submissionId })), {
|
|
497
550
|
timeout: recoveryConfig.recoveryTimeout,
|
|
498
551
|
phase: () => "recovery",
|
|
@@ -517,8 +570,10 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
517
570
|
const selection = yield* runTransaction("select maintenance lane", () => ctx.storage.transaction(async (transaction) => {
|
|
518
571
|
const { state } = await readMaintenanceState(transaction);
|
|
519
572
|
const retries = (state.bindingRetries ?? []).filter((retry) => heads.get(retry.threadId)?.submissionId === retry.submissionId);
|
|
520
|
-
const runnable = eligible.filter((threadId) => !retries.some((retry) => retry.threadId === threadId && retry.notBefore > selectionTime));
|
|
521
|
-
const
|
|
573
|
+
const runnable = eligible.filter((threadId) => !reserved.has(threadId) && !native.active.has(threadId) && !native.deferred.has(threadId) && !retries.some((retry) => retry.threadId === threadId && retry.notBefore > selectionTime));
|
|
574
|
+
const pivot = Math.max(0, runnable.findIndex((threadId) => state.lastServedThreadId === void 0 || threadId > state.lastServedThreadId));
|
|
575
|
+
const selected = dispatch ? [...runnable.slice(pivot), ...runnable.slice(0, pivot)].slice(0, nativeDispatchConcurrency - reserved.size) : [];
|
|
576
|
+
const next = selected.at(-1);
|
|
522
577
|
if (next !== void 0) await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(ThreadMaintenanceState.make({
|
|
523
578
|
...state,
|
|
524
579
|
lastServedThreadId: next
|
|
@@ -530,7 +585,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
530
585
|
const after = backlog.filter((threadId) => state.lastRecoveredThreadId === void 0 || threadId > state.lastRecoveredThreadId);
|
|
531
586
|
const before = backlog.filter((threadId) => state.lastRecoveredThreadId !== void 0 && threadId <= state.lastRecoveredThreadId);
|
|
532
587
|
return {
|
|
533
|
-
selected
|
|
588
|
+
selected,
|
|
534
589
|
retries,
|
|
535
590
|
backlog: [...after, ...before]
|
|
536
591
|
};
|
|
@@ -542,45 +597,19 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
542
597
|
for (const threadId of selection.backlog) recovery.pending.add(threadId);
|
|
543
598
|
yield* Deferred.succeed(recovery.queue, selection.backlog);
|
|
544
599
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
bindingFailure = failure;
|
|
551
|
-
return Effect.succeed(Option.none());
|
|
552
|
-
}));
|
|
553
|
-
if (selected !== void 0) {
|
|
554
|
-
const previous = retries.find((retry) => retry.submissionId === selected.submissionId);
|
|
555
|
-
let retry;
|
|
556
|
-
let reportBindingFailure = false;
|
|
557
|
-
if (bindingFailure !== void 0) {
|
|
558
|
-
const now = yield* Clock.currentTimeMillis;
|
|
559
|
-
const attempts = Math.min(30, (previous?.attempts ?? 0) + 1);
|
|
560
|
-
reportBindingFailure = previous === void 0 || now - previous.reportedAt >= 9e5;
|
|
561
|
-
retry = BindingRetry.make({
|
|
562
|
-
threadId: selected.threadId,
|
|
563
|
-
submissionId: selected.submissionId,
|
|
564
|
-
attempts,
|
|
565
|
-
notBefore: now + Math.min(6e4, 5e3 * 2 ** (attempts - 1)),
|
|
566
|
-
reportedAt: reportBindingFailure ? now : previous?.reportedAt ?? now
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
if (retry !== void 0 || previous !== void 0) {
|
|
570
|
-
yield* failpoint.hit("maintenance:binding-retry:before");
|
|
571
|
-
retries = yield* runTransaction("record submission binding retry", () => ctx.storage.transaction(async (transaction) => {
|
|
572
|
-
const { state } = await readMaintenanceState(transaction);
|
|
573
|
-
const bindingRetries = [...(state.bindingRetries ?? []).filter((entry) => entry.submissionId !== selected.submissionId), ...retry === void 0 ? [] : [retry]];
|
|
574
|
-
await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(ThreadMaintenanceState.make({
|
|
575
|
-
...state,
|
|
576
|
-
bindingRetries
|
|
577
|
-
})));
|
|
578
|
-
return bindingRetries;
|
|
579
|
-
}));
|
|
580
|
-
yield* failpoint.hit("maintenance:binding-retry:after");
|
|
581
|
-
}
|
|
582
|
-
if (bindingFailure !== void 0) yield* reportBindingFailure ? Effect.logError("Thread awaits a current agent binding; original work remains pending", Cause.fail(bindingFailure)) : Effect.logDebug("Thread binding retry remains pending", Cause.fail(bindingFailure));
|
|
600
|
+
for (const threadId of selection.selected) {
|
|
601
|
+
const selected = heads.get(threadId);
|
|
602
|
+
if (selected === void 0) continue;
|
|
603
|
+
native.dispatched++;
|
|
604
|
+
native.active.set(threadId, yield* Effect.forkIn(attemptSelected(selected, selection.retries, yieldAfter, recovery), native.scope));
|
|
583
605
|
}
|
|
606
|
+
if (native.active.size > 0) return {
|
|
607
|
+
phase: "actionable",
|
|
608
|
+
nonterminal: current.length,
|
|
609
|
+
nextAttemptAt: void 0,
|
|
610
|
+
dispatched: selection.selected.length > 0
|
|
611
|
+
};
|
|
612
|
+
const retries = selection.retries;
|
|
584
613
|
const remaining = yield* Stream.runCollect(ledger.scanNonterminal);
|
|
585
614
|
const waitingHeads = /* @__PURE__ */ new Map();
|
|
586
615
|
const autonomous = remaining.some((snapshot) => {
|
|
@@ -590,7 +619,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
590
619
|
if (headWaiting === true && snapshot.state === "ready" && reports.get(snapshot.submissionId)?.decision._tag === "ApplyInput") return false;
|
|
591
620
|
return !waiting(snapshot);
|
|
592
621
|
});
|
|
593
|
-
const progressed =
|
|
622
|
+
const progressed = native.progressed || recovery.repaired;
|
|
594
623
|
const now = yield* Clock.currentTimeMillis;
|
|
595
624
|
const ordinaryDelay = autonomous ? yield* rearmDelay(progressed, started.stalls) : 0;
|
|
596
625
|
const nextEligible = [...recoveryFaults.values()].map((fault) => fault.retryAt).concat(eligible.map((threadId) => retries.find((retry) => retry.submissionId === heads.get(threadId)?.submissionId)?.notBefore ?? now));
|
|
@@ -619,9 +648,11 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
619
648
|
return mutationOverlap || next.dirty > next.processed ? now + minimumAlarmDelay : void 0;
|
|
620
649
|
})));
|
|
621
650
|
yield* failpoint.hit("maintenance:checkpoint:after");
|
|
651
|
+
native.deferred.clear();
|
|
652
|
+
native.progressed = false;
|
|
653
|
+
native.needsCheckpoint = false;
|
|
622
654
|
return {
|
|
623
655
|
phase: "actionable",
|
|
624
|
-
settled: Option.isSome(settlement) ? 1 : 0,
|
|
625
656
|
nonterminal: remaining.length,
|
|
626
657
|
nextAttemptAt
|
|
627
658
|
};
|
|
@@ -671,15 +702,22 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
671
702
|
const backfill = yield* fork(drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection), Effect.timeoutOption(config.projectionDispatchTimeoutMillis)));
|
|
672
703
|
let backfillObserved = false;
|
|
673
704
|
let recoveryObserved = false;
|
|
674
|
-
|
|
705
|
+
const dispatch = {
|
|
706
|
+
scope: auxiliaryScope,
|
|
707
|
+
active: /* @__PURE__ */ new Map(),
|
|
708
|
+
deferred: /* @__PURE__ */ new Set(),
|
|
709
|
+
dispatched: 0,
|
|
710
|
+
settled: 0,
|
|
711
|
+
progressed: false,
|
|
712
|
+
needsCheckpoint: false
|
|
713
|
+
};
|
|
714
|
+
let native = yield* fork(advance(started, yieldAfter, observed, recovery, dispatch));
|
|
675
715
|
let result = {
|
|
676
716
|
phase: "caught-up",
|
|
677
|
-
settled: 0,
|
|
678
717
|
nonterminal: started.nonterminal,
|
|
679
718
|
nextAttemptAt: void 0
|
|
680
719
|
};
|
|
681
720
|
let phase = result.phase;
|
|
682
|
-
let settled = 0;
|
|
683
721
|
let nativeCheck = false;
|
|
684
722
|
const until = DateTime.toEpochMillis(yieldAfter);
|
|
685
723
|
const dispatchEnd = DateTime.toEpochMillis(dispatchUntil);
|
|
@@ -691,16 +729,33 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
691
729
|
result = yield* Fiber.join(native);
|
|
692
730
|
native = void 0;
|
|
693
731
|
if (result.phase === "actionable") phase = "actionable";
|
|
694
|
-
|
|
695
|
-
|
|
732
|
+
observed.nativeOnly = dispatch.active.size > 0;
|
|
733
|
+
if (result.dispatched === true && dispatch.active.size < nativeDispatchConcurrency) {
|
|
734
|
+
dispatch.scanGeneration = void 0;
|
|
735
|
+
nativeCheck = true;
|
|
736
|
+
}
|
|
696
737
|
recovery.started = true;
|
|
697
738
|
yield* Deferred.succeed(recovery.queue, []);
|
|
698
739
|
checkLanes();
|
|
699
740
|
}
|
|
741
|
+
for (const [threadId, attempt] of dispatch.active) {
|
|
742
|
+
const exit = attempt.pollUnsafe();
|
|
743
|
+
if (exit === void 0) continue;
|
|
744
|
+
dispatch.active.delete(threadId);
|
|
745
|
+
dispatch.scanGeneration = void 0;
|
|
746
|
+
if (Exit.isFailure(exit)) return yield* Effect.failCause(exit.cause);
|
|
747
|
+
dispatch.settled += exit.value;
|
|
748
|
+
dispatch.progressed ||= exit.value > 0;
|
|
749
|
+
dispatch.needsCheckpoint = true;
|
|
750
|
+
if (exit.value === 0) dispatch.deferred.add(threadId);
|
|
751
|
+
nativeCheck = true;
|
|
752
|
+
checkLanes();
|
|
753
|
+
}
|
|
700
754
|
if (!recoveryObserved && recoveryFiber.pollUnsafe() !== void 0) {
|
|
701
755
|
yield* Fiber.join(recoveryFiber);
|
|
702
756
|
recoveryObserved = true;
|
|
703
|
-
|
|
757
|
+
dispatch.scanGeneration = void 0;
|
|
758
|
+
nativeCheck ||= recovery.needsCheckpoint;
|
|
704
759
|
}
|
|
705
760
|
if (!backfillObserved) {
|
|
706
761
|
const exit = backfill.pollUnsafe();
|
|
@@ -726,7 +781,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
726
781
|
messageExhausted = true;
|
|
727
782
|
}
|
|
728
783
|
}
|
|
729
|
-
if (failure !== void 0 && native === void 0) return yield* Effect.failCause(failure);
|
|
784
|
+
if (failure !== void 0 && native === void 0 && dispatch.active.size === 0) return yield* Effect.failCause(failure);
|
|
730
785
|
const now = yield* Clock.currentTimeMillis;
|
|
731
786
|
if (now >= until) break;
|
|
732
787
|
for (const lane of lanes) {
|
|
@@ -779,19 +834,22 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
779
834
|
else delivery = yield* fork(wave.run);
|
|
780
835
|
}
|
|
781
836
|
}
|
|
782
|
-
if (native === void 0 && recoveryObserved && !recovery.needsCheckpoint && backfillObserved && delivery === void 0 && lanes.every((lane) => lane.fiber === void 0)) break;
|
|
783
|
-
if (native === void 0 && nativeCheck) {
|
|
837
|
+
if (native === void 0 && dispatch.active.size === 0 && recoveryObserved && !recovery.needsCheckpoint && backfillObserved && delivery === void 0 && lanes.every((lane) => lane.fiber === void 0)) break;
|
|
838
|
+
if (native === void 0 && nativeCheck && dispatch.active.size < nativeDispatchConcurrency) {
|
|
784
839
|
nativeCheck = false;
|
|
785
|
-
const
|
|
840
|
+
const nativeCheckpoint = dispatch.needsCheckpoint && dispatch.active.size === 0;
|
|
841
|
+
const checkpoint = recoveryObserved && recovery.needsCheckpoint || nativeCheckpoint;
|
|
786
842
|
if (checkpoint) recovery.needsCheckpoint = false;
|
|
843
|
+
const reserved = new Set(dispatch.active.keys());
|
|
787
844
|
native = yield* fork(Effect.gen(function* () {
|
|
788
845
|
const awakened = checkpoint ? started : yield* beginNative(observed);
|
|
789
|
-
return yield* advance(awakened, yieldAfter, observed, recovery, !checkpoint || settled === 0);
|
|
846
|
+
return yield* advance(awakened, yieldAfter, observed, recovery, dispatch, failure === void 0 && !nativeCheckpoint && (!checkpoint || dispatch.settled === 0), reserved);
|
|
790
847
|
}));
|
|
791
848
|
}
|
|
792
849
|
const next = Math.min(native === void 0 ? result.nextAttemptAt ?? Infinity : Infinity, now + config.wakeScanInterval, until);
|
|
793
850
|
const completing = [
|
|
794
851
|
...native === void 0 ? [] : [native],
|
|
852
|
+
...dispatch.active.values(),
|
|
795
853
|
...recoveryObserved ? [] : [recoveryFiber],
|
|
796
854
|
...backfillObserved ? [] : [backfill],
|
|
797
855
|
...delivery === void 0 ? [] : [delivery],
|
|
@@ -805,11 +863,17 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
805
863
|
if (native !== void 0) {
|
|
806
864
|
result = yield* Fiber.join(native);
|
|
807
865
|
if (result.phase === "actionable") phase = "actionable";
|
|
808
|
-
|
|
809
|
-
observed.nativeOnly = false;
|
|
866
|
+
observed.nativeOnly = dispatch.active.size > 0;
|
|
810
867
|
recovery.started = true;
|
|
811
868
|
yield* Deferred.succeed(recovery.queue, []);
|
|
812
869
|
}
|
|
870
|
+
for (const attempt of dispatch.active.values()) {
|
|
871
|
+
const settled = yield* Fiber.join(attempt);
|
|
872
|
+
dispatch.settled += settled;
|
|
873
|
+
dispatch.progressed ||= settled > 0;
|
|
874
|
+
}
|
|
875
|
+
dispatch.active.clear();
|
|
876
|
+
observed.nativeOnly = false;
|
|
813
877
|
yield* Fiber.joinAll([
|
|
814
878
|
recoveryFiber,
|
|
815
879
|
backfill,
|
|
@@ -817,10 +881,9 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
817
881
|
...lanes.flatMap((lane) => lane.fiber === void 0 ? [] : [lane.fiber])
|
|
818
882
|
]);
|
|
819
883
|
if (failure !== void 0) return yield* Effect.failCause(failure);
|
|
820
|
-
if (recovery.needsCheckpoint) {
|
|
821
|
-
result = yield* advance(started, yieldAfter, observed, recovery, false);
|
|
884
|
+
if (recovery.needsCheckpoint || dispatch.dispatched > 0) {
|
|
885
|
+
result = yield* advance(started, yieldAfter, observed, recovery, dispatch, false);
|
|
822
886
|
if (result.phase === "actionable") phase = "actionable";
|
|
823
|
-
settled += result.settled;
|
|
824
887
|
}
|
|
825
888
|
yield* Scope.close(auxiliaryScope, Exit.void);
|
|
826
889
|
yield* failpoint.hit("maintenance:finish:before");
|
|
@@ -843,7 +906,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
843
906
|
const report = MaintenancePassReport.make({
|
|
844
907
|
phase,
|
|
845
908
|
recovered: recovery.recovered,
|
|
846
|
-
settled,
|
|
909
|
+
settled: dispatch.settled,
|
|
847
910
|
nonterminal: result.nonterminal,
|
|
848
911
|
alarm: disposition
|
|
849
912
|
});
|