context.Context is not just for cancelation

context.Context is not just for cancelation

9eed44f137609e6ce3b6f1e14f80b9e1?s=128

Masayuki Izumi

July 26, 2018
Tweet

Transcript

  1. context.Context is not just for cancelation Go(Un)Conference 3kg @izumin5210

  2. izumin5210 Engineer at Wantedly, Inc. Wantedly People ‣ Web Application

    Engineer - Server-side : Golang, Ruby, etc. - Web Frontend ‣ Interests in developer productivity on microservices ‣ I'll talk on builderscon Tokyo 2018 :)
  3. * https://golang.org/pkg/context/ Package context defines the Context type, which carries

    deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.* context.Context
  4. * https://golang.org/pkg/context/ Package context defines the Context type, which carries

    deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.* context.Context
  5. carries deadlines

  6. * https://golang.org/pkg/context/ Package context defines the Context type, which carries

    deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.* context.Context
  7. carries cancelation signals

  8. `context.Context`Usecases ‣ λΠϜΞ΢τ  FHଞͷαʔό΁ͷϦΫΤετΛ౤͚͛ͨͲɼ஗͘ͳ͍ͬͯΔ৔߹͸ఘΊ͍ͨ ‣ Ωϟϯηϧͷ఻ൖ  FHϨεϙϯε͸ฦ͠ऴΘͬͯΔͷʹHPSPVUJOF͕ࢮͳͣಈ͖ଓ͚͍ͯΔͷ͸ࠔΔ

  9. Cancelation ‣ ద੾ͳDBODFMॲཧ͸લఏ  ӈͷ͸ద౰͚ͩͲ ‣ ඪ४ύοέʔδͰ΋ଟ͘Ͱ࣮૷ࡁΈ  `database/sql`ͱ͔`net/http`ͱ͔

  10. ࠷ߴʹศར ͔͍͖ͭͬͯ·͠ΐ͏

  11. &/%

  12. &/%

  13. ࢖͍ॴ͕Θ͔Βͳ͍๨ΕΒΕ͕ͪͳ ΋͏Ұͭͷػೳ

  14. * https://golang.org/pkg/context/ Package context defines the Context type, which carries

    deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.* context.Context
  15. func WithValue(parent Context, key, val interface{}) Context func (Context) Value(key

    interface{}) interface{}
  16. ͳΜͰ΋͸͍ͬͯศརʢʁʣ ctx := context.TODO() ctx = context.WithValue(ctx, "ctxkey1", 100) ctx

    = context.WithValue(ctx, "ctxkey2", "foobar") // Output: // 100 foobar fmt.Println(ctx.Value("ctxkey1"), ctx.Value("ctxkey2"))
  17. func WithValue(parent Context, key, val interface{}) Context func (Context) Value(key

    interface{}) interface{}
  18. TFUUFSHFUUFSͰʢ͋Δఔ౓ʣܕΛकΖ͏ func GetFoo(ctx context.Context) string { if v, ok :=

    ctx.Value("foo").(string); ok { return v } return "" } func SetFoo(ctx context.Context, v string) context.Context { return context.WithValue(ctx, "foo", v) }
  19. func GetFoo(ctx context.Context) string { if v, ok := ctx.Value("foo").(string);

    ok { return v } return "" } func SetFoo(ctx context.Context, v string) context.Context { return context.WithValue(ctx, "foo", v) }
  20. type ctxKeyFoo struct {} func GetFoo(ctx context.Context) string { if

    v, ok := ctx.Value(ctxKeyFoo{}).(string); ok { return v } return "" } func SetFoo(ctx context.Context, v string) context.Context { return context.WithValue(ctx, ctxKeyFoo{}, v) } LFZͷॏෳΛ๷͝͏
  21. Ͱɼ

  22. ԿΛೖΕΔ͔ Կʹ࢖͏͔

  23. ! Bad pattern " ϩδοΫʢυϝΠϯ ӬଓԽϩδοΫʣʹؔΘΔ΋ͷΛೖΕͳ͍ type PostStore interface {

    Create(ctx context.Context) error } func (s *postStoreImpl) Create(ctx context.Context) error { title := GetPostTitleFromContext(ctx) body := GetPostBodyFromContext(ctx) // snip. }
  24. ! Bad pattern " JOUFSGBDFఆ͚ٛͩͰ͸ύϥϝλʹԿ͕ඞཁ͔Θ͔Βͳ͍DBMMFS͕಺෦࣮૷ʹґଘͯ͠͠·͏ type PostStore interface { Create(ctx

    context.Context) error } func (s *postStoreImpl) Create(ctx context.Context) error { title := GetPostTitleFromContext(ctx) body := GetPostBodyFromContext(ctx) // snip. }
  25. ܕ͕མͪΔ! JOUFSGBDF͕Θ͔ΓͮΒ͘ͳΔ!

  26. Կʹ࢖͑Δͷ͔!

  27. Idea - υϝΠϯͱؔ܎ͳ͍ϝλσʔλΛೖΕΔ ‣ ʢυϝΠϯɾӬଓԽɾFUDʣϩδοΫͰ࢖ΘΕͳ͍΋ͷͳΒྑ͍  ͦͷσʔλ͕ͳͯ͘΋ϩδοΫʹ͸Өڹ͕ग़ͳ͍΋ͷ  ͦͷϩδοΫͱ͸ຊ࣭తʹؔ܎ͳ͍΋ͷ 

    JOUFSGBDFʹυϝΠϯͱؔ܎ͳ͍΋ͷ͕͋ΔͱDPOGVTJOH!
  28. ϩδοΫͰ࢖ΘΕͳ͍΋ͷʁ ‣ ͳΜΒ͔ͷܭଌɾσʔλऩूʹ༻͍ΒΕΔ΋ͷ  FH  ϝτϦΫεऩू  αʔϏεؒ௨৴ͷτϨʔγϯά 

    ΤϥʔϨϙʔςΟϯά
  29. e.g. New Relic ʢϝτϦΫεऩूʣ ‣ αʔόͷϦΫΤετίϯςΩετ఻ൖ  `sql.DB.QueryContext`ͳͲΛXSBQͯ͠ɼͦͷதͰTFHNFOUΛ࡞ΕΕ͹ྑ͍  ϦΫΤετΛड͚Δͱ͜ΖˠΫΤϦ౤͛Δͱ͜Ζ·ͰDPOUFYU఻ൖͤ͞Δ

  30. e.g. New Relic `context.Context` ʹ `newrelic.Transaction` Λ֨ೲ͓ͯ͘͠ ʢNJEEMFXBSF `http.Handler` ΍H31$ͷJOUFSDFQUPSͰ΍Ε͹0,ʣ

    // SetTransaction stores newrelic transaction object into given context. func SetTransaction(ctx context.Context, txn newrelic.Transaction) context.Context { return context.WithValue(ctx, txnKey, txn) } https://godoc.org/github.com/izumin5210/newrelic-contrib-go/nrutil
  31. e.g. New Relic + DB `sql.DB` ΛXSBQͯ͠ɼDPOUFYU͔Β5SBOTBDUJPOΛऔΓग़͠TFHNFOUΛ࡞Δ func (w *dbWrapper)

    QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) { var ( rows *sql.Rows err error ) w.segment(ctx, q, args, func() { rows, err = w.original.QueryContext(ctx, q, args...) }) return rows, err } https://godoc.org/github.com/izumin5210/newrelic-contrib-go/nrsql
  32. Point ‣ ͋ΒΏΔϦΫΤετʹ͸ඞͣDPOUFYUΛ౉͓ͯ͘͠ Ͳ͏ͤλΠϜΞ΢τͱ͔ߟ͑ͳ͍ͱ͍͚ͳ͘ͳΔ ͱΓ͋͑ͣ౉͠ͱ͚͹ɼ৽͘͠ͳΜ͔ܭଌ͍ͨ͠ͱ͖ͱ͔ศར type PostStore interface { Create(ctx

    context.Context, title, body string, userID UserID) error }
  33. ‣ `context.Context`͸ΩϟϯηϧɾλΠϜΞ΢τͷͨΊͷϧʔϧ  ͋Δఔ౓Ҏ্େ͖ͳαʔϏεͩͱඞཁෆՄܽͳػೳ  ͚ͩͲɼͦΕ͚ͩ͡Όͳ͍ ‣ ϦΫΤετͷϝλ৘ใΛ͓࣋ͨͤͯ͘ͱ͍Ζ͍Ζܭଌ͢Δͱ͖ศར  3FRVFTU*%΍USBDJOHʹඞཁͳ৘ใॾʑɼϝτϦΫεऩूΦϒδΣΫτͳͲΛͬͦ͜Γ࣋ͪճͤΔ

    ‣ ϦΫΤετͬΆ͍ؔ਺ʹ͸`context.Context`Λඞͣ౉͢Α͏ʹ͢Δ  Ͳ͏ͤ͋ͱ͔Βཉ͘͠ͳΔͷͰɼ͍·͙͢࢖Θͳͯ͘΋࠷ॳ͔Β౉͓ͯ͜͠͏  ͋ͱ͔Β௥Ճ͢Δͷ͸୯७ʹखؒʹͳΔ
  34. https://www.wantedly.com/projects/223823