-
Notifications
You must be signed in to change notification settings - Fork 10
fix(sync): restore foreground catch-up backpressure recovery #1934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -271,6 +271,17 @@ export { | |
| // deep-importing the compiled `dist/` module. | ||
| export { mapWithConcurrency } from './map-with-concurrency.js'; | ||
| export { CATCHUP_MAX_CONCURRENT_PEER_SYNCS } from './sync/catchup-concurrency.js'; | ||
| export { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Issue: The catch-up policy helper leaks internal orchestration onto the public agent surface What's wrong Example Suggested direction Confidence note For Agents |
||
| CATCHUP_BACKPRESSURE_RETRY_DELAYS_MS, | ||
| FOREGROUND_CATCHUP_SYNC_PRIORITY, | ||
| catchupPriorityForMode, | ||
| runCatchupPlanesWithPolicy, | ||
| type CatchupMode, | ||
| type CatchupPlaneContext, | ||
| type CatchupPlanePolicyOptions, | ||
| type CatchupPlanePolicyResult, | ||
| type CatchupPlaneResult, | ||
| } from './sync/catchup-policy.js'; | ||
| export { | ||
| classifyDurableProgress, | ||
| createFailedPeerDurableSyncResult, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| export type CatchupMode = 'background' | 'foreground'; | ||
|
|
||
| export const FOREGROUND_CATCHUP_SYNC_PRIORITY = 2_000; | ||
| export const CATCHUP_BACKPRESSURE_RETRY_DELAYS_MS = [100, 250, 500] as const; | ||
|
|
||
| export interface CatchupPlaneResult { | ||
| deferredBackpressure?: number; | ||
| } | ||
|
|
||
| export interface CatchupPlaneContext { | ||
| priority?: number; | ||
| } | ||
|
|
||
| export interface CatchupPlanePolicyOptions< | ||
| TDurable extends CatchupPlaneResult, | ||
| TShared extends CatchupPlaneResult, | ||
| > { | ||
| mode: CatchupMode; | ||
| includeSharedMemory: boolean; | ||
| syncDurable: (context: CatchupPlaneContext) => Promise<TDurable>; | ||
| syncSharedMemory: (context: CatchupPlaneContext) => Promise<TShared>; | ||
| retryDelaysMs?: readonly number[]; | ||
| wait?: (delayMs: number) => Promise<void>; | ||
| } | ||
|
|
||
| export interface CatchupPlanePolicyResult< | ||
| TDurable extends CatchupPlaneResult, | ||
| TShared extends CatchupPlaneResult, | ||
| > { | ||
| durable: TDurable; | ||
| shared: TShared | null; | ||
| } | ||
|
|
||
| export function catchupPriorityForMode(mode: CatchupMode): number | undefined { | ||
| return mode === 'foreground' ? FOREGROUND_CATCHUP_SYNC_PRIORITY : undefined; | ||
| } | ||
|
|
||
| async function runCatchupPlane<T extends CatchupPlaneResult>( | ||
| mode: CatchupMode, | ||
| run: (context: CatchupPlaneContext) => Promise<T>, | ||
| options: Pick<CatchupPlanePolicyOptions<T, T>, 'retryDelaysMs' | 'wait'>, | ||
| ): Promise<T> { | ||
| const context = { priority: catchupPriorityForMode(mode) }; | ||
| let result = await run(context); | ||
| if (mode !== 'foreground') return result; | ||
|
|
||
| const retryDelaysMs = options.retryDelaysMs ?? CATCHUP_BACKPRESSURE_RETRY_DELAYS_MS; | ||
| const wait = options.wait ?? ((delayMs: number) => new Promise<void>((resolve) => { | ||
| setTimeout(resolve, delayMs); | ||
| })); | ||
| for (const delayMs of retryDelaysMs) { | ||
| if ((result.deferredBackpressure ?? 0) === 0) return result; | ||
| await wait(delayMs); | ||
| result = await run(context); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Canonical foreground/background catch-up policy shared by the in-agent and | ||
| * worker-backed runners. Durable metadata must settle before SWM starts; when | ||
| * only SWM is deferred, retries never refetch the already-completed durable | ||
| * plane. | ||
| */ | ||
| export async function runCatchupPlanesWithPolicy< | ||
| TDurable extends CatchupPlaneResult, | ||
| TShared extends CatchupPlaneResult, | ||
| >( | ||
| options: CatchupPlanePolicyOptions<TDurable, TShared>, | ||
| ): Promise<CatchupPlanePolicyResult<TDurable, TShared>> { | ||
| const durable = await runCatchupPlane(options.mode, options.syncDurable, options); | ||
| if (!options.includeSharedMemory || (durable.deferredBackpressure ?? 0) > 0) { | ||
| return { durable, shared: null }; | ||
| } | ||
|
|
||
| const shared = await runCatchupPlane(options.mode, options.syncSharedMemory, options); | ||
| return { durable, shared }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Issue: Foreground mode is not verified through the public catch-up API
What's wrong
The changed behavior introduces a public
modeoption, but the current agent-path regression test bypasses the code that reads and forwards that option. This leaves the user-facing foreground catch-up path under-verified.Example
If
modewere accidentally omitted from the object passed torunCatchupOverPeers,syncContextGraphFromConnectedPeers(..., { includeSharedMemory: true, mode: 'foreground' })would still run as background catch-up, but the new private-helper test would continue to pass.Suggested direction
Cover the public API propagation path, not only
runCatchupOverPeersdirectly.For Agents
Add or adjust a test to call
agent.syncContextGraphFromConnectedPeers('coalesced-cg', { includeSharedMemory: true, mode: 'foreground' })with one connected peer and stubbed durable/SWM sync methods. Prove the first durable deferral is retried withFOREGROUND_CATCHUP_SYNC_PRIORITYbefore SWM starts, while preserving existing background behavior.