-
Notifications
You must be signed in to change notification settings - Fork 291
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
feat: FallbackLayer
transport
#2135
base: main
Are you sure you want to change the base?
Conversation
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.
mostly questions, I think overall this makes sense and I was able to follow, but I have some concerns about the make_request impl, because if there are multiple concurrent requests, would this result in empty transports error rn?
let mut transports = self.transports.lock().expect("Lock poisoned"); | ||
for _ in 0..self.active_transport_count.min(transports.len()) { | ||
if let Some(transport) = transports.pop() { | ||
top_transports.push(transport); |
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.
not exactly sure I fully understood this but it seems like this pops the transports and on success it adds them back?
what happens if there are no available transports?
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.
Yes, I've updated this to use a Vec
instead of a BinaryHeap
and re-sort it whenever a score changes.
This way I don't need to take the transports from the list on each request anymore.
6f68e2a
to
45994f8
Compare
Motivation
Having a provider be able to consume a list of transports can increase both availability and performance, both desirable features of many applications that need a connection to Ethereum.
Several issues such as #1938 and #1760 outline a feature like this in Alloy.
Other libraries such as Viem and Ethers.js also have something similar.
Solution
This PR implements
FallbackLayer
andFallbackService
, following the usualtower
abstractions. Most notably, the layer here works with aVec<S>
because this service is meant for managing a list of transports.When sending a request to a provider using this layer, it queries the top ranked
N
transports (configurable with theactive_transport_count
option) concurrently, returning the first successful result. Whenever a result arrives, metrics are updated to keep track of the performance of each transport, using a simple combination of average latency and success rate.I tried to keep this simple, but there is potential for future work if someone finds it useful:
Usage
I've added an example usage binary here: merklefruit/alloy-examples#1, but in short here is how it works:
PR Checklist
Tested on a live example, as I couldn't add comprehensive unit tests without running into cyclic dependencies in alloy itself.