-
Notifications
You must be signed in to change notification settings - Fork 321
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
Graceful shutdown #528
Comments
I think the server only shuts down in a I suppose this could possibly be desirable for |
Oh I think I see what you are saying - essentially we should have a way to await all tasks which are still alive, I think? |
Yes. I mean, new requests are not handled, but at least those that are still awaiting response should be finished |
Was just coming to make an issue for this. It's critical from a production standpoint that a load balancer or swarm manager can initiate a controlled or graceful shutdown. Any requests that came in before the "shut down please" request should still finish. Icing would be a way to shutdown with a timeout but I think we can compose that if there is an API for shutdown. The way I envision usage would be a |
Definitely agree with @mehcode. Having the ability to gracefully shutdown the server is pretty important for me as well. 👍 |
FWIW here is how I do it using the async-ctrlc crate: use async_std::prelude::FutureExt;
async fn run() -> Result<()> {
let ctrlc = async {
CtrlC::new().expect("Cannot use CTRL-C handler").await;
println!("termination signal received, stopping server...");
Ok(())
};
let app = async {
let app = tide::new();
app.listen("localhost:8080").await
};
app.race(ctrlc).await?;
println!("server stopped.");
Ok(())
} Please note that this will not wait for all running requests to complete, this is a "brutal graceful shutdown", if I dare. But at least it handles SIGTERM and CTRL-C shutdown (cross platform) which is better than nothing. |
I did try messing around with adding graceful shutdown to The actual waiting for requests to finish is pretty simple with the design I used: spawn a task that uses a The other part seems just as simple as well: if someone has initiated a shutdown (I used an To get around this I was able to use a If anyone has thoughts on how to avoid the issue, do let me know and I can give them a try and report back. Or if anyone else wants to take a stab at implementing it themselves, by all means 👍 |
I've thought about this a little in the past, and gathered some notes on this. What I think we need to do is use Lines 334 to 335 in 293610b
Lines 292 to 293 in 293610b
This allows us to stop receiving incoming requests and allow the server to gracefully exit once a token has been received. However let src = StopSource::new();
let token = src.token();
// Send a token to each app we initialize
task::spawn(async move {
let mut app = tide::new();
app.stop_on(token.clone());
app.listen("localhost:8080").await?;
});
// app will stop receiving requests once we drop the `StopSource`.
// this can be be done in response to e.g. a signal handler. It's a bit of a question how we'd combine this today. We could probably define our own |
I'm learning Rust, so I tried to see if I could fix this: https://github.com/http-rs/tide/compare/main...GWBasic:GracefulShutdown2?w=1 Specifically, I solved @repnop 's problem by using future::select. It allows awaiting on two futures and returns when the first completes: https://github.com/http-rs/tide/compare/main...GWBasic:GracefulShutdown2?w=1#diff-1f14b8b6953e2388b19efaf814862a89c0b57c45a6814f79ed373fde05d864d0R82 I then created a "CancelationToken" future that returns once it is canceled. So far, I only know how to test tcp_listener, and I adjusted its tests to work by canceling gracefully: https://github.com/http-rs/tide/compare/main...GWBasic:GracefulShutdown2?w=1#diff-0f530b03216a301ca1004179731df8510170be421ff7abf545db673eca3bc4beR12 I haven't done anything with waiting for ongoing requests to complete, or testing other listeners. I must admit that I've tried to return futures from the listen function; but that leads to lots of fighting with the borrow checker. That's a bit more than I can handle as a Rust novice. ;) Anyway, if you believe my branch is in line with tide's overall design philosophy, I'm happy to make a pull request and change whatever you want. (Especially for testing the rest of the listeners.) Otherwise, this bug was a great learning exercise for me. |
@GWBasic Can you make at least a draft PR? |
@Fishrock123 : See #746 (Note that some merge conflicts creeped in.) Looking forward to your feedback! |
@Fishrock123 : I had too many merge conflicts in #746, so I created #766 It uses futures-lite. Thoughts? Again, I'm mostly interested in learning Rust, so if you'd rather do this in a different way, that's fine with me. |
I've also made #836. Please give any thoughts or suggestions! |
Any news? |
I think it's important to break this down into two parts:
A design for Future Rust.I've recently written about async cancellation. For Tide, the change we need to make is that our calls to We could do this using e.g. the Using (for example) the use stop_token::prelude::*;
use std::time::{Duration, Instant};
use async_std::task;
// Create the server and register a route which takes some time to complete.
let mut server = tide::new();
server.at("/").get(|_| async move {
task::sleep(Duration::from_secs(10)).await;
Ok("hello")
});
// Start listening for incoming requests, but close the server after 3 secs.
// Instead of waiting for a duration we could instead also wait for an external signal.
// Either way, this should graciously wait for all requests to terminate.
server
.listen("localhost:8080")
.timeout_at(Instant::now() + Duration::from_secs(3));
.await?;
println!("finished cleaning up all requests!"); What can we do now?Unfortunately we currently don't have access to Likely the easiest route to go would be to hard-code a I hope this helps clarify what the current thinking is! |
Thanks! I'm sure I read on a page linked from your blog that Maybe |
Hey, thanks for the mention. I'm leaving this thread: I only attempted (#766) as a learning exercise. Best of luck! |
Footnotes
|
Tide could store
JoinHandle
of every request it handles, andawait
them when server is shutting down.tide/src/server.rs
Lines 300 to 311 in 7d0b848
async-std book
The text was updated successfully, but these errors were encountered: