Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Context of Go

The Context of Go

Conference talk @ StayAtHomeConf 2020

The "context" package is one of the most widely used packages on Go applications. However, its usages are a bit tricky, specially for newcomers. So, why don't we take profit of this confinement situation to learn a bit about its usages?

More Decks by Joan López de la Franca Beltran

Other Decks in Programming

Transcript

  1. THE CONTEXT OF GO @ME IN 1 MINUTE ‣ Joan

    López de la Franca ‣ a.k.a. @joanjan14 or @joanlopez
  2. THE CONTEXT OF GO @ME IN 1 MINUTE ‣ Joan

    López de la Franca ‣ a.k.a. @joanjan14 or @joanlopez
 





 ‣ Software Engineer @ Cabify (Maxi Mobility)
  3. THE CONTEXT OF GO @ME IN 1 MINUTE ‣ Joan

    López de la Franca ‣ a.k.a. @joanjan14 or @joanlopez
 





 ‣ Software Engineer @ Cabify (Maxi Mobility)
 





 ‣ Junior speaker ‣ @GopherConRu ‣ @gopherconuk
  4. THE CONTEXT OF GO @FRIENDSOFGOTECH IN 1 MINUTE ‣ Since

    the 1st of January 2019 ‣ We have +700 followers
  5. THE CONTEXT OF GO @FRIENDSOFGOTECH IN 1 MINUTE ‣ Since

    the 1st of January 2019 ‣ We have +700 followers ‣ Written +60 blog posts
  6. THE CONTEXT OF GO @FRIENDSOFGOTECH IN 1 MINUTE ‣ Since

    the 1st of January 2019 ‣ We have +700 followers ‣ Written +60 blog posts ‣ In-person & online trainings
  7. THE CONTEXT OF GO @FRIENDSOFGOTECH IN 1 MINUTE ‣ Since

    the 1st of January 2019 ‣ We have +700 followers ‣ Written +60 blog posts ‣ In-person & online trainings ‣ Killgrave: mock server -the easy way-
  8. EXPECTATIONS… EXPECT…WHAT? THE CONTEXT OF GO I’m gonna talk about

    cookies —> NO Junior-level, but not from scratch —> NO context.Context core package —> NO
  9. SHOPPING TIME THE CONTEXT OF GO type Gopher struct {

    IngredientToBuy string } func (g *Gopher) Buy() error { shoppingTime := time.Duration(rand.Int63n(10)) * time.Second time.Sleep(shoppingTime) return nil } func NewGopher(ingredientToBuy string) *Gopher { return &Gopher{ IngredientToBuy: ingredientToBuy, } } func main() { ingredients := []string{ "sugar", "flour", "eggs", "butter", "yeast", "chocolate", } for _, ingredient := range ingredients { gopher := NewGopher(ingredient) err := gopher.Buy() if err != nil { fmt.Printf( "Gophers couldn't find %s\n", ingredient, ) continue } fmt.Printf( "Gophers found %s\n", ingredient, ) } }
  10. SHOPPING TIME (2) THE CONTEXT OF GO func main() {

    ingredients := []string{ "sugar", "flour", "eggs", "butter", "yeast", "chocolate", } wg := new(sync.WaitGroup) for _, ingredient := range ingredients { wg.Add(1) go dispatchGopher(ingredient, wg) } wg.Wait() } func dispatchGopher(ingredient string, wg *sync.WaitGroup) { defer wg.Done() gopher := NewGopher(ingredient) err := gopher.Buy() if err != nil { fmt.Printf( "Gophers couldn't find %s\n", ingredient, ) return } fmt.Printf( "Gophers found %s\n", ingredient, ) }
  11. SHOPPING TIME (3) THE CONTEXT OF GO type SMS struct

    { Ingredient string Err error } type Gopher struct { IngredientToBuy string Mobile chan<- SMS } func NewGopher(mobile chan<- SMS, ingredientToBuy string) *Gopher { return &Gopher{ Mobile: mobile, IngredientToBuy: ingredientToBuy, } } func (g *Gopher) Buy() { shoppingTime := time.Duration(rand.Int63n(10)) * time.Second time.Sleep(shoppingTime) g.Mobile <- SMS{ Ingredient: g.IngredientToBuy, Err: nil, } } func dispatchGopher(ingredient string, mobile chan<- SMS) { gopher := NewGopher(mobile, ingredient) gopher.Buy() } func main() { ingredients := map[string]bool{ "sugar": false, "flour": false, "eggs": false, "butter": false, "yeast": false, "chocolate": false, } mobile := make(chan SMS) for ingredient := range ingredients { go dispatchGopher(ingredient, mobile) } for i := 0; i < len(ingredients); i++ { sms := <-mobile result := sms.Err == nil ingredients[sms.Ingredient] = result } fmt.Printf( "Shopping list summary %v\n", ingredients, ) }
  12. FALLACIES OF DISTRIBUTED COMPUTING THE CONTEXT OF GO “The network

    is reliable” “Latency is zero” “Bandwidth is infinite” “The network is secure” … by L. Peter Deutsch Wikipedia
  13. FALLACIES OF DISTRIBUTED COMPUTING THE CONTEXT OF GO “The network

    is reliable” —> Shops are closed “Latency is zero” —> Missing ingredients “Bandwidth is infinite” —> Police controls “The network is secure” —> Suffer the symptoms … by COVID-19 Wikipedia CORONAVIRUS LOCKDOWN
  14. THE CONTEXT PACKAGE (2) THE CONTEXT OF GO // A

    Context carries a deadline, cancelation signal, and request-scoped values // across API boundaries. Its methods are safe for simultaneous use by multiple // goroutines. type Context interface { // Done returns a channel that is closed when this Context is canceled // or times out. Done() <-chan struct{} // Err indicates why this context was canceled, after the Done channel // is closed. Err() error ... }
  15. SHOPPING TIME (4) THE CONTEXT OF GO func randBool() bool

    { return rand.Float32() < 0.5 } func (g *Gopher) Buy(ctx context.Context) { var err error shoppingTime := time.Duration(rand.Int63n(10)) * time.Second select { case <-time.After(shoppingTime): if randBool() { err = nil } else { err = errors.New("missing ingredient") } case <-ctx.Done(): err = ctx.Err() } g.Mobile <- SMS{ Ingredient: g.IngredientToBuy, Err: err, } }
  16. THE CONTEXT PACKAGE (5) THE CONTEXT OF GO ctx :=

    context.Background() child1, cancel1 := context.WithCancel(ctx) CTX CHILD1
  17. THE CONTEXT PACKAGE (6) THE CONTEXT OF GO ctx :=

    context.Background() child1, cancel1 := context.WithCancel(ctx) child2, cancel2 := context.WithCancel(ctx) CTX CHILD1 CHILD2
  18. THE CONTEXT PACKAGE (7) THE CONTEXT OF GO ctx :=

    context.Background() child1, cancel1 := context.WithCancel(ctx) child2, cancel2 := context.WithCancel(ctx) child11, cancel11 := context.WithCancel(child1) child21, cancel21 := context.WithCancel(child2) CTX CHILD1 CHILD21 CHILD2 CHILD22
  19. THE CONTEXT PACKAGE (8) THE CONTEXT OF GO ctx :=

    context.Background() child1, cancel1 := context.WithCancel(ctx) child2, cancel2 := context.WithCancel(ctx) child11, cancel11 := context.WithCancel(child1) child21, cancel21 := context.WithCancel(child2) cancel1() CTX CHILD1 CHILD21 CHILD2 CHILD22
  20. THE CONTEXT PACKAGE (9) THE CONTEXT OF GO ctx :=

    context.Background() child1, cancel1 := context.WithCancel(ctx) child2, cancel2 := context.WithCancel(ctx) child11, cancel11 := context.WithCancel(child1) child21, cancel21 := context.WithCancel(child2) cancel1() cancel21() CTX CHILD1 CHILD21 CHILD2 CHILD22
  21. SHOPPING TIME (5) THE CONTEXT OF GO func main() {

    ingredients := map[string]error{ … } mobile := make(chan SMS) ctx, cancel := context.WithCancel(context.Background()) defer cancel() for ingredient := range ingredients { go dispatchGopher(ctx, ingredient, mobile) } for i := 0; i < len(ingredients); i++ { sms := <-mobile if sms.Err != nil { cancel() } ingredients[sms.Ingredient] = sms.Err } fmt.Printf( "Shopping list summary %v\n", ingredients, ) } func dispatchGopher( ctx context.Context, ingredient string, mobile chan<- SMS, ) { gopher := NewGopher(mobile, ingredient) gopher.Buy(ctx) }
  22. SHOPPING TIME (6) THE CONTEXT OF GO func main() {

    ingredients := map[string]error{ … } mobile := make(chan SMS) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() for ingredient := range ingredients { go dispatchGopher(ctx, ingredient, mobile) } for i := 0; i < len(ingredients); i++ { sms := <-mobile if sms.Err != nil { cancel() } ingredients[sms.Ingredient] = sms.Err } fmt.Printf( "Shopping list summary %v\n", ingredients, ) } func dispatchGopher( ctx context.Context, ingredient string, mobile chan<- SMS, ) { gopher := NewGopher(mobile, ingredient) gopher.Buy(ctx) }
  23. REAL-WORLD EXAMPLES THE CONTEXT OF GO func search(w http.ResponseWriter, r

    *http.Request) { sites := []string{"/images", "/videos", "/songs", "/shopping"} responses := make(chan *http.Response) ctx, cancel := context.WithCancel(context.Background()) for _, site := range sites { go func() { client := &http.Client{ /* Initialization here */ } request, _ := http.NewRequestWithContext(ctx, "GET", site, nil) response, _ := client.Do(request) responses <- response }() } for i := 0; i < len(sites); i++ { response := <-responses /* Do whatever with response (i.e. append) */ if response.StatusCode == http.StatusInternalServerError { cancel() } } } handler /videos /images /songs
  24. REAL-WORLD EXAMPLES (2) THE CONTEXT OF GO func main() {

    ctx, cancel := context.WithCancel(context.Background()) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { oscall := <-c log.Printf("system call:%+v", oscall) cancel() }() mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }, )) srv := &http.Server{ Addr: ":8000", Handler: mux, } if err := serve(ctx, srv); err != nil { log.Printf("failed to serve:+%v\n", err) } HTTP
 SERVER DB MB ASYNC 1. Interrupt signal arrives 2. Cancel main context 3. Start a new context with timeout (5s) - Hey man, you have 5 seconds to do
 

 the graceful shutdown.
  25. REAL-WORLD EXAMPLES (3) THE CONTEXT OF GO func serve(ctx context.Context,

    srv *http.Server) (err error) { go func() { if err = srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("unexpected error while serving the HTTP service: %s\n", err) } }() log.Printf("HTTP service started") <-ctx.Done() log.Printf("HTTP service stopped") ctxShutDown, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err = srv.Shutdown(ctxShutDown); err != nil { log.Fatalf("HTTP service shutdown failed: %s", err) return } log.Printf("HTTP service exited properly") return }
  26. REAL-WORLD EXAMPLES (4) THE CONTEXT OF GO “At Google, we

    require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests. This allows Go code developed by many different teams to interoperate well. It provides simple control over timeouts and cancelation and ensures that critical values like security credentials transit Go programs properly.” https://blog.golang.org/context
  27. REAL-WORLD EXAMPLES (5) THE CONTEXT OF GO 
 (by @fiunchinho)


    
 - https://github.com/kubernetes/enhancements/blob/master/keps/sig-api- machinery/20200123-client-go-ctx.md#client-signatures-after-refactor - https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/ CHANGELOG-1.18.md#client-go - https://github.com/kubernetes-sigs/clientgofix
  28. THE CONTEXT PACKAGE (11) THE CONTEXT OF GO // A

    Context carries a deadline, cancelation signal, and request-scoped values // across API boundaries. Its methods are safe for simultaneous use by multiple // goroutines. type Context interface { ... // Value returns the value associated with key or nil if none. Value(key interface{}) interface{} }
  29. THE CONTEXT PACKAGE (13) THE CONTEXT OF GO ctx :=

    context.Background() child1 := context.WithValue(ctx, "title", "The Context of Go") CTX CHILD1
 
 “title”: “…"
  30. THE CONTEXT PACKAGE (14) THE CONTEXT OF GO ctx :=

    context.Background() child1 := context.WithValue(ctx, "title", "The Context of Go") child11 := context.WithValue(ctx, "author", "Joan") CTX CHILD1
 
 “title”: “…" CHILD21
 
 “author”: “…"

  31. THE CONTEXT PACKAGE (15) THE CONTEXT OF GO ctx :=

    context.Background() child1 := context.WithValue(ctx, "title", "The Context of Go") child11 := context.WithValue(ctx, "author", "Joan") child11.Value("title") // "The Context of Go" child11.Value("author") // "Joan" CTX CHILD1
 
 “title”: “…" CHILD21
 
 “author”: “…"

  32. THE CONTEXT PACKAGE (16) THE CONTEXT OF GO ctx :=

    context.Background() child1 := context.WithValue(ctx, "title", "The Context of Go") child11 := context.WithValue(ctx, "author", "Joan") child11.Value("title") // "The Context of Go" child11.Value("author") // "Joan" child1.Value("author") // nil CTX CHILD1
 
 “title”: “…" CHILD21
 
 “author”: “…"

  33. THE CONTEXT PACKAGE (17) THE CONTEXT OF GO Some usages:


    
 Logging, Tracing, Metrics, And so on…
  34. RECAP: TIPS & TRICKS THE CONTEXT OF GO ‣ Do

    not use nil contexts, use context.TODO() instead
  35. RECAP: TIPS & TRICKS THE CONTEXT OF GO ‣ Do

    not use nil contexts, use context.TODO() instead ‣ Remember to defer cancellations to help the GC
  36. RECAP: TIPS & TRICKS THE CONTEXT OF GO ‣ Do

    not use nil contexts, use context.TODO() instead ‣ Remember to defer cancellations to help the GC ‣ Do not store the context in a struct (except http.Request)
  37. RECAP: TIPS & TRICKS THE CONTEXT OF GO ‣ Do

    not use nil contexts, use context.TODO() instead ‣ Remember to defer cancellations to help the GC ‣ Do not store the context in a struct (except http.Request) ‣ Do not use contexts as dependency containers
  38. RECAP: TIPS & TRICKS THE CONTEXT OF GO ‣ Do

    not use nil contexts, use context.TODO() instead ‣ Remember to defer cancellations to help the GC ‣ Do not store the context in a struct (except http.Request) ‣ Do not use contexts as dependency containers ‣ Only put values that don’t control the flow of execution
  39. THANK YOU SO MUCH! THE CONTEXT OF GO Some references:


    
 https://golang.org/ https://blog.golang.org/context https://friendsofgo.tech/ https://blog.friendsofgo.tech/posts/context-en-golang/ https://www.youtube.com/watch?v=LSzR0VEraWw https://www.youtube.com/watch?v=-_B5uQ4UGi0 https://pixabay.com/ https://emojipedia.org/