-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Experimental retry with fallback provider #88
Conversation
transactions: Array<TransactionResponse>; | ||
} | ||
|
||
export class MultiJsonRpcProvider extends ethers.providers.Provider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make the implementation more transparent by passing directly the arguments to the provider method.
Consider this implementation (very similar to resolveInAttempts
):
executeWithRetries (methodName: string) {
let attemptsMade = 0;
const that = this;
return async function wrappedProviderMethod(...args) {
const provider = that.nextProvider();
attemptsMade++;
try {
await provider[methodName](...args);
} catch (error) {
if (attemptsMade >= that.maxRetries) {
throw new Error(`Failed to execute ${methodName} after ${attemptsMade} attempts. Last error: ${error}`);
}
else {
await sleep(500)
return wrappedProviderMethod(...args);
}
}
}
}
This would allow you to replace all methods with something like this:
getNetwork(...args: any[]) {
return this.executeWithRetries('getNetwork', args);
}
Which in my opinion has some advantages. Namely:
- it's more clear to the developer that the class is not modifying the behaviour of any of the methods
- if the signature of any of the methods change, we don't need to update the signature of our MultiJsonRpcProvider class
- MultiJsonRpcProvider doesn't need to know nothing about the underlying providers types, which is easier to mantain in the long run. We could even remove the types added above this class. This is a HUGE advantage IMO
- We reduce a lot of duplicated code (right now all methods of the calss that only call a provider have the same code to call resolveInAttempts, if we were to add a new param to resolveInAttempts we would need to add this in most of the module's code
- The class only has code in one method, allowing you to very easily undertand and modify what the class does. The fact that all other methods just call one "factory" function makes it very declarative IMO
- By using sleep, we simplify the code by not needing to manually create and resolve the promise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to get the type of the arguments of a function? 🤔 That way we could replace the ...any[]
for the actual signature of the getNetwork
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can, but it can be bothersome. We should evaluate the provider API first and see whether it suits our purposes before diving into it.
toBlock?: BlockTag; | ||
} | ||
|
||
function _isFrozen(object: any): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really really really don't like having all this code copied over from ethers 🥶
@@ -0,0 +1,606 @@ | |||
"use strict"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be redundant with proper tsconfig.json
values.
This should be explicitly enabled in order to work.
Closes #86