Update RetryProvider to use exponential backoff (#859)

pull/861/head
Asa Oines 2 years ago committed by GitHub
parent e5a72eaba2
commit 8703da5ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      typescript/infra/src/config/chain.ts
  2. 25
      typescript/sdk/src/providers/RetryProvider.ts
  3. 11
      typescript/utils/src/utils.ts

@ -19,8 +19,8 @@ export async function fetchProvider(
const provider = celoChainNames.has(chainName) const provider = celoChainNames.has(chainName)
? new StaticCeloJsonRpcProvider(rpc) ? new StaticCeloJsonRpcProvider(rpc)
: new RetryJsonRpcProvider(new ethers.providers.JsonRpcProvider(rpc), { : new RetryJsonRpcProvider(new ethers.providers.JsonRpcProvider(rpc), {
retryLimit: 2, maxRequests: 6,
interval: 250, baseRetryMs: 50,
}); });
return provider; return provider;
} }

@ -2,16 +2,17 @@
// //
// Mostly taken from the removed version that was in ethers.js // Mostly taken from the removed version that was in ethers.js
// See: https://github.com/ethers-io/ethers.js/discussions/3006 // See: https://github.com/ethers-io/ethers.js/discussions/3006
import { assert } from 'console';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { utils } from '@abacus-network/utils'; import { utils } from '@abacus-network/utils';
export type RetryOptions = { export type RetryOptions = {
// The wait interval in between // Maximum number of times to make the RPC
interval: number; maxRequests: number;
// Maximum number of times to retry // Exponential backoff base value
retryLimit: number; baseRetryMs: number;
}; };
export class RetryProvider extends ethers.providers.BaseProvider { export class RetryProvider extends ethers.providers.BaseProvider {
@ -20,6 +21,10 @@ export class RetryProvider extends ethers.providers.BaseProvider {
readonly retryOptions: RetryOptions, readonly retryOptions: RetryOptions,
) { ) {
super(provider.getNetwork()); super(provider.getNetwork());
assert(
retryOptions.maxRequests >= 1,
'RetryOptions.maxRequests must be >= 1',
);
ethers.utils.defineReadOnly(this, 'provider', provider); ethers.utils.defineReadOnly(this, 'provider', provider);
ethers.utils.defineReadOnly(this, 'retryOptions', retryOptions); ethers.utils.defineReadOnly(this, 'retryOptions', retryOptions);
} }
@ -28,8 +33,8 @@ export class RetryProvider extends ethers.providers.BaseProvider {
perform(method: string, params: any): Promise<any> { perform(method: string, params: any): Promise<any> {
return utils.retryAsync( return utils.retryAsync(
() => this.provider.perform(method, params), () => this.provider.perform(method, params),
this.retryOptions.retryLimit, this.retryOptions.maxRequests,
this.retryOptions.interval, this.retryOptions.baseRetryMs,
); );
} }
} }
@ -41,13 +46,17 @@ export class RetryJsonRpcProvider extends ethers.providers.JsonRpcProvider {
readonly retryOptions: RetryOptions, readonly retryOptions: RetryOptions,
) { ) {
super(provider.connection, provider.network); super(provider.connection, provider.network);
assert(
retryOptions.maxRequests >= 1,
'RetryOptions.maxRequests must be >= 1',
);
} }
async send(method: string, params: Array<any>): Promise<any> { async send(method: string, params: Array<any>): Promise<any> {
return utils.retryAsync( return utils.retryAsync(
async () => this.provider.send(method, params), async () => this.provider.send(method, params),
this.retryOptions.retryLimit, this.retryOptions.maxRequests,
this.retryOptions.interval, this.retryOptions.baseRetryMs,
); );
} }
} }

@ -101,12 +101,13 @@ export function sleep(ms: number): Promise<void> {
return new Promise<void>((resolve) => setTimeout(resolve, ms)); return new Promise<void>((resolve) => setTimeout(resolve, ms));
} }
// Retries an async function when it raises an exception // Retries an async function if it raises an exception,
// if all the tries fail it raises the last thrown exception // with exponential backoff.
// If all the tries fail it raises the last thrown exception
export async function retryAsync<T>( export async function retryAsync<T>(
runner: () => T, runner: () => T,
attempts = 3, attempts = 5,
delay = 500, baseRetryMs = 50,
) { ) {
let saveError; let saveError;
for (let i = 0; i < attempts; i++) { for (let i = 0; i < attempts; i++) {
@ -114,7 +115,7 @@ export async function retryAsync<T>(
return runner(); return runner();
} catch (error) { } catch (error) {
saveError = error; saveError = error;
await sleep(delay * (i + 1)); await sleep(baseRetryMs * 2 ** i);
} }
} }
throw saveError; throw saveError;

Loading…
Cancel
Save