Contexts in Context

Contexts in Context

The context type allows deadlines, cancelation signals, and other request-scoped values, to propagate through request chains and across API boundaries, but in practice how can these features be used? In this talk Matt will talk about how Monzo use the humble context to power a variety of features in their Go based microservice platform.

67f4a8f2a209a38d7242829947b26ba3?s=128

mattheath

April 17, 2019
Tweet

Transcript

  1. 5.
  2. 6.
  3. 7.
  4. 8.
  5. 9.
  6. 10.
  7. 16.
  8. 17.
  9. 18.
  10. 19.
  11. 20.
  12. 21.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  13. 22.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  14. 23.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  15. 24.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  16. 25.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  17. 26.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  18. 37.
  19. 42.

    package context type Context interface { Deadline() (deadline time.Time, ok

    bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
  20. 43.
  21. 44.
  22. 45.
  23. 46.
  24. 47.

    LICENCE WITH RESTRICTIONS WE ARE HERE A UK banking licence

    is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! FEB 2015 JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY JUN JUL MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC PRE APPLICATION APPLICATION MOBILISATION LAUNCH LICENCE WITH RESTRICTIONS 50K MAX DEPOSIT WE ARE HERE A UK banking licence is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY JUN JUL MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC E APPLICATION APPLICATION MOBILISATION LAUNCH LICENCE WITH RESTRICTIONS WE ARE HERE AUG
 2016 JAN
 2016 Feb
 2015 APR
 2017 A UK banking licence is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! FEB 2015 JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC PRE APPLICATION APPLICATION MOBILISATION LICENCE WITH RESTRICTIONS WE ARE HERE
  25. 48.

    LICENCE WITH RESTRICTIONS WE ARE HERE A UK banking licence

    is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! FEB 2015 JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY JUN JUL MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC PRE APPLICATION APPLICATION MOBILISATION LAUNCH LICENCE WITH RESTRICTIONS 50K MAX DEPOSIT WE ARE HERE A UK banking licence is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY JUN JUL MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC E APPLICATION APPLICATION MOBILISATION LAUNCH LICENCE WITH RESTRICTIONS WE ARE HERE AUG
 2016 JAN
 2016 Feb
 2015 APR
 2017 A UK banking licence is authorised by the PRA and regulated by the PRA and FCA1, allowing deposit- taking and balance sheet lending. Once granted, it allows firms to passport across Europe, accessing This is followed by a “mobilisation” phase during which final capital is raised and IT systems are completed, before launching to the public. We received a UK banking licence in August 2016! FEB 2015 JAN 2016 JAN 2017 MAR FEB FEB APR MAR MAR APR MAY APR JUN MAY JUL JUN AUG JUL SEP AUG NOV SEP DEC NOV DEC PRE APPLICATION APPLICATION MOBILISATION LICENCE WITH RESTRICTIONS WE ARE HERE 2 YEARS AGO!!
  26. 49.
  27. 50.

    ?

  28. 51.

  29. 52.

  30. 53.

  31. 57.
  32. 58.
  33. 60.
  34. 61.
  35. 62.
  36. 63.
  37. 64.
  38. 65.
  39. 66.
  40. 67.
  41. 68.

    package slog
 
 func Error(ctx context.Context, msg string, params ...interface{})

    { if l := DefaultLogger(); l != nil { l.Log(Eventf(DebugSeverity, ctx, msg, params...)) } }
  42. 69.

    package slog
 
 func Error(ctx context.Context, msg string, params ...interface{})

    { if l := DefaultLogger(); l != nil { l.Log(Eventf(DebugSeverity, ctx, msg, params...)) } }
  43. 72.

    2019-04-17 15:38:09.134539276 [DBG] Slog -> Auth request: 
 {"auth_type": "Auth",

    "merchant": {"merchant_description": "Deliveroo! LONDON UK", "merchant_id": "XXXX"}, "network": “Mastercard" ...}
 {opentracing_id=2855967a9de91afb, trace_id=19ce030f-f652-4b4d-5f35-5d75f472f482, service=service.blah, service_version=d7b742fe5c03998748348fe9d7d81a6b8c7358d, hostname=s- blah-79f9b54887-j62d2}
 
 (handle_auth_message.go:25 handleAuthMessage)
  44. 73.

    2019-04-17 15:38:09.134539276 [DBG] Slog -> Auth request: 
 {"auth_type": "Auth",

    "merchant": {"merchant_description": “Deliveroo! LONDON UK", "merchant_id": "XXXX"}, "network": “Mastercard" ...}
 {opentracing_id=2855967a9de91afb, trace_id=19ce030f-f652-4b4d-5f35-5d75f472f482, service=service.blah, service_version=d7b742fe5c03998748348fe9d7d81a6b8c7358d, hostname=s- blah-79f9b54887-j62d2}
 
 (handle_auth_message.go:25 handleAuthMessage)
  45. 74.

    API

  46. 75.

    API Service API Service API Service API Service API Service

    API Service API Service API Service Edge Gateway
  47. 82.

    // Generate new ID // Store value in context tid,

    err := NewTraceID() // v4 UUID if err == nil { req.Context = context.WithValue(req.Context, "Trace-Id", tid) }
  48. 83.

    // Generate new ID // Store value in context tid,

    err := NewTraceID() // v4 UUID if err == nil { req.Context = context.WithValue(req.Context, "Trace-Id", tid) }
  49. 84.

    // Generate new ID // Store value in context tid,

    err := NewTraceID() // v4 UUID if err == nil { req.Context = context.WithValue(req.Context, "Trace-Id", tid) }
  50. 86.

    Service A Service B Load Balancer Edge Gateway Request ID:

    19ce030f-f652-4b4d-5f35-5d75f472f482 Request ID: 19ce030f-f652-4b4d-5f35-5d75f472f482 Request ID: 19ce030f-f652-4b4d-5f35-5d75f472f482 API Service
  51. 89.

    // Add headers previously stashed in the context if stash,

    ok := req.Context.Value(ctxKey).(http.Header); ok && stash != nil { for k, v := range stash { if req.Header[k] == nil { req.Header[k] = v } } } // Stash headers in the context stash := http.Header{} for k, v := range req.Header { for _, r := range passedHeaders { if r.MatchString(k) { stash[k] = v } } } if len(stash) > 0 { req.Context = context.WithValue(req.Context, ctxKey, stash) }
  52. 90.

    // Add headers previously stashed in the context if stash,

    ok := req.Context.Value(ctxKey).(http.Header); ok && stash != nil { for k, v := range stash { if req.Header[k] == nil { req.Header[k] = v } } } // Stash headers in the context stash := http.Header{} for k, v := range req.Header { for _, r := range passedHeaders { if r.MatchString(k) { stash[k] = v } } } if len(stash) > 0 { req.Context = context.WithValue(req.Context, ctxKey, stash) }
  53. 91.

    // Add headers previously stashed in the context if stash,

    ok := req.Context.Value(ctxKey).(http.Header); ok && stash != nil { for k, v := range stash { if req.Header[k] == nil { req.Header[k] = v } } } // Stash headers in the context stash := http.Header{} for k, v := range req.Header { for _, r := range passedHeaders { if r.MatchString(k) { stash[k] = v } } } if len(stash) > 0 { req.Context = context.WithValue(req.Context, ctxKey, stash) }
  54. 92.

    // Add headers previously stashed in the context if stash,

    ok := req.Context.Value(ctxKey).(http.Header); ok && stash != nil { for k, v := range stash { if req.Header[k] == nil { req.Header[k] = v } } } // Stash headers in the context stash := http.Header{} for k, v := range req.Header { for _, r := range passedHeaders { if r.MatchString(k) { stash[k] = v } } } if len(stash) > 0 { req.Context = context.WithValue(req.Context, ctxKey, stash) }
  55. 96.
  56. 97.
  57. 98.
  58. 101.
  59. 102.
  60. 111.

    rspCh := make(chan Response, 1) go dispatchRequest(ctx, rspCh) select {

    case rsp := <-rspCh: return rsp case <-ctx.Done(): return ctx.Error() }
  61. 112.

    rspCh := make(chan Response, 1) go dispatchRequest(ctx, rspCh) select {

    case rsp := <-rspCh: return rsp case <-ctx.Done(): return ctx.Error() }
  62. 113.

    rspCh := make(chan Response, 1) go dispatchRequest(ctx, rspCh) select {

    case rsp := <-rspCh: return rsp case <-ctx.Done(): return ctx.Error() }
  63. 114.

    // check if the context is cancelled! 
 select {

    case <-ctx.Done(): return ctx.Error() default: } wooooooo… let’s go!
  64. 116.

    Cancel outstanding work! Decrease tail latencies! Reduce load on your

    database!
 
 Unless operations should complete…
  65. 119.

    Service A Service B Load Balancer Edge Gateway API Service

    Client disconnects Cascading failure!! Errors everywhere!! Intense panic!
  66. 124.