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 an async interface using futures-rs #141
Conversation
d7fe429
to
c9fba9e
Compare
This should be "done" now. Only the basic |
Awesome. So happy to see this moving forward |
Thanks! I will be playing with this. |
Really nice. This makes me very happy :) I would merge it as such and then fix up some smaller things such as missing documentation on master. I would not waste too much time on that yet in case it turns out we need to tweak the API more. Would just need a release of combine before I can merge this. |
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.
Only thing that needs changing before merging from my part is that combine points to a release.
👍 so good to see work here happening. I’ll look over the code today as well. |
src/connection.rs
Outdated
pub trait ConnectionLike: Sized { | ||
/// Sends an already encoded (packed) command into the TCP socket and | ||
/// reads the single response from it. | ||
fn req_packed_command(self, cmd: Vec<u8>) -> RedisFuture<(Self, Value)>; |
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 may have stuck a bit to close to the synchronous versions by using this pattern actually. I will be making a separate PR (building on this one) hopefully today where the Connection
allows multiple commands to be in flight concurrently without needing an explicit pipeline. The trade off is that it won't be able to handle explicitly built pipelines (at least not initially) and there is some additional overhead due to needing Arc
/Rc
.
match pieces.next() { | ||
Some(detail) => fail!((kind, desc, detail.to_string())), | ||
None => fail!((kind, desc)), | ||
match parser.poll()? { |
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 should mention that instead of calling the asynchronous parsing here, we could parse directly from the Read
instance. The only drawback is that the parser needs to be instantiated twice (once for I == PartialStream<easy::Stream<&[u8]>>
and once for I == ReadStream<R>
.
keys: vec![], | ||
} | ||
.invoke(con) | ||
script: self, |
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.
Sorry for the churn caused by accidentally having rustfmt on. I could revert some of the changes If you want but I fear the files I modified may have to stick. (Would be nice to run through rustfmt on the whole repo though in my opinion).
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.
@Marwes i'm fine enforcing rustfmt. I tried to do it in the past but it completely broke with some of the macros at the point. Need to reinvestigate this.
I should release 3.0.0-beta.1 (beta == only minor breaking changes planned) later today unless I get caught up in something and don't have time. |
src/connection.rs
Outdated
@@ -622,3 +621,243 @@ pub fn transaction<K: ToRedisArgs, | |||
} | |||
} | |||
} | |||
|
|||
pub mod async { |
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.
The file is already quite long and here we have another module.
IMO this should be extracted into its own file here.
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 wanted to say. Also makes reading the diff hard because the types have the same name :)
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 could change to AsyncConnection
etc but I feel it is more idiomatic to write async::Connection
to avoid ambiguities in that case.
src/lib.rs
Outdated
pub use types::{/* utility functions */ from_redis_value, /* error kinds */ ErrorKind, | ||
/* conversion traits */ FromRedisValue, /* utility types */ InfoDict, | ||
NumericBehavior, /* error and result types */ RedisError, RedisResult, | ||
ToRedisArgs, /* low level values */ Value, RedisFuture}; |
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 auto-formatted change makes it both harder to read and to change.
IMO we should keep the old formatting and mark it so rustfmt does not touch it.
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.
Fixed
@@ -0,0 +1,236 @@ | |||
use std::fmt::Arguments; |
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.
Would it be better to have connection::sync
and connection::async
modules?
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.
As long as we still export connection::sync::Connection
(and other types from that module) on the root module, we could split it up and not break existing usage.
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.
@badboy we never exposed modules anyways. Only the toplevel thing.
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.
@mitsuhiko Exactly, that's why I just wanted to mention that we re-export it to keep it working
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 left it under src/async.rs
since I would need to re-export it as async
under the root anyway.
I guess I can close my nom attempt which is a good thing! |
@gsquire Thanks for your effort anyway! |
Released a new version of combine and updated this PR to point to it https://crates.io/crates/combine |
Is there anything blocking this from being merged? |
IMO 2 things:
|
Did a rebase and squash since most of the commits won't compile due to depending on dead git branches. Still lacking unix stream support due to tokio-uds not having a new release but that can be added in without a breaking release at a later point. |
I believe all remaining issues have been resolved(?) (pointing combine to a real 3.0 release was the last one). cc @mitsuhiko @badboy |
I haven't been following closely, but does that mean that Unix stream support is broken for the sync interface as well? We rely on this in our production environment and would need to pin to an earlier commit. |
No, that only affects the async interface (they are largely separate implementations in that respect) |
👍 Thanks for clearing that up. I recall some discussion around having the implementations be unified, and it's good to hear there won't be any breaking changes due to this feature on the sync side. At least, until they unify :). |
Bump (@mitsuhiko @badboy ) |
Ping @mitsuhiko and @badboy . Anything I can do to help get this and #143 merged? |
I'm just back from a work week. I'll put this on my schedule for the week. |
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 job!
The rustfmt
made it a bit harder to review, but I'm fine with it being done now and not having to deal with it later.
@mitsuhiko's "Request changes" should be resolved.
Some small things remaining and one last question:
Is it likely that we can switch away from a boxed future in the future (pun intended) by using -> impl X
? (No need to do it here now)
Furthermore, we should at least run cargo bench --no-run
on CI with nightly to ensure the code actually compiles. That can be done in another bug.
Cargo.toml
Outdated
@@ -19,18 +19,29 @@ build = "build.rs" | |||
[features] | |||
with-rustc-json = ["rustc-serialize"] | |||
with-unix-sockets = ["unix_socket"] | |||
# with-async-unix-sockets = ["tokio-uds"] |
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.
Please remove this comment.
Cargo.toml
Outdated
tokio-reactor = "0.1" | ||
tokio-tcp = "0.1" | ||
tokio-io = "0.1" | ||
# tokio-uds = { git = "https://github.com/Marwes/tokio-uds", branch = "only_tokio_upgrade", optional = true } |
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.
Also this comment.
benches/bench_basic.rs
Outdated
extern crate tokio_core; | ||
|
||
use futures::Future; | ||
use tokio_core::reactor::Core; |
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.
tokio_core
is not in the dependencies anymore. This whole benchmark needs a rewrite.
benches/bench_basic.rs
Outdated
fn bench_simple_getsetdel_async(b: &mut Bencher) { | ||
let client = get_client(); | ||
let mut core = Core::new().unwrap(); | ||
let con = client.get_async_connection(&core.handle()); |
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.
get_async_connection
doesn't take a handle anymore, so I guess this all needs to change.
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.
Fixed (and I enabled cargo check
for travis)
benches/bench_basic.rs
Outdated
bench_simple_getsetdel_pipeline, | ||
bench_simple_getsetdel_pipeline_precreated, | ||
bench_long_pipeline, | ||
bench_encode_pipeline, | ||
bench_encode_pipeline_nested | ||
bench_encode_pipeline_nested, | ||
bench_long_pipeline |
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 now duplicated.
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.
Fixed
src/async.rs
Outdated
.map(|con| ActualConnection::Tcp(BufReader::new(con))), | ||
) | ||
} | ||
#[cfg(feature = "with-async-unix-sockets")] |
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's weird seeing this feature, when it's not available in Cargo.toml.
What do you think about extracting the async-unix-socket code from this PR, but immediately opening one adding it, pending the updated tokio-uds crate?
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.
tokio-uds
actually got a release so I removed the feature and enable tokio-uds
support with the normal uds feature https://crates.io/crates/tokio-uds
src/parser.rs
Outdated
Some(detail) => fail!((kind, desc, detail.to_string())), | ||
None => fail!((kind, desc)), | ||
match parser.poll()? { | ||
Async::NotReady => panic!("Blocking read received WouldBlock error"), |
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.
Seeing a panic makes me kinda worry:
Is it likely that code is ever hit? Is it an actual error that can't be handled at all?
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.
Changed it to return WouldBlock
again which is the only reason why it could happen.
extern crate futures; | ||
extern crate partial_io; | ||
#[macro_use] | ||
extern crate quickcheck; |
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.
Yey for seeing quickcheck used here.
…ync Read instance
Version 0.2 were released a few weeks ago so we might as well enable it immediately
Sorry about that. I think I had it split up before but I ended up with a nasty rebase and had to remove it to make it easier. Tried to break it up into two commits again but looked pretty nasty still.
Not in the trait methods which return a future, but in all the other functions then certainly.
Did it as |
I think I resolved everything (can't find anything I missed at least)? |
Now uses combine v3.0.0 (stable release)
Hmmm, the tests fail on Windows:
|
Kicking of a println test on appveyor to see if it is an easy fix, otherwise I will try and get this work locally on windows tonight. |
@Marwes Just ping me again if this is resolved. I'm happy to merge it then :) |
These are rarely necessary and should hopefully fix the issue on windows since a rather weird use of net2 could be removed
@badboy I made some mistake when using the If someone ends up needing a function that takes an explicit handle we can revisit this and try to implement it then instead. Everything is green now! |
Great! Merging it now! |
Great work! Looking forward to trying this out! |
This adds support for communicating with redis instances asynchronously using futures and tokio. Putting this up now to show the changes and additions I have done so far.
Since the current response parser is tied to reading synchronously from a
Read
instance I rewrote it using https://github.com/Marwes/combine. I choose combine since I know it (obviously) and I wanted to try the state machine support I have been working on in tandem with this PR (this PR is what made me come up with the idea). Though I am a bit biased I do think it should be a good fit after I have worked out the bugs so I hope the change is acceptable. (I did see the Nom parser in #129 but didn't use it as I'd still need to code the state machine manually whereas combine creates it automatically).query(_async)
is currently the only added API and there is also a bunch of cleanup to be done.Closes #103