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)
? new StaticCeloJsonRpcProvider(rpc)
: new RetryJsonRpcProvider(new ethers.providers.JsonRpcProvider(rpc), {
retryLimit: 2,
interval: 250,
maxRequests: 6,
baseRetryMs: 50,
});
return provider;
}

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

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

Loading…
Cancel
Save