Adding context to existing code

Adding context to existing code

4618c5e97c59abd315cc2d7dc809f8c8?s=128

Alexey Palazhchenko

March 24, 2019
Tweet

Transcript

  1. Adding context to existing code Alexey Palazhchenko

  2. Existing code QueryRow(string, ...interface{}) *Row Query(string, ...interface{}) (*Rows, error) Exec(string,

    ...interface{}) (Result, error)
  3. Solution 1: «just» add ctx QueryRow(string, ...interface{}) *Row QueryRow(context.Context, string,

    ...interface{}) *Row Query(string, ...interface{}) (*Rows, error) Query(context.Context, string, ...interface{}) (*Rows, error) Exec(string, ...interface{}) (Result, error) Exec(context.Context, string, ...interface{}) (Result, error)
  4. Solution 1 problems • Internal code – large-scale refactoring.

  5. Solution 1 problems • Internal code – large-scale refactoring. •

    Use context.TODO().
  6. Solution 1 problems • Internal code – large-scale refactoring. •

    Use context.TODO(). • «TODO is recognized by static analysis tools that determine whether Contexts are propagated correctly in a program.»
  7. Solution 1 problems • Internal code – large-scale refactoring. •

    Use context.TODO(). • «TODO is recognized by static analysis tools that determine whether Contexts are propagated correctly in a program.» • This tool does not exist.
  8. Solution 1 problems • External code – large-scale refactoring for

    users.
  9. Solution 1 problems • External code – large-scale refactoring for

    users. • v0.m -> v0.m+1
  10. Solution 1 problems • External code – large-scale refactoring for

    users. • v0.m -> v0.m+1 • vM -> vM+1
  11. Solution 2x QueryRow(string, ...interface{}) *Row QueryRowContext(context.Context, string, ...interface{}) *Row Query(string,

    ...interface{}) (*Rows, error) QueryContext(context.Context, string, ...interface{}) (*Rows, error) Exec(string, ...interface{}) (Result, error) ExecContext(context.Context, string, ...interface{}) (Result, error)
  12. Solution 2x Delete(record Record) error DeleteFrom(view View, tail string, args

    ...interface{}) (uint, error) Exec(query string, args ...interface{}) (sql.Result, error) FindAllFrom(view View, column string, args ...interface{}) ([]Struct, error) FindByPrimaryKeyFrom(table Table, pk interface{}) (Record, error) FindByPrimaryKeyTo(record Record, pk interface{}) error FindOneFrom(view View, column string, arg interface{}) (Struct, error) FindOneTo(str Struct, column string, arg interface{}) error FindRows(view View, column string, arg interface{}) (*sql.Rows, error) Insert(str Struct) error InsertColumns(str Struct, columns ...string) error InsertMulti(structs ...Struct) error NextRow(str Struct, rows *sql.Rows) error QualifiedColumns(view View) []string QualifiedView(view View) string Query(query string, args ...interface{}) (*sql.Rows, error) QueryRow(query string, args ...interface{}) *sql.Row Reload(record Record) error Save(record Record) error SelectAllFrom(view View, tail string, args ...interface{}) (structs []Struct, err error) SelectOneFrom(view View, tail string, args ...interface{}) (Struct, error) SelectOneTo(str Struct, tail string, args ...interface{}) error SelectRows(view View, tail string, args ...interface{}) (*sql.Rows, error) Update(record Record) error UpdateColumns(record Record, columns ...string) error UpdateView(str Struct, columns []string, tail string, args ...interface{}) (uint, error)
  13. Solution 3 type Querier struct { Context context.Context } func

    (q *Querier) QueryRow(query string, args ...interface{}) *sql.Row
  14. https://golang.org/pkg/context/ «Do not store Contexts inside a struct type; instead,

    pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx.» Solution 3 «problem»
  15. https://github.com/golang/go/issues/14660 «While we've told people not to add contexts to

    structs, I think that guidance is over-aggressive. The real advice is not to store contexts. They should be passed along like parameters. But if the struct is essentially just a parameter, it's okay.» Solution 3
  16. https://github.com/golang/go/issues/22602 context: relax recommendation against putting Contexts in structs Solution

    3
  17. Solution 3b type Querier struct { ctx context.Context } func

    (q *Querier) WithContext(ctx context.Context) *Querier func (q *Querier) QueryRow(query string, args ...interface{}) *sql.Row
  18. Conclusion • "Just" add ctx to internal code.

  19. Conclusion • "Just" add ctx to internal code. • Consider

    your options for the external code.
  20. Conclusion • "Just" add ctx to internal code. • Consider

    your options for the external code. • Know best practices beyond their short versions.