Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/content/docs/plugins/client-retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ const planets = await client.planet.list({ limit: 10 }, {
retry: 3, // Maximum retry attempts
retryDelay: 2000, // Delay between retries in ms
shouldRetry: options => true, // Determines whether to retry based on the error
onRetry: (options) => {}, // Hook executed on each retry
onRetry: (options) => {
// Hook executed on each retry

return (isSuccess) => {
// Execute after the retry is complete
}
},
}
})
```
Expand Down
16 changes: 14 additions & 2 deletions packages/client/src/plugins/retry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,21 @@ describe('clientRetryPlugin', () => {
})

it('onRetry', async () => {
handlerFn.mockRejectedValue(new Error('fail'))
let count = 0
handlerFn.mockImplementation(() => {
count++

if (count === 4) {
return 'success'
}

throw new Error('fail')
})

const clean = vi.fn()
const onRetry = vi.fn(() => clean)

await expect(client('hello', { context: { retry: 3, retryDelay: 0, onRetry } })).rejects.toThrow('Internal server error')
await expect(client('hello', { context: { retry: 3, retryDelay: 0, onRetry } })).resolves.toEqual('success')

expect(handlerFn).toHaveBeenCalledTimes(4)

Expand Down Expand Up @@ -164,6 +173,9 @@ describe('clientRetryPlugin', () => {
)

expect(clean).toHaveBeenCalledTimes(3)
expect(clean).toHaveBeenNthCalledWith(1, false)
expect(clean).toHaveBeenNthCalledWith(2, false)
expect(clean).toHaveBeenNthCalledWith(3, true)
})

it('should not retry if signal aborted', async () => {
Expand Down
27 changes: 14 additions & 13 deletions packages/client/src/plugins/retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface ClientRetryPluginContext {
/**
* The hook called when retrying, and return the unsubscribe function.
*/
onRetry?: (options: ClientRetryPluginAttemptOptions<ClientRetryPluginContext>) => void | (() => void)
onRetry?: (options: ClientRetryPluginAttemptOptions<ClientRetryPluginContext>) => void | ((isSuccess: boolean) => void)
}

export class ClientRetryPluginInvalidEventIteratorRetryResponse extends Error { }
Expand Down Expand Up @@ -77,24 +77,24 @@ export class ClientRetryPlugin<T extends ClientRetryPluginContext> implements St

let lastEventId = interceptorOptions.lastEventId
let lastEventRetry: undefined | number
let unsubscribe: void | (() => void)
let callback: void | ((isSuccess: boolean) => void)
let attemptIndex = 0

const next = async (initial?: { error: unknown }) => {
let current = initial
const next = async (initialError?: { error: unknown }) => {
let currentError = initialError

while (true) {
const updatedInterceptorOptions = { ...interceptorOptions, lastEventId }

if (current) {
if (currentError) {
if (attemptIndex >= maxAttempts) {
throw current.error
throw currentError.error
}

const attemptOptions: ClientRetryPluginAttemptOptions<ClientRetryPluginContext> = {
...updatedInterceptorOptions,
attemptIndex,
error: current.error,
error: currentError.error,
lastEventRetry,
}

Expand All @@ -104,10 +104,10 @@ export class ClientRetryPlugin<T extends ClientRetryPluginContext> implements St
)

if (!shouldRetryBool) {
throw current.error
throw currentError.error
}

unsubscribe = onRetry?.(attemptOptions)
callback = onRetry?.(attemptOptions)

const retryDelayMs = await value(retryDelay, attemptOptions)

Expand All @@ -117,18 +117,19 @@ export class ClientRetryPlugin<T extends ClientRetryPluginContext> implements St
}

try {
currentError = undefined
return await interceptorOptions.next(updatedInterceptorOptions)
}
catch (error) {
currentError = { error }

if (updatedInterceptorOptions.signal?.aborted === true) {
throw error
}

current = { error }
}
finally {
unsubscribe?.()
unsubscribe = undefined
callback?.(!currentError)
callback = undefined
}
}
}
Expand Down