fix(relay): recover cloud session refresh failures

This commit is contained in:
OrcaWin
2026-07-23 14:00:02 -07:00
parent 4f436a8eb1
commit 5dfd5d29a9
2 changed files with 29 additions and 3 deletions
@@ -39,6 +39,32 @@ describe('RelayAuthCoordinator transient recovery', () => {
expect(statuses.at(-1)).toBe('registered')
})
it('retries when cloud-session refresh fails before identity can be read', async () => {
vi.useFakeTimers()
const broker = { closeNow: vi.fn() }
const readContext = vi
.fn()
.mockRejectedValueOnce(new Error('temporary cloud session refresh failure'))
.mockResolvedValueOnce(context)
const openBroker = vi.fn().mockResolvedValue(broker)
const coordinator = new RelayAuthCoordinator({
readContext,
openBroker,
onStatus: vi.fn(),
random: () => 0.5
})
coordinator.reconcile()
await vi.advanceTimersByTimeAsync(0)
expect(readContext).toHaveBeenCalledOnce()
expect(openBroker).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(501)
expect(readContext).toHaveBeenCalledTimes(2)
expect(openBroker).toHaveBeenCalledOnce()
expect(coordinator.getActiveBroker()).toBe(broker)
})
it('backs a sustained outage off to the five-minute jitter cap', async () => {
vi.useFakeTimers()
const openBroker = vi.fn().mockRejectedValue(new Error('temporary control open failure'))
@@ -112,7 +112,7 @@ export class RelayAuthCoordinator {
}
private async reconcileEpoch(epoch: number, expectedIdentityKey?: string): Promise<void> {
let retryIdentityKey: string | null = null
let retryIdentityKey: string | undefined
try {
const context = await this.options.readContext()
if (!this.isEpochCurrent(epoch)) {
@@ -182,14 +182,14 @@ export class RelayAuthCoordinator {
} catch (error) {
if (this.isEpochCurrent(epoch)) {
this.options.onStatus('offline')
if (retryIdentityKey && shouldRetryRelayConnectionError(error)) {
if (shouldRetryRelayConnectionError(error)) {
this.scheduleRetry(epoch, retryIdentityKey)
}
}
}
}
private scheduleRetry(epoch: number, expectedIdentityKey: string): void {
private scheduleRetry(epoch: number, expectedIdentityKey?: string): void {
if (this.retryTimer || !this.isEpochCurrent(epoch)) {
return
}