* Run a host-scheduled pending pull from its persisted cursor. * * The call shares the backend's mutation FIFO with push, pull, and * command brackets. A successful pull clears the host's durable * intent. A failed pull advances bounded exponential backoff; the * last failed attempt r
(id?: string)
| 593 | * last failed attempt remains stored and is reported as exhausted. |
| 594 | */ |
| 595 | retryPendingSync(id?: string): Promise<WorkspaceRetryPendingSyncResult> { |
| 596 | return this.#serialize(id, async (resolvedId) => { |
| 597 | if (resolvedId === undefined) { |
| 598 | throw new Error("Workspace has no backend configured for pending sync retry"); |
| 599 | } |
| 600 | const scheduler = this.#retryScheduler; |
| 601 | if (scheduler === undefined) { |
| 602 | throw new Error("Workspace has no retryScheduler configured"); |
| 603 | } |
| 604 | const intent = await scheduler.get(resolvedId); |
| 605 | if (intent === undefined) return { status: "idle", backend: resolvedId }; |
| 606 | if (intent.attempt > this.#retryMaxAttempts) { |
| 607 | return { |
| 608 | status: "exhausted", |
| 609 | backend: resolvedId, |
| 610 | attempt: intent.attempt, |
| 611 | error: "pending sync retry attempts exhausted", |
| 612 | }; |
| 613 | } |
| 614 | try { |
| 615 | const result = await this.#pullResolved(resolvedId); |
| 616 | await scheduler.clear(resolvedId); |
| 617 | return { |
| 618 | status: "complete", |
| 619 | backend: resolvedId, |
| 620 | applied: result.applied, |
| 621 | skipped: result.skipped, |
| 622 | }; |
| 623 | } catch (error) { |
| 624 | const message = safeErrorMessage(error); |
| 625 | if (intent.attempt >= this.#retryMaxAttempts) { |
| 626 | return { |
| 627 | status: "exhausted", |
| 628 | backend: resolvedId, |
| 629 | attempt: intent.attempt, |
| 630 | error: message, |
| 631 | }; |
| 632 | } |
| 633 | const next = this.#retryIntent(resolvedId, intent.attempt + 1); |
| 634 | await scheduler.schedule(next); |
| 635 | return { status: "pending", ...next, error: message }; |
| 636 | } |
| 637 | }); |
| 638 | } |
| 639 | |
| 640 | #pullResolved(resolvedId: string | undefined): Promise<ApplyResult> { |
| 641 | return withSpan( |
no test coverage detected