4

I recently started rewriting some of my Python services in Go to speed them up and came across this section of the gin documentation: https://github.com/gin-gonic/gin#goroutines-inside-a-middleware

So I understand the instructions, but I'm trying to understand why? What is the significance of making a copy, and what problem is introduced if I do not make a copy of context for goroutines within handlers?

Michael
  • 1,428
  • 3
  • 15
  • 34
  • Checking the code, the `copy` doesn't hold all values of the original context, but just a few. At first glance e.g. the `Lock` which isn't copied, and the response writer gets replaced on `copy`. Both makes sence since the go routine can finish at any time, even long after the requests has been processed and answered. The original response writer might already be closed, and the original `lock` already be unlocked. Or in opposite the go routine might finish way early, premature unlocking the `lock` or closing the response writer. Similar issues would probably arise with other skipped fields. – NotX Sep 18 '22 at 18:23

1 Answers1

1

Gin-gonic is async itself which makes it great. If you are using concurrency within your handlers it's very likely you will face situation when Context, which is struct goes

  • outdated since it's holding the data from each request (parameters, keys protected by mutex)

  • empty, which will cause a fallback to default Context

here's how it looks like:

type Context struct {
writermem responseWriter
Request   *http.Request
Writer    ResponseWriter

Params   Params
handlers HandlersChain
index    int8
fullPath string

engine       *Engine
params       *Params
skippedNodes *[]skippedNode

// This mutex protects Keys map.
mu sync.RWMutex

// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]any

// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs

// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string

// queryCache caches the query result from c.Request.URL.Query().
queryCache url.Values

// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url.Values

// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests.
sameSite http.SameSite

}

In order to foresee such issues and race conditions - you must use Copy whenever your handlers are using go concurrency

here's a quote from gin-gonic repository:

// Copy returns a copy of the current context that can be safely used outside the request's scope.
// This has to be used when the context has to be passed to a goroutine.