-
Notifications
You must be signed in to change notification settings - Fork 125
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
Migrate Gotham to std Futures to support Async/Await #370
Conversation
With the anticipated release of async-await in stable rust this week, I took an effort to migrate Gotham to run on std futures using the pre-release versions of the dependencies (tokio, hyper, etc). *NOTE*: This doesn't attempt to introduce `await` or `async fn` into the codebase (there was a single unavoidable case due to an `tokio::File::metadata` API change). That is designed as a future activity. This migration involved a few key efforts: 1. Convert from Futures 0.1 to Futures-preview 0.3 (mostly `Future<Item=>` to `Future<Output=Result<>>`). 2. Update dependencies to pre-release versions (`tokio-0.2` and `hyper-0.13`). There's still other dependencies that are outstanding and blocking the full release. 3. Migrate Handler trait to a pinned box HandlerFuture. This is a **work-in-progress** with a few blockers before this would be ready: Gotham Dependencies: - [ ] Update Futures from `futures-preview` to `futures = 0.3` when the other dependencies (hyper, tokio, etc) update in concert. - [ ] Update Tokio to `0.2` from alpha pre-releases - [ ] Update Hyper to `0.13` from alpha pre-releases - [ ] Update Tower-Service to `0.3` from alpha pre-releases. Hyper is migrating many of its traits to `tower-service::Service` and so is now a direct dependency. - [ ] Released version of `futures_rustls` which is currently a branch of `tokio-rustls` ported to Futures-preview - [ ] Released version of `futures-tokio-compat` or suggested `tokio::compat` library for bridging `futures::AsyncRead` and `tokio::AsyncRead`. See tokio-rs/tokio#1297 and async-rs/async-std#54 Middleware Dependencies: - [ ] Diesel - Requires updated release of `tokio-threadpool` - [ ] JWT - Requires updated release of `jsonwebtoken` since it's dependency `ring` conflicts withs `futures-rustls`.
The following examples were not updated (and thus commented out in `Cargo.toml`) due to incompatible dependencies: - [ ] `hello_world_until` is dependent on a compatible version of `tokio-signal` - [ ] `diesel` is dependent on the Diesel middleware - [ ] `openssl` is dependent on `tokio-openssl` - [ ] `websocket` is dependent on `tokio-tungstenite`
9ff138c
to
f4532f8
Compare
Wow - thanks @kevinastone! This is a huge effort! This is definitely something that would have been inevitable soon but we hadn't managed to start yet. We'll see if we can divide and conquer the remaining items on your list 👍 |
After applying this patch, will |
Without async traits, I don't believe we can implement Here's an example: |
I have made a PoC of how I think we could make Unfortunately, it had to break support for the
|
I would vote for |
So, I started working on this branch to tick off items above the list, but I got stuck with the following:
I can't implement the above hyper trait for a tokio_rustls struct. My branch is at: https://github.com/reign-rs/gotham/tree/await |
It was a considerable amount of work to port to It seems to run correctly, I just haven't got all the doctests to run successfully. I'd also like to split up the commits into something more manageable. |
Thanks for the efforts here so far @kevinastone @pksunkara . |
I'll queue up a review by EOW. |
This involved changing the way that the runtime is managed and handling `block_on` for synchronously running futures. Hyper-0.13 required changing to the tower_service trait for implementing a `Connect` trait used for the TLS and plain tcp stream wrappers. The other major change is that Hyper migrated from a `Chunk` trait to use `Bytes`. Bytes-0.5 is now a trait rather than a struct which no longer implements `Extends<u8>` requiring a new `BytesMut` buffer for some common concatenation operations.
I pushed up one big commit for the final |
Codecov Report
@@ Coverage Diff @@
## master #370 +/- ##
=========================================
Coverage ? 79.51%
=========================================
Files ? 105
Lines ? 4964
Branches ? 0
=========================================
Hits ? 3947
Misses ? 1017
Partials ? 0
Continue to review full report at Codecov.
|
Replace tokio-signal with tokio (signal was migrated into tokio itself)
3089a24
to
48a4c9c
Compare
The JWT middleware is still blocked on an updated version of Tokio-openssl (required by Tokio-tungstenite (required by Besides these middleware/examples above, the rest of the outstanding items for this PR are complete. |
I have reviewed it pretty extensively. I don't see any issues with the 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.
I haven't had time to review fully, but things seem good to me so far. I've left a few comments but they're just mainly for my benefit rather than any specific issues.
I'll try take another pass, when I have time to actually stare at it for a while, but if others give approval for merging I'm not going to stand in the way. Seems like great work!
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 work @kevinastone - thanks, this is a huge amount of effort!
I'm going to take the bold move of merging this in :)
Since it's necessarily such a large PR, if there are any omissions or improvements, we can deal with them in separate PRs. This way we can keep things integrating and get some wider testing on master before doing the next release.
It's a shame about the dependency issues - I'll create issues for those and we can work out what to do about them later.
Thanks for the merge. Now we need to get #281 done so we can properly use async/await. |
As part of gotham-rs#370, this function was updated from spawning a future to awaiting it through `try_for_each_concurrent`. As as a result of that change, any protocol error will result in terminating the server. This can happen if a client is connecting using HTTP instead of HTTPS, or even the client rejecting the server's HTTPS certificate and informing the server of said rejection! To reproduce this, you can start the hello_world_tls example, and send a request using plain HTTP. The server will immediately exit. With this patch applied, it does not.
As part of gotham-rs#370, this function was updated from spawning a future to awaiting it through `try_for_each_concurrent`. Another effect of that (in addition to the one mentioned in the earlier post) is that it means Gotham cannot saturate more than one CPU core, because all requests will be running in the same task, and therefore will be polled by the same CPU core. The task is already `Send + 'static`, so it can trivially be spawned off in its own task. Doing so allows Gotham to saturate more than one CPU core. One way to repro this is to update the async example to add a request handler that performs a busy loop (but does yield the CPU so that other tasks may be executed, if any are available): ``` diff --git a/examples/handlers/async_handlers/Cargo.toml b/examples/handlers/async_handlers/Cargo.toml index 6c1eae0..085a391 100644 --- a/examples/handlers/async_handlers/Cargo.toml +++ b/examples/handlers/async_handlers/Cargo.toml @@ -14,3 +14,4 @@ mime = "0.3" futures = "0.3.1" serde = "1" serde_derive = "1" +tokio = "0.2" diff --git a/examples/handlers/async_handlers/src/main.rs b/examples/handlers/async_handlers/src/main.rs index 9bb0167..2922c61 100644 --- a/examples/handlers/async_handlers/src/main.rs +++ b/examples/handlers/async_handlers/src/main.rs @@ -235,6 +235,15 @@ fn parallel_handler(mut state: State) -> Pin<Box<HandlerFuture>> { .boxed() } +fn busy_handler(state: State) -> Pin<Box<HandlerFuture>> { + async move { + loop { + tokio::task::yield_now().await; + } + } + .boxed() +} + /// Create a `Router`. fn router() -> Router { build_simple_router(|route| { @@ -250,6 +259,9 @@ fn router() -> Router { .get("/parallel") .with_query_string_extractor::<QueryStringExtractor>() .to(parallel_handler); + route + .get("/busy") + .to(busy_handler); }) } ``` Then, send multiple requests to this endpoint. Notice that without this patch, Gotham's CPU usage saturates at 100%, whereas without this patch, it does not (and scales proportionally to the number of concurrent such requests).
With the anticipated release of async-await in stable rust this week, I took an effort to migrate Gotham to run on std futures using the pre-release versions of the dependencies (tokio, hyper, etc).
NOTE: This doesn't attempt to introduce
await
orasync fn
into the codebase (there was a single unavoidable case due to antokio::File::metadata
API change). That is designed as a future activity.This migration involved a few key efforts:
Future<Item=>
toFuture<Output=Result<>>
).tokio-0.2
andhyper-0.13
). There's still other dependencies that are outstanding and blocking the full release.This is a work-in-progress with a few blockers before this would be ready:
Gotham Dependencies:
futures-preview
tofutures = 0.3
when the other dependencies (hyper, tokio, etc) update in concert.0.2
from alpha pre-releases0.13
from alpha pre-releases0.3
from alpha pre-releases. Hyper is migrating many of its traits totower-service::Service
and so is now a direct dependency.futures_rustls
which is currently a branch oftokio-rustls
ported to Futures-previewfutures-tokio-compat
or suggestedtokio::compat
library for bridgingfutures::AsyncRead
andtokio::AsyncRead
. See Use futures AsyncRead, AsyncWrite, ready! macro everywhere tokio-rs/tokio#1297and Compatibility with tokio? async-rs/async-std#54
Middleware Dependencies:
tokio-threadpool
jsonwebtoken
since it's dependencyring
conflicts withsfutures-rustls
.The following examples were not updated (and thus commented out in
Cargo.toml
) due to incompatible dependencies:hello_world_until
is dependent on a compatible version oftokio-signal
diesel
is dependent on the Diesel middlewareopenssl
is dependent ontokio-openssl
websocket
is dependent ontokio-tungstenite