context.Err() on complete - go

I am making multiple RPC calls to my server where the handler looks like:
func (h *handler) GetData(ctx context.Context, request Payload) (*Data, error) {
go func(ctx context.Context) {
for {
test := 0
select {
case <-ctx.Done():
if ctx.Err() == context.Canceled {
log.Info(ctx.Err())
test = 1
break
}
}
if test == 1 {
break
}
}
}(ctx)
data := fetchData(request)
return data, nil
}
the fetchData API takes around 5 seconds to get data and reply back to my service. Meanwhile if the client requests again, then I abort the old request and fire a new request. The abort is not visible on context object.
Rather ctx.Err() shows a value of context.Canceled even when the calls are not cancelled and end gracefully with expected data.
I am new to Go and don't understand how exactly context manages Cancels, timeout and completion.
Some insight on the behaviour will be helpful.

From the docs (emphasize mine):
For incoming server requests, the context is canceled when the client's connection closes, the request is canceled (with HTTP/2), or when the ServeHTTP method returns.
In other words, cancellation does not necessarly mean that the client aborted the request.
Contexts that can be canceled must be canceled eventually, and the HTTP server takes care of that:
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
What you observe works as intended.

Related

Using `Context` to implement timeout

Assuming that I have a function that sends web requests to an API endpoint, I would like to add a timeout to the client so that if the call is taking too long, the operation breaks either by returning an error or panicing the current thread.
Another assumption is that, the client function (the function that sends web requests) comes from a library and it has been implemented in a synchronous way.
Let's have a look at the client function's signature:
func Send(params map[string]string) (*http.Response, error)
I would like to write a wrapper around this function to add a timeout mechanism. To do that, I can do:
func SendWithTimeout(ctx context.Context, params map[string]string) (*http.Response, error) {
completed := make(chan bool)
go func() {
res, err := Send(params)
_ = res
_ = err
completed <- true
}()
for {
select {
case <-ctx.Done():
{
return nil, errors.New("Cancelled")
}
case <-completed:
{
return nil, nil // just to test how this method works
}
}
}
}
Now when I call the new function and pass a cancellable context, I successfully get a cancellation error, but the goroutine that is running the original Send function keeps on running to the end.
Since, the function makes an API call meaning that establishing socket/TCP connections are actually involved in the background, it is not a good practice to leave a long-running API behind the scene.
Is there any standard way to interrupt the original Send function when the context.Done() is hit?
This is a "poor" design choice to add context support to an existing API / implementation that did not support it earlier. Context support should be added to the existing Send() implementation that uses it / monitors it, renaming it to SendWithTimeout(), and provide a new Send() function that takes no context, and calls SendWithTimeout() with context.TODO() or context.Background().
For example if your Send() function makes an outgoing HTTP call, that may be achieved by using http.NewRequest() followed by Client.Do(). In the new, context-aware version use http.NewRequestWithContext().
If you have a Send() function which you cannot change, then you're "out of luck". The function itself has to support the context or cancellation. You can't abort it from the outside.
See related:
Terminating function execution if a context is cancelled
Is it possible to cancel unfinished goroutines?
Stopping running function using context timeout in Golang
cancel a blocking operation in Go

gRPC Unary Call Connection State Check on Server

I have an unary gRPC call that can take up to few minute to be processed on my Go gRPC server (involves human agent on the mobile APP). I would like to know if there is a way to check if the connection has been terminated on the client side before sending the response.
I found the solution for ServerStreaming case with Context Status.Done channel, but it does not work for my Unary RPC.
Below is the signature of the function where the control should be made:
func (*Server) EndpointName(ctx context.Context, in *pb.EndpointRequest) (*pb.EndpointResponse, error) {
As per the function definition shown in your question, the endpoint function is passed a context (ctx context.Context). If the connection drops the context will be cancelled.
For example I can modify the helloworld example so that it simulates a long running job:
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
select {
case <-ctx.Done():
fmt.Println("Context is done:", ctx.Err())
return nil, status.Error(codes.Canceled, "does not matter as nothing will ever get this anyway...")
case <-time.After(time.Minute):
// This simulates a long-running process. In reality the process itself should be checking the context
}
return &pb.HelloReply{}, nil
}
To test this I altered greeter_client to call the function and then panic() whilst waiting for the response; the server outputs:
2022/08/08 08:16:57 server listening at [::]:50051
Context is done: context canceled

