-
Notifications
You must be signed in to change notification settings - Fork 227
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
rpc: Upgrade WebSocketClient to support full client functionality #646
Conversation
This commit is multi-faceted. It: 1. Drastically simplifies much of the subscription-related functionality. 2. Expands the WebSocketClient's capabilities to be able to handle other types of requests other than just subscriptions (i.e. it implements the Client trait). Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Codecov Report
@@ Coverage Diff @@
## master #646 +/- ##
========================================
- Coverage 41.7% 40.6% -1.2%
========================================
Files 191 192 +1
Lines 12595 12410 -185
Branches 3223 3137 -86
========================================
- Hits 5262 5042 -220
- Misses 6991 7048 +57
+ Partials 342 320 -22
Continue to review full report at Codecov.
|
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
@@ -39,6 +39,7 @@ pub struct ChannelRx<T>(mpsc::UnboundedReceiver<T>); | |||
impl<T> ChannelRx<T> { | |||
/// Wait indefinitely until we receive a value from the channel (or the | |||
/// channel is closed). | |||
#[allow(dead_code)] |
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 method is not used if only the http-client
feature is enabled, but putting it behind a feature flag breaks things.
/// simple, singular response. | ||
/// | ||
/// [`Subscription`]: struct.Subscription.html | ||
pub trait SimpleRequest: Request {} |
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 is the simplest way I could find to configure the type system in such a way that I could restrict access to the Client::perform
method to only those kinds of requests that expect a single response (i.e. non-subscription-related requests), while still preserving the existing overall type architecture.
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
ChannelTx::send now no longer needs to be mutable because the underlying channel has no mutability requirement. This leads to viral changes throughout the interfaces and clients (positive ones). ChannelTx::send also doesn't need to be async, because it relies on an unbounded channel. This also has some viral implications for other methods throughout the RPC client. If, in future, we want to support bounded channels, then we can consider making it async again. But we probably shouldn't make it async if we don't need it to be. Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
The latest changes are awesome. When can we review and merge? |
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.
rpc/src/client/transport/router.rs
Outdated
); | ||
} | ||
} | ||
// Obtain a mutable reference because the previous reference was |
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.
interesting 🤔 i would expect Rust to figure out that the loop is over
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.
for x in y { b }
is syntactic sugar for let mut iter = y.into_iter(); while let Some(x) = iter.next() { b }
and into_iter()
consumes self
, therefore one cannot access the collection being iterated over (ie. subs_for_query
) after the loop is over.
On the other hand, for x in &y { b }
would desugar to a call to iter()
which does not not consume self
but instead yields reference to the underlying elements. Same with for x in &mut y
.
I wonder though if we could use for (id, event_tx) in &subs_for_query
here instead?
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.
got it! thanks for explaining it @romac 🙏
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.
So interestingly, the for (id, event_tx) in &subs_for_query
didn't work previously, and I've checked again now, it results in the following error:
error[E0277]: `&&mut std::collections::HashMap<std::string::String, client::sync::ChannelTx<std::result::Result<event::Event, error::Error>>>` is not an iterator
--> rpc/src/client/transport/router.rs:35:31
|
35 | for (id, event_tx) in &subs_for_query {
| ^^^^^^^^^^^^^^^ `&&mut std::collections::HashMap<std::string::String, client::sync::ChannelTx<std::result::Result<event::Event, error::Error>>>` is not an iterator
|
= help: the trait `std::iter::Iterator` is not implemented for `&&mut std::collections::HashMap<std::string::String, client::sync::ChannelTx<std::result::Result<event::Event, error::Error>>>`
= note: required by `std::iter::IntoIterator::into_iter`
for (id, event_tx) in &mut subs_for_query
also doesn't compile.
The following code, however, does work, so I'll update the code accordingly:
for (id, event_tx) in subs_for_query.borrow_mut() {
// ...
}
I feel as though if I manually expanded all the syntactic sugar, I'd understand this better 😁
self.subscriptions.get_mut(&query).unwrap() | ||
} | ||
}; | ||
subs_for_query.insert(id.to_string(), tx); |
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.
what if the id already exists?
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.
It'd be overwritten.
My assumption here is that our UUIDs that we're automatically generating for subscriptions won't ever collide. I suppose that leaves a very small, albeit non-zero, non-deterministic probability of having a subscription dropped arbitrarily, but I'd imagine that networking failures leading to the same outcome would be far higher probability than that of a UUIDv4 collision.
pub enum PublishResult { | ||
Success, | ||
NoSubscribers, | ||
AllDisconnected, |
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.
what is the intuition behind this type of result?
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.
It was one way of passing some additional information back to the WebSocketClient
after it attempts to publish an event.
Since the WebSocketClient
only sends an unsubscribe request once all subscribers for a particular query have dropped, I needed to be able to distinguish between the case where there were no subscribers to begin with (i.e. we somehow received a spurious event without having a subscription) and the case where we had subscribers, but since we published last to them they dropped their receiving ends. Both would publish the event to zero subscribers.
In the former case, we would consider this to be unexpected behaviour on the part of the node (I don't do anything about it right now though), and in the latter case it'd be a signal to send a single unsubscribe request.
} | ||
} | ||
}, | ||
async fn confirm_pending_command( |
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.
confirm here means sending response?
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 can rename it to something a bit clearer though.
…wice Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
This PR made the following dependency changes: Added Packages (Duplicate versions in '()'):
clap 2.33.3
colored 1.9.3
heck 0.3.1
num-derive 0.3.3
proc-macro-error 1.0.4
proc-macro-error-attr 1.0.4
simple_logger 1.11.0
strsim 0.8.0 (0.9.3)
structopt 0.3.20
structopt-derive 0.4.13
tendermint-rpc-probe 0.17.0-rc3
textwrap 0.11.0
unicode-segmentation 1.7.1
unicode-width 0.1.8
vec_map 0.8.2
Removed Packages (Remaining versions in '()'):
abscissa_tokio 0.5.1
Updated Packages:
async-tungstenite: 0.8.0 -> 0.9.3
tendermint: 0.16.0 -> 0.17.0-rc3
tendermint-light-client: 0.16.0 -> 0.17.0-rc3
tendermint-light-node: 0.16.0 -> 0.17.0-rc3
tendermint-proto: 0.1.0 -> 0.17.0-rc3
tendermint-rpc: 0.16.0 -> 0.17.0-rc3
tendermint-testgen: 0.1.0 -> 0.17.0-rc3
|
In partial fulfillment of #582.
This PR expands the
WebSocketClient
's capabilities to be able to handle other types of requests other than just subscriptions (i.e. it implements theClient
trait).It also drastically simplifies some of the internal code for the RPC crate.
📖 Rendered updated ADR (008)