gogoroutinewaitgroupdefer-keyword

Golang 'defer' causing delay in sending(receiving) API response


I have created an API, which after processing the request sends a response and starts a background goroutine which logs some messages. I have used 'defer' to execute goroutine after the API request has been handled.

Following are the code snippets:

sendResponse Pseudocode:

{
  ...
  res.Header().Add("Content-Type", "application/json")
  json.NewEncoder(res).Encode(response)
}

Response struct:

type Response struct {
   StatusCode int16  `json:"statusCode"`
   Message    string `json:"message"`
}

API Body:

{ 
 ...
 defer logMsgs()
 sendAPIResponse()
 return
}

logMsgs Pseudocode:

func logMsgs(){
   time.Sleep(10*time.Seconds)
   wg := sync.Waitgroup{}
   wg.Add(1)
   go func(){
      for i = 1 to 10
         print(i)
      wg.Done()
   }()
   wg.Wait()
}

Expected Outcome is to receive the API response and after few seconds(here 10s) value of 'i' is printed from 1 to 10.

But in Actual Outcome, API response is received after 10s.

As per my understanding defer functions are called after the surrounding (or current/enclosing) function terminates or returns.

So I must first receive the API response and later values of 'i' must be logged. But in my case I am receiving the response after some seconds(10s) are elapsed. This would be a major issue if value of 'i' is quite large.

It will be helpful if someone explains this abnormal behavior and provides possible solutions. Thanks


Solution

  • I have used 'defer' to execute goroutine after the API request has been handled.

    defer does not launch a goroutine. It executes the deferred function on the calling goroutine. And it's very much possible that the response is cached and data written to it is not flushed until you return from your handler.

    If you want to execute the logging in a new goroutine, use the go keyword like this:

    defer func() {
        go logMsgs()
    }()
    sendAPIResponse()
    return
    

    Note however that after returning from the handler, launched goroutines cannot access request and response specific objects as they may be reused for new requests. So save / pass everything that the logger needs in order to safely use them after returning from the handler.