-
Notifications
You must be signed in to change notification settings - Fork 10
/
retry.tsx
95 lines (88 loc) · 3.22 KB
/
retry.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import {convertAPIError, isAbortError} from 'app/utils/errors';
import {globalErrorStore} from 'app/utils/navigation';
import {ErrorCode} from 'generated/fetch';
// Retry a fetch `maxRetries` number of times with a `timeoutMillis` wait between retries
// Respects fetch aborts
export async function fetchAbortableRetry<T>(fetchFn: () => Promise<T>, timeoutMillis: number, maxRetries: number): Promise<T> {
let retries = 0;
while (true) {
try {
return await fetchFn();
} catch (e) {
retries++;
if (isAbortError(e) || retries >= maxRetries) {
throw e;
}
// effectively a sleep for timeoutMillis
await new Promise(resolve => setTimeout(resolve, timeoutMillis));
}
}
}
/*
* A method to run an api call with our global error handling. It also adds retries on 503
* errors. This will convert errors to our JavaScript object, and push them to our global
* error handler.
* Parameters:
* fetchFn: Lambda that will run an API call, in the form of () => apiClient.apiCall(args)
* maxRetries?: The number of times it will retry before failing. Defaults to 3.
*/
export async function fetchWithGlobalErrorHandler<T>(fetchFn: () => Promise<T>, maxRetries: number = 3): Promise<T> {
let retries = 0;
while (true) {
try {
return await fetchFn();
} catch (e) {
retries++;
const errorResponse = await convertAPIError(e);
if (retries === maxRetries) {
globalErrorStore.next(errorResponse);
throw e;
}
switch (errorResponse.statusCode) {
case 503:
// Only retry on 503s
break;
case 500:
globalErrorStore.next(errorResponse);
throw e;
case 403:
if (errorResponse.errorCode === ErrorCode.USERDISABLED) {
globalErrorStore.next(errorResponse);
}
throw e;
case 0:
globalErrorStore.next(errorResponse);
throw e;
default:
throw e;
}
}
}
}
/*
* A method to run an api call with a specified number of retries and exponential backoff.
* This method will only error
* Parameters:
* apiCall: Lambda that will run an API call, in the form of () => apiClient.apiCall(args)
* maxRetries: The amount of retries the system will take before erroring
* defaultWaitTime: How long the base exponential backoff is, in milliseconds
* For example, if 1000 is passed in, it will wait 1s for the first retry,
* 2s for the second, etc.
*/
export async function apiCallWithGatewayTimeoutRetries<T>(
apiCall: () => Promise<T>, maxRetries = 3, initialWaitTime = 1000): Promise<T> {
return apiCallWithGatewayTimeoutRetriesAndRetryCount(apiCall, maxRetries, 1, initialWaitTime);
}
async function apiCallWithGatewayTimeoutRetriesAndRetryCount<T>(
apiCall: () => Promise<T>, maxRetries = 3, retryCount = 1, initialWaitTime = 1000): Promise<T> {
try {
return await apiCall();
} catch (ex) {
if (ex.status !== 504 || retryCount > maxRetries) {
throw ex;
}
await new Promise(resolve => setTimeout(resolve, initialWaitTime * Math.pow(2, retryCount)));
return await apiCallWithGatewayTimeoutRetriesAndRetryCount(
apiCall, maxRetries, retryCount + 1, initialWaitTime);
}
}