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

Зачем и как написать свой database/sql драйвер

Зачем и как написать свой database/sql драйвер

Iskander (Alex) Sharipov

February 16, 2019
Tweet

More Decks by Iskander (Alex) Sharipov

Other Decks in Programming

Transcript

  1. View Slide

  2. Зачем и как написать свой
    database/sql драйвер
    Алексей Палажченко

    Percona

    View Slide

  3. Зачем?
    • Написание нового драйвера для SQL СУБД

    • или исправление существующего

    • Написание «наддрайвера»

    • для observability и отладки

    • Написание драйвера для NoSQL СУБД

    • или даже не для СУБД

    View Slide

  4. Зачем?
    • Написание нового драйвера для SQL СУБД

    • https://github.com/AlekSi/mysqlx

    • Написание «наддрайвера»

    • https://github.com/luna-duclos/instrumentedsql

    • Написание драйвера для NoSQL СУБД

    • https://github.com/AlekSi/sqlcsv

    View Slide

  5. Как?

    View Slide

  6. Как?

    View Slide

  7. • func IsScanValue(v interface{}) bool

    • func IsValue(v interface{}) bool

    • type ColumnConverter

    • type Conn

    • type Driver

    • type Execer

    • type NotNull

    • func (n NotNull) ConvertValue(v interface{}) (Value, error)

    • type Null

    • func (n Null) ConvertValue(v interface{}) (Value, error)

    • type Result

    • type Rows

    • type RowsAffected

    • func (RowsAffected) LastInsertId() (int64, error)

    • func (v RowsAffected) RowsAffected() (int64, error)

    • type Stmt

    • type Tx

    • type Value

    • type ValueConverter

    • type Valuer

    View Slide

  8. • func IsScanValue(v interface{}) bool

    • func IsValue(v interface{}) bool

    • type ColumnConverter

    • type Conn

    • type ConnBeginTx

    • type ConnPrepareContext

    • type Connector

    • type Driver

    • type DriverContext

    • type Execer

    • type ExecerContext

    • type IsolationLevel

    • type NamedValue

    • type NamedValueChecker

    • type NotNull

    • func (n NotNull) ConvertValue(v interface{}) (Value, error)

    • type Null

    • func (n Null) ConvertValue(v interface{}) (Value, error)

    • type Pinger

    • type Queryer

    • type QueryerContext
    • type Result

    • type Rows

    • type RowsAffected

    • func (RowsAffected) LastInsertId() (int64, error)

    • func (v RowsAffected) RowsAffected() (int64, error)

    • type RowsColumnTypeDatabaseTypeName

    • type RowsColumnTypeLength

    • type RowsColumnTypeNullable

    • type RowsColumnTypePrecisionScale

    • type RowsColumnTypeScanType

    • type RowsNextResultSet

    • type SessionResetter

    • type Stmt

    • type StmtExecContext

    • type StmtQueryContext

    • type Tx

    • type TxOptions

    • type Value

    • type ValueConverter

    • type Valuer

    View Slide

  9. 3 источника знаний
    • Код database/sql и database/sql/driver

    • Код существующих драйверов

    • но полно плохого и старого

    • Кровь, пот и падающий прод

    View Slide

  10. Driver
    // Database drivers

    // may implement DriverContext […]

    type Driver interface {

    Open(name string) (Conn, error)

    }

    View Slide

  11. Driver
    type driver struct{}

    View Slide

  12. Conn
    type Conn interface {

    Prepare(query string) (Stmt, error)

    Close() error


    // Deprecated: Drivers should

    // implement ConnBeginTx instead

    // (or additionally).

    Begin() (Tx, error)

    }

    View Slide

  13. Conn
    type conn struct {

    transport net.Conn

    }

    View Slide

  14. Stmt
    type Stmt interface {

    Close() error


    // NumInput may also return -1 […]

    NumInput() int


    // Deprecated: Drivers should implement

    // StmtExecContext instead (or additionally).

    Exec(args []Value) (Result, error)

    // Deprecated: Drivers should implement

    // StmtQueryContext instead (or additionally).

    Query(args []Value) (Rows, error)

    }

    View Slide

  15. Stmt
    type stmt struct {

    conn *conn

    query string

    }

    View Slide

  16. Value
    Value is a value that drivers must be able to handle.
    It is either nil, a type handled by a database
    driver's NamedValueChecker interface, or an instance
    of one of these types:

    int64

    float64

    bool

    []byte

    string

    time.Time
    type Value interface{}

    View Slide

  17. Result
    type Result interface {

    LastInsertId() (int64, error)

    RowsAffected() (int64, error)

    }

    View Slide

  18. Rows
    type Rows interface {

    Columns() []string

    Close() error

    Next(dest []Value) error

    }

    View Slide

  19. ErrBadConn
    ErrBadConn should be returned by a driver to
    signal to the sql package that a driver.Conn
    is in a bad state […] and the sql package
    should retry on a new connection.
    var ErrBadConn = errors.New("[…]")

    View Slide

  20. Rows
    type rows struct {

    conn *conn

    // […]

    }

    View Slide

  21. Минимум
    • driver.Open(name string) (Conn, error)
    • conn.Prepare(query string) (Stmt, error)
    • stmt.Exec(args []Value) (Result, error)
    • stmt.Query(args []Value) (Rows, error)
    • rows.Next(dest []Value) error

    View Slide

  22. QueryerContext
    If a Conn does not implement QueryerContext
    […] DB.Query will first prepare a query,
    execute the statement, and then close the
    statement.
    QueryerContext may return ErrSkip.
    type QueryerContext interface {

    QueryContext(context.Context, string,

    []NamedValue) (Rows, error)

    }

    View Slide

  23. ErrSkip
    ErrSkip may be returned by some optional
    interfaces' methods to indicate at runtime
    that the fast path is unavailable and the sql
    package should continue as if the optional
    interface was not implemented. ErrSkip is
    only supported where explicitly documented.
    var ErrSkip = errors.New("[…]")

    View Slide

  24. NamedValue
    type NamedValue struct {

    Name string

    Ordinal int

    Value Value

    }

    View Slide

  25. NamedValueChecker
    type NamedValueChecker interface {

    CheckNamedValue(*NamedValue) error

    }

    View Slide

  26. Valuer
    type Valuer interface {

    Value() (Value, error)

    }

    View Slide

  27. Valuer и Scanner
    type JSON interface{}
    func (j JSON) Value() (driver.Value, error)

    func (j *JSON) Scan(value interface{}) error
    var _ driver.Valuer = JSON{}

    var _ sql.Scanner = &JSON{}

    View Slide

  28. Connector
    type Connector interface {

    Connect(context.Context) (Conn, error)

    Driver() Driver

    }

    View Slide

  29. Connector
    type Config struct {

    tlsConfig *tls.Config

    // […]

    }
    func (c *Config) Connect(ctx) (Conn, error)

    View Slide

  30. SessionResetter
    type SessionResetter interface {

    ResetSession(ctx context.Context) error

    }

    View Slide

  31. SessionResetter
    db.SetMaxIdleConns(1)

    db.SetMaxOpenConns(1)

    db.SetConnMaxLifetime(0)

    View Slide

  32. Наддрайверы

    View Slide

  33. Зачем наддрайверы?
    • Логирование

    • Метрики

    • Профилирование

    • Трейсинг

    • Отладка

    View Slide

  34. Опциональные
    интерфейсы
    func (c wrappedConn) Query(query string,

    args []driver.Value)

    (driver.Rows, error) {


    queryer, ok := c.parent.(driver.Queryer)

    if ok {

    return queryer.Query(query, args)

    }

    // ???

    View Slide

  35. Опциональные
    интерфейсы
    func (c wrappedConn) Query(query string,

    args []driver.Value)

    (driver.Rows, error) {


    queryer, ok := c.parent.(driver.Queryer)

    if ok {

    return queryer.Query(query, args)

    }

    return nil, driver.ErrSkip

    View Slide

  36. Опциональные
    интерфейсы
    • reflect

    • слишком много багов :(

    • Кодогенерация

    • только для конкретного драйвера

    • Реализация как в database/sql

    • SessionResetter – возвращаем ErrBadConn

    View Slide

  37. Context
    // Deprecated: Drivers should implement
    ExecerContext instead.

    type Execer interface {

    Exec(string, []Value) (Result, error)

    }
    type ExecerContext interface {

    ExecContext(ctx, string, []NamedValue)

    (Result, error)

    }

    View Slide

  38. Самый главный
    совет

    View Slide

  39. Тестируйте
    • На реальных системах, с минимумом моков

    • На всех комбинациях Go и версий систем

    • Docker Compose отлично для этого подходит

    View Slide

  40. Спасибо!
    Вопросы?

    View Slide

  41. GopherCon Russia 2019
    13 апреля, Москва
    gophercon-russia.ru

    View Slide