-
Notifications
You must be signed in to change notification settings - Fork 224
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
Add minimal blocking ABCI crate #794
Conversation
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]>
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]>
* Add integration testing utility for ABCI key/value store Signed-off-by: Thane Thomson <[email protected]> * Add hacky bash script to demonstrate parallel execution Signed-off-by: Thane Thomson <[email protected]> * Created abci test harness (#800) * Created abci test harness * cargo make additions and docs Co-authored-by: Greg Szabo <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
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.
Great stuff, very clean and minimal, <3 the test suite :)
Left a few minor comments but otherwise it looks really good to me!
abci/src/application/kvstore.rs
Outdated
|
||
fn channel_send<T>(tx: &Sender<T>, value: T) -> Result<()> { | ||
tx.send(value) | ||
.map_err(|e| Error::ChannelSend(e.to_string())) |
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.
Can we hold on to the original error here rather than its string representation?
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 might be able to do that with eyre
- I'll try it out 👍 Originally I had trouble with this because SendError is generic over T
, which would imply that the error needs to be generic over T
too. But if the error can be wrapped as a Box<dyn std::error::Error>
this shouldn't be a problem, I think.
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.
Oh I see. Since it's just an example app it does not matter too much, just a nice-to-have (probably not worth it if we have to pull in eyre
).
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.
Here's the conundrum with eyre, as per https://docs.rs/eyre/0.6.5/eyre/struct.Report.html:
Report
requires that the error isSend
,Sync
, and'static
.
This would imply that T
must be Send
, Sync
and 'static
too. In this case I'm trying to send a variant of the Command
enum:
enum Command {
/// Get the height of the last commit.
GetInfo { result_tx: Sender<(i64, Vec<u8>)> },
/// Get the key associated with `key`.
Get {
key: String,
result_tx: Sender<(i64, Option<String>)>,
},
/// Set the value of `key` to to `value`.
Set {
key: String,
value: String,
result_tx: Sender<Option<String>>,
},
/// Commit the current state of the application, which involves recomputing
/// the application's hash.
Commit { result_tx: Sender<(i64, Vec<u8>)> },
}
std::sync::mpsc::Sender
is !Sync
, and I ideally need to be able to send it across the channel in order to get a dedicated response asynchronously.
Perhaps this is a hint that a better overall design is possible? I suppose falling back to Arc<Mutex<...>>
instead of the handle/driver architecture would solve this. Or simply "flattening" std::sync::mpsc::SendError<T>
, like I'm doing in the original code here. I can't think why the SendError
would need to be generic over T
in the first place?
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 the current solution and architecture are fine.
Just wondering, have you tried just casting SendError
to Box<dyn std::error::Error>
and wrapping that one in ChannelSend
(without using eyre
at all)?
Regarding SendError
being generic over T
: that's because it's defined as
pub struct SendError<T>(pub T);
which allows you to get back the value that failed to be sent.
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.
Just wondering, have you tried just casting SendError to Box and wrapping that one in ChannelSend (without using eyre at all)?
T
would still need to implement Sync
, afaik 😞
Co-authored-by: Romain Ruetschi <[email protected]>
…s/pull/794\#discussion_r573100911 Signed-off-by: Thane Thomson <[email protected]>
…t type Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
|
||
/// Called once upon genesis. | ||
fn init_chain(&self, _request: RequestInitChain) -> ResponseInitChain { | ||
Default::default() |
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.
Could you expand why the default pattern in here is preferred over not having a default implementation for these methods?
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 was modelling it on what the BaseApplication
does in the Go version: https://github.com/tendermint/tendermint/blob/master/abci/types/application.go#L34
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
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.
👍
/// Attempt to retrieve the value associated with the given key. | ||
pub fn get<K: AsRef<str>>(&self, key: K) -> Result<(i64, Option<String>)> { | ||
let (result_tx, result_rx) = channel(); | ||
channel_send( |
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.
Could you explain why you've chosen this concurrency model (communication via channels) over something like a mutex & hashmap? As someone who's learning Rust it will be useful to me.
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 may be a personal preference/readability thing 😁 I'd say there are pros and cons to each approach. I've always tended to prefer an actor-style model for managing state in concurrent systems (explicit-lock-free state encapsulation with message passing and pattern matching on messages), which I've tried to implement here in a lightweight way. It's easier for me to reason about such state machines in general (like this).
When it comes to contention, I'd imagine using mutexes would push bottlenecks down the stack towards the networking layer, outside of the app's direct control. With the actor-based model it'd be able to leverage memory (in the unbounded channel) to avoid pushing the bottleneck towards the networking layer. On more constrained devices, this channel-based approach wouldn't provide any benefit for the additional complexity cost, so mutexes would probably be the way to go.
Practically both models would probably work the same for this specific application, although I'm not sure which one would provide better performance (I'd have to benchmark to find out). I'd imagine that the mutex-based approach would probably provide better performance, speed-wise, as long as the app can keep up with incoming requests.
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]>
Signed-off-by: Thane Thomson <[email protected]>
Signed-off-by: Thane Thomson <[email protected]>
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.
Good stuff & discussion 👍
I don't have admin rights on https://github.com/tendermint/rust-abci but after the ABCI crate here gets published, perhaps it'll be good to archive the old repo and point to here? |
@@ -0,0 +1,33 @@ | |||
[package] | |||
name = "tendermint-abci" |
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.
name = "tendermint-abci" | |
name = "abci" |
it can be called just this and published as a new breaking version of https://crates.io/crates/abci -- @xla @liamsi can help with that
* Add minimal blocking ABCI library Signed-off-by: Thane Thomson <[email protected]> * Expand API to implement in-memory key/value store app Signed-off-by: Thane Thomson <[email protected]> * Add kvstore-rs ABCI app Signed-off-by: Thane Thomson <[email protected]> * Add rudimentary README Signed-off-by: Thane Thomson <[email protected]> * Bump proto version dependency to v0.18.0 Signed-off-by: Thane Thomson <[email protected]> * Replace manual default structs with Default::default() Signed-off-by: Thane Thomson <[email protected]> * Enable debug logging for all incoming ABCI requests Signed-off-by: Thane Thomson <[email protected]> * Improve CLI UX Signed-off-by: Thane Thomson <[email protected]> * Allow for read buffer size customization Signed-off-by: Thane Thomson <[email protected]> * Add crate description Signed-off-by: Thane Thomson <[email protected]> * Update README for ABCI crate Signed-off-by: Thane Thomson <[email protected]> * Add ABCI integration test for minimal ABCI crate (#797) * Add integration testing utility for ABCI key/value store Signed-off-by: Thane Thomson <[email protected]> * Add hacky bash script to demonstrate parallel execution Signed-off-by: Thane Thomson <[email protected]> * Created abci test harness (#800) * Created abci test harness * cargo make additions and docs Co-authored-by: Greg Szabo <[email protected]> * Update abci/src/codec.rs Co-authored-by: Romain Ruetschi <[email protected]> * Apply suggestion from https://github.com/informalsystems/tendermint-rs/pull/794\#discussion_r573100911 Signed-off-by: Thane Thomson <[email protected]> * Refactor error handing and expose eyre::Result as crate default Result type Signed-off-by: Thane Thomson <[email protected]> * Refactor to use tracing instead of log Signed-off-by: Thane Thomson <[email protected]> * Add newline Signed-off-by: Thane Thomson <[email protected]> * Remove comment relating to constraints on Codec struct params Signed-off-by: Thane Thomson <[email protected]> * Version tendermint-abci crate in line with other tendermint-rs crates Signed-off-by: Thane Thomson <[email protected]> * Update CHANGELOG Signed-off-by: Thane Thomson <[email protected]> * Expand crate documentation Signed-off-by: Thane Thomson <[email protected]> * Extract request dispatch functionality from Application trait Signed-off-by: Thane Thomson <[email protected]> * Move ABCI server example to crate root Signed-off-by: Thane Thomson <[email protected]> * Fix broken link in docs Signed-off-by: Thane Thomson <[email protected]> * Replace EchoApp example with KeyValueStoreApp example Signed-off-by: Thane Thomson <[email protected]> Co-authored-by: Greg Szabo <[email protected]> Co-authored-by: Romain Ruetschi <[email protected]>
Closes #29
In order to reduce the amount of code for review, I've extracted portions of #750 and expanded on them, but purely for the blocking case (using the Rust standard library's networking functionality). I've now included the full range of ABCI requests/responses, but purely using their Protobuf representations (also to reduce the sheer quantity of code). Now that the investigative work in #750 has been done, adding
async
compatibility should be a relatively simple (but probably tedious) task - TBD in separate PRs.There's a minimal
kvstore-rs
application that can be built into a binary, and the basic functionality works with a fresh Tendermint node. See the ABCI crate README for details:📖 README rendered