-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Implement graceful shutdown of servers and connections #153
Comments
Now that Golang 1.8 brought graceful shutdown [1], hopefully this is simpler? |
Yes, we could implement a similar API for both quic and h2quic. I didn't start working on this yet, if anyone wants to help please ping the issue and I will provide more context. |
I would like to give a hand here |
@lucas-clemente Is this issue still available? I see that the file has moved to |
@Kh4n Yes, we haven't worked on this yet. |
Looking at the most recent draft version for QUIC (v25), GOAWAY frames are no longer part of the spec (section C.21). Should we now send a STOP_SENDING frame with an appropriate application error code? |
GOAWAY is a HTTP feature, not a QUIC feature, see https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-goaway. |
That makes sense. I have added a GOAWAY frame into frames.go, following how the headersFrame is constructed. Looking at the spec, I need to send this frame on a control stream, but I do not have access to any of the sessions or their respective streams. There are a variety of ways I think we can track the sessions and streams; is there a preferred method of doing this that you have in mind? |
The control stream is opened here: https://github.com/lucas-clemente/quic-go/blob/62d3a4166a86de0f4a6a31f66411a6c3446f28fb/http3/server.go#L166-L171. It would probably make sense to save it in a member of the Does that make sense? Let me know if you run into any problems, I'm happy to help! |
Ok, so I opted to make a func (s *Server) CloseGracefully(timeout time.Duration) error {
s.closed.Set(true)
s.mutex.Lock()
defer s.mutex.Unlock()
// send GOAWAY frames to each open incoming stream
var err error
for str, ctrlStr := range s.ctrlStreams {
goaway := goawayFrame{
StreamID: str.StreamID(),
}
buf := bytes.NewBuffer([]byte{0})
goaway.Write(buf)
_, cerr := ctrlStr.Write(buf.Bytes())
if cerr != nil && err == nil {
err = cerr
}
}
// give time for those connections to either complete or self terminate
time.Sleep(timeout)
// close them
for ln := range s.listeners {
if cerr := (*ln).Close(); cerr != nil && err == nil {
err = cerr
}
}
return err
} I am confused by the |
Why do you need a map? There's only a single control stream: https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-control-streams. For the graceful shutdown, it looks like you just need to send a single GOAWAY frame on that control stream: https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-connection-shutdown. Regarding accepting the control stream using Does that make sense? |
Ah ok, I see that in the spec. I used a map originally because I thought we needed a GOAWAY frame for each open stream, and its easier to use a So I have added a basic implementation in. All it does is send a GOAWAY with the max StreamID as suggested in the spec, waits for a timeout, then closes the connections. You can get more fancy by checking for new requests and letting those complete, or timing the current ones, but I have not done that.
so I will see if I can fix that. Testing with the example web server and client seems to show that it is working. How does one check if an error is an Application error code 0x0, by the way? |
After sending the GOAWAY, do you stop accepting new requests?
That's expected. We're using a mock session to check what calls the http3 package is making to a QUIC session. You'll probably need to add a |
I thought that is what |
None of our tests actually dials a QUIC connection to an external server. That's what we have mocks for. |
Not sure I understand. What does the following code do? |
It's using a mock session, not a real |
How do I modify this mock session? I see that one of the tests returns an error 266 (0x10A) which is an error i just added indicating that the settings stream was not received. |
You can't modify the mock session. You can register calls and define return values via |
Finally fixed the errors. Cleaning up a little now and testing again with example server |
A common thing that I'm adding is keeping track of streams. However, I feel that the quic.Session interface should have some method for doing this by itself. I didn't see any method inside quic.Session, but I do see that the implementation has a streamsMap field. Is it ok to add a method like this? |
Why do you need to keep track of streams for graceful shutdown? |
Well the server almost certainly needs to, as it uses the streamIDs to decide what streams to allow to continue. As of right now, it just allows them all to continue. On the client side, requests with streamIDs >= goaway frame streamID need to be terminated, as they will not complete. However, for right now, I have not added that in, given that the server sends the max streamID anyways. |
Why is keeping track of the highest opened stream ID not sufficient?
As long as a request is in flight, there's a go routine somewhere in |
Keeping track of the highest streamID is not really necessary, if all you want to do is let all the requests finish and then quit. In that case, just send the highest possible streamID (2^62 - 4 i believe) and the servers job is done. But if you wanted to do something more complicated like evaluate what requests you want to process, you'll need to save the requests and their associated streams somewhere so you can know when they complete, as well as close any streams above the selected streamID. How would I notify the go routines in the client? I didn't see a built in way. I see a Cancel channel in the requests, maybe use that? |
That doesn't match my understanding of how GOAWAY works: https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-connection-shutdown
That's something that you'd need to build. Currently this is not yet supported. Note that there are multiple places where |
Hmm, I'm rereading it but I don't see any way to implement third to last paragraph of that section without keeping track of the streams/requests. I will take your word for it though. As far as the client goes, I am alright with adding it in. I will try those suggestions as well. |
|
Ok, maybe I have a misunderstanding of how the server works. Basically, the StreamID parameter in a GOAWAY frame indicates that all requests with StreamID >= to it have are not going to be processed. How would the server, without having access to a list of streams, close those requests? |
Wouldn't it make sense that the server processes all requests on streams that it already has accepted, and then stops accepting new streams after that? |
Ah, yes that is true for graceful shutdown. General case of GOAWAY allows server to terminate streams at will. I guess we should change the CloseGracefully doc comment to indicate that there is no timeout, and that all current requests will be processed. Otherwise, I don't think that would follow the quic-http spec. |
Was unable to work due to school, but I just finished it up. Redid most of it bc of merges. As of now:
|
Thank you @Kh4n. That sounds reasonable. Would you mind sending us a PR? |
Done, but it looks like I missed a test. Trying to see if I can fix |
Any news? |
For normal shutdowns, we should send
GOAWAY
frames, stop accepting new connections, and let all requests finish before shutting down the server.The text was updated successfully, but these errors were encountered: