雑なメモ書き

気楽にいきます

golangのginでcontextをどう扱うべきだろうか

公式でのContextの扱いの解説

Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled.

サーバーへの着信要求はコンテキストを作成する必要があり、サーバーへの発信呼び出しはコンテキストを受け入れる必要があります。それらの間の関数呼び出しのチェーンは、コンテキストを伝播する必要があり、オプションで、WithCancel、WithDeadline、WithTimeout、またはWithValueを使用して作成された派生コンテキストに置き換えます。コンテキストがキャンセルされると、そのコンテキストから派生したすべてのコンテキストもキャンセルされます。

  • 本来引き回すべき物であって
  • 何かしら別の要件があった場合
  • cancel,deadline,timeout,withValueなどで派生コンテキストに置き換えるべきで
  • そのまま引き渡す方がいいに見える

gin.Contextのよくある扱い

  • 公式ではこのような例がある
package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
  • ここから関数を呼び出すときどうすべきなのだろうか
  • 例えば以下の様なメソッドを上記に追加した場合
func myMethod(ctx context.Context) {

}
  • c.Request.Context()を渡すべきなのか
myMethod(c.Request.Context())
  • gin.Contextをそのまま渡すべきなのか

gin.Contexを確認する

  • engine.Newをしているここにある
func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
    engine.pool.New = func() interface{} {
        return engine.allocateContext()
    }
    return engine
}

allocateContext

func (engine *Engine) allocateContext() *Context {
    return &Context{engine: engine, KeysMutex: &sync.RWMutex{}}
}

ServeHTTP

  • これがリクエスト単位で呼び出されている
289 // Conforms to the http.Handler interface.
290 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
291     c := engine.pool.Get().(*Context)
292     c.writermem.reset(w)
293     c.Request = req
294     c.reset()
295
296     engine.handleHTTPRequest(c)
297
298     engine.pool.Put(c)
299 }
  • このpool.Get()で呼び出されている
  • これの正体は
engine.pool.New = func() interface{} {
    return engine.allocateContext()
}
  • allocateContextなので
  • 毎回このこのコンテキストは割り当てられている
  • なので問題なさそうにみえる
  • 呼び出し単位で扱って良いように見える