CloseHandler is not called for a gorilla/websocket if I am not reading messages anywhere, I simply get a write error eventually

I have a websocket server using gorilla/websocket.
I have a situation where I am simply writing messages to a set of websockets. My custom CloseHandler is never called when I close the websocket on the browser side.
However, adding a goroutine that calls ReadMessage indefinitely (till some error) leads to the CloseHandler being invoked.
Here's the basic idea:
In one goroutine, I run something like this:
for {
for client := range clients {
client.stream <- data
}
time.Sleep(time.Second)
}
and the other code, called in a separate goroutine, one per client:
go (func() {
// If I call wsock.ReadMessage here, my CloseHandler works!
})()
for msg := range myclient.stream {
if err := wsock.WriteMessage(websocket.TextMessage, msg); err != nil {
break
}
}
When I close the websocket on the browser side, I expect the CloseHandler to be called, however, it's never called, instead, I eventually get an error on the WriteMessage call.
The close handler is called when a close message is received from the peer. The application must read the connection to receive close and other control messages.
If the application does not read the connection or the peer does not send a close message, then the close handler will not be called.
If your goal is to detect closed connections, then read the connection until an error as returned as shown in the documentation:
func readLoop(c *websocket.Conn) {
for {
if _, _, err := c.NextReader(); err != nil {
c.Close()
break
}
}
}
The application should only set a close handler when the application must perform some action before the connection bounces the close message back to the peer.

Is there anyway to close client request in golang/gin?

Using gin framework.
Is there anyway to notify client to close request connection, then server handler can do any back-ground jobs without letting the clients to wait on the connection?
func Test(c *gin.Context) {
c.String(200, "ok")
// close client request, then do some jobs, for example sync data with remote server.
//
}
Yes, you can do that. By simply returning from the handler. And the background job you want to do, you should put that on a new goroutine.
Note that the connection and/or request may be put back into a pool, but that is irrelevant, the client will see that serving the request ended. You achieve what you want.
Something like this:
func Test(c *gin.Context) {
c.String(200, "ok")
// By returning from this function, response will be sent to the client
// and the connection to the client will be closed
// Started goroutine will live on, of course:
go func() {
// This function will continue to execute...
}()
}
Also see: Goroutine execution inside an http handler

MGO and long running Web Services - recovery

I've written a REST web service that uses mongo as the backend data store. I was wondering at this stage (before deployment), what the best practices were, considering a service that essentially runs forever(ish).
Currently, I'm following this type of pattern:
// database.go
...
type DataStore struct {
mongoSession *mgo.Session
}
...
func (d *DataStore) OpenSession () {
... // read setup from environment
mongoSession, err = mgo.Dial(mongoURI)
if err != nil {}
...
}
func (d *DataStore) CloseSession() {...}
func (d *DataStore) Find (...) (results...) {
s := d.mongoSession.Copy()
defer s.Close()
// do stuff, return results
}
In main.go:
func main() {
ds := NewDataStore()
ds.OpenSession()
defer ds.CloseSession()
// Web Service Routes..
...
ws.Handle("/find/{abc}", doFindFunc)
...
}
My question is - what's the recommended practice for recovery from session that has timed out, lost connection (the mongo service provider I'm using is remote, so I assume that this will happen), so on any particular web service call, the database session may no longer work? How do people handle these cases to detect that the session is no longer valid and a "fresh" one should be established?
Thanks!
what you may want is to do the session .Copy() for each incoming HTTP request (with deffered .Close()), copy again from the new session in your handlers if ever needed..
connections and reconnections are managed by mgo, you can stop and restart MongoDB while making an HTTP request to your web service to see how its affected.
if there's a db connection problem while handling an HTTP request, a db operation will eventually timeout (timeout can be configured by using DialWithTimeout instead of the regular Dial, so you can respond with a 5xx HTTP error code in such case.

Resources