1
0
Fork 0

Add retry delay

pull/558/head
Dave Hadka 2020-08-17 15:32:04 -05:00
parent 1ef26b2390
commit de52c861c1
3 changed files with 30 additions and 11 deletions

View File

@ -20,7 +20,8 @@ async function handleResponse(
if (response.statusCode >= 900) { if (response.statusCode >= 900) {
throw Error('Test Error') throw Error('Test Error')
} else if (response.statusCode >= 600) { } else if (response.statusCode >= 600) {
const error: any = Error('Test Error with Status Code') // eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = Error('Test Error with Status Code') as any
error['statusCode'] = response.statusCode - 300 error['statusCode'] = response.statusCode - 300
throw error throw error
} else { } else {
@ -37,7 +38,9 @@ async function testRetryExpectingResult(
const actualResult = await retry( const actualResult = await retry(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode (response: TestResponse) => response.statusCode,
2, // maxAttempts
0 // delay
) )
expect(actualResult.result).toEqual(expectedResult) expect(actualResult.result).toEqual(expectedResult)
@ -54,12 +57,14 @@ async function testRetryConvertingErrorToResult(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode, (response: TestResponse) => response.statusCode,
2, 2, // maxAttempts
0, // delay
(e: Error) => { (e: Error) => {
const error: any = e // eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = e as any
return { return {
statusCode: error['statusCode'], statusCode: error['statusCode'],
result: error['result'] result: error['result'] ?? null
} }
} }
) )
@ -77,7 +82,9 @@ async function testRetryExpectingError(
retry( retry(
'test', 'test',
async () => handleResponse(responses.pop()), async () => handleResponse(responses.pop()),
(response: TestResponse) => response.statusCode (response: TestResponse) => response.statusCode,
2, // maxAttempts,
0 // delay
) )
).rejects.toBeInstanceOf(Error) ).rejects.toBeInstanceOf(Error)
} }

View File

@ -11,6 +11,9 @@ export enum CompressionMethod {
Zstd = 'zstd' Zstd = 'zstd'
} }
// The default delay in milliseconds between retry attempts.
export const RetryDelay = 5000
// Socket timeout in milliseconds during download. If no traffic is received // Socket timeout in milliseconds during download. If no traffic is received
// over the socket during this period, the socket is destroyed and the download // over the socket during this period, the socket is destroyed and the download
// is aborted. // is aborted.

View File

@ -4,6 +4,7 @@ import {
IHttpClientResponse, IHttpClientResponse,
ITypedResponse ITypedResponse
} from '@actions/http-client/interfaces' } from '@actions/http-client/interfaces'
import {RetryDelay} from './constants'
export function isSuccessStatusCode(statusCode?: number): boolean { export function isSuccessStatusCode(statusCode?: number): boolean {
if (!statusCode) { if (!statusCode) {
@ -31,11 +32,16 @@ export function isRetryableStatusCode(statusCode?: number): boolean {
return retryableStatusCodes.includes(statusCode) return retryableStatusCodes.includes(statusCode)
} }
async function sleep(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
export async function retry<T>( export async function retry<T>(
name: string, name: string,
method: () => Promise<T>, method: () => Promise<T>,
getStatusCode: (arg0: T) => number | undefined, getStatusCode: (arg0: T) => number | undefined,
maxAttempts = 2, maxAttempts = 2,
delay = RetryDelay,
onError: ((arg0: Error) => T | undefined) | undefined = undefined onError: ((arg0: Error) => T | undefined) | undefined = undefined
): Promise<T> { ): Promise<T> {
let errorMessage = '' let errorMessage = ''
@ -69,7 +75,7 @@ export async function retry<T>(
isRetryable = isRetryableStatusCode(statusCode) isRetryable = isRetryableStatusCode(statusCode)
errorMessage = `Cache service responded with ${statusCode}` errorMessage = `Cache service responded with ${statusCode}`
} }
core.debug( core.debug(
`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}` `${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`
) )
@ -79,6 +85,7 @@ export async function retry<T>(
break break
} }
await sleep(delay)
attempt++ attempt++
} }
@ -95,11 +102,12 @@ export async function retryTypedResponse<T>(
method, method,
(response: ITypedResponse<T>) => response.statusCode, (response: ITypedResponse<T>) => response.statusCode,
maxAttempts, maxAttempts,
RetryDelay,
// If the error object contains the statusCode property, extract it and return // If the error object contains the statusCode property, extract it and return
// an ITypedResponse<T> so it can be processed by the retry logic. Explicitly // an ITypedResponse<T> so it can be processed by the retry logic.
// casting Error object to any to workaround missing property errors.
(e: Error) => { (e: Error) => {
const error : any = e // eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = e as any
if (error['statusCode']) { if (error['statusCode']) {
return { return {
statusCode: error['statusCode'], statusCode: error['statusCode'],
@ -122,6 +130,7 @@ export async function retryHttpClientResponse<T>(
name, name,
method, method,
(response: IHttpClientResponse) => response.message.statusCode, (response: IHttpClientResponse) => response.message.statusCode,
maxAttempts maxAttempts,
RetryDelay
) )
} }