11package test
22
33import (
4+ "bytes"
45 "context"
56 "crypto/tls"
67 "errors"
@@ -44,7 +45,7 @@ func (s *GatewayOpsTestSuite) TestGracefulShutdown() {
4445 GrpcCertificate : * gwCert ,
4546 DapiCertificate : * gwCert ,
4647 NumInstances : 1 ,
47-
48+ ProxyServices : [] string { "query" },
4849 StartupCallback : func (m * gateway.StartupInfo ) {
4950 gwStartInfoCh <- m
5051 },
@@ -94,6 +95,8 @@ func (s *GatewayOpsTestSuite) TestGracefulShutdown() {
9495 var eofs int
9596 var unexpectedErr error
9697 wg .Add (1 )
98+
99+ // Continually run basic requests against the gateway and continue during shutdown.
97100 go func () {
98101 defer wg .Done ()
99102
@@ -104,7 +107,7 @@ func (s *GatewayOpsTestSuite) TestGracefulShutdown() {
104107 case errors .Is (err , io .EOF ):
105108 // There is a small window between the flushing the request bytes to the socket and the http handler receiving
106109 // the bytes where the server sees the connection as idle and will close it. We record these errors and
107- // include them when checking all requests recieved responses.
110+ // include them when checking all requests received responses.
108111 eofs ++
109112 case errors .Is (err , syscall .ECONNREFUSED ):
110113 // This is what we expect to see once the listeners have closed
@@ -113,7 +116,7 @@ func (s *GatewayOpsTestSuite) TestGracefulShutdown() {
113116 // wroteRequest hook has not triggered. Such racy errors are unavoidable when running directly against an
114117 // http server.
115118 default :
116- // Any errors not mentinoned above are not expected and should cause a failure to be investigated.
119+ // Any errors not mentioned above are not expected and should cause a failure to be investigated.
117120 unexpectedErr = err
118121 return
119122 }
@@ -126,9 +129,37 @@ func (s *GatewayOpsTestSuite) TestGracefulShutdown() {
126129 }
127130 }()
128131
132+ wg .Add (1 )
133+
134+ slowReqSent := make (chan any )
135+ // Send a long running request (this one takes approx 2 mins) and ensure that the gateway forcibly stops this request
136+ // during shutdown.
137+ go func () {
138+ defer wg .Done ()
139+
140+ statement := []byte (`{"statement": "SELECT SUM(t + t2) AS total FROM ARRAY_RANGE(0,8000) AS t, ARRAY_RANGE(0,4000) AS t2"}` )
141+ reqBody := bytes .NewReader (statement )
142+
143+ req , err := http .NewRequestWithContext (context .Background (), "POST" , fmt .Sprintf ("https://%s/_p/query/query/service" , dapiAddr ), reqBody )
144+ assert .NoError (s .T (), err , "failed to create request with client trace" )
145+
146+ req .SetBasicAuth (testConfig .CbUser , testConfig .CbPass )
147+ req .Header .Set ("Content-Type" , "application/json" )
148+
149+ slowReqSent <- struct {}{}
150+ _ , err = dapiCli .Do (req )
151+
152+ // After the configured period shutdown will reuturn and we cancel the context. This triggers the go routine in Serve()
153+ // that stops the http server forcibly, causing EOFs on existing connections doing work.
154+ assert .ErrorIs (s .T (), err , io .EOF )
155+ }()
156+
129157 // Allow some requests to run against the gateway before shutting down
130158 time .Sleep (time .Second )
131159
160+ // Wait for the slow running request to be sent before starting shutdown
161+ <- slowReqSent
162+
132163 gw .Shutdown ()
133164
134165 wg .Wait ()
0 commit comments