Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Зачем?

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Зачем? • Написание нового драйвера для SQL СУБД • или исправление существующего • Написание «наддрайвера»

Slide 6

Slide 6 text

Зачем? • Написание нового драйвера для SQL СУБД • или исправление существующего • Написание «наддрайвера» • для observability и отладки

Slide 7

Slide 7 text

Зачем? • Написание нового драйвера для SQL СУБД • или исправление существующего • Написание «наддрайвера» • для observability и отладки • Написание драйвера для NoSQL СУБД

Slide 8

Slide 8 text

Зачем? • Написание нового драйвера для SQL СУБД • или исправление существующего • Написание «наддрайвера» • для observability и отладки • Написание драйвера для NoSQL СУБД • или даже не для СУБД

Slide 9

Slide 9 text

Зачем? • Написание нового драйвера для SQL СУБД • https://github.com/AlekSi/mysqlx • Написание «наддрайвера» • https://github.com/luna-duclos/instrumentedsql • Написание драйвера для NoSQL СУБД • https://github.com/AlekSi/sqlcsv

Slide 10

Slide 10 text

Как?

Slide 11

Slide 11 text

Как?

Slide 12

Slide 12 text

• 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

Slide 13

Slide 13 text

• 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

Slide 14

Slide 14 text

3 источника знаний

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

3 источника знаний • Код database/sql и database/sql/driver • Код существующих драйверов

Slide 17

Slide 17 text

3 источника знаний • Код database/sql и database/sql/driver • Код существующих драйверов • но полно плохого и старого

Slide 18

Slide 18 text

3 источника знаний • Код database/sql и database/sql/driver • Код существующих драйверов • но полно плохого и старого • Кровь, пот и падающий прод

Slide 19

Slide 19 text

Driver // Database drivers
 // may implement DriverContext […]
 type Driver interface {
 Open(name string) (Conn, error)
 }

Slide 20

Slide 20 text

Driver type driver struct{}

Slide 21

Slide 21 text

Conn type Conn interface {
 Prepare(query string) (Stmt, error)
 Close() error
 
 // Deprecated: Drivers should
 // implement ConnBeginTx instead
 // (or additionally).
 Begin() (Tx, error)
 }

Slide 22

Slide 22 text

Conn type conn struct {
 transport net.Conn
 }

Slide 23

Slide 23 text

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)
 }

Slide 24

Slide 24 text

Stmt type stmt struct {
 conn *conn
 query string
 }

Slide 25

Slide 25 text

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{}

Slide 26

Slide 26 text

Result type Result interface {
 LastInsertId() (int64, error)
 RowsAffected() (int64, error)
 }

Slide 27

Slide 27 text

Rows type Rows interface {
 Columns() []string
 Close() error
 Next(dest []Value) error
 }

Slide 28

Slide 28 text

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("[…]")

Slide 29

Slide 29 text

Rows type rows struct {
 conn *conn
 // […]
 }

Slide 30

Slide 30 text

Минимум • 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

Slide 31

Slide 31 text

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)
 }

Slide 32

Slide 32 text

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("[…]")

Slide 33

Slide 33 text

NamedValue type NamedValue struct {
 Name string
 Ordinal int
 Value Value
 }

Slide 34

Slide 34 text

NamedValue type NamedValue struct {
 Name string
 Ordinal int
 Value interface{}
 }

Slide 35

Slide 35 text

NamedValueChecker type NamedValueChecker interface {
 CheckNamedValue(*NamedValue) error
 }

Slide 36

Slide 36 text

Valuer type Valuer interface {
 Value() (Value, error)
 }

Slide 37

Slide 37 text

Valuer и Scanner type JSON []byte func (j JSON) Value() (driver.Value, error)
 func (j *JSON) Scan(value interface{}) error var _ driver.Valuer = JSON{}
 var _ sql.Scanner = &JSON{}

Slide 38

Slide 38 text

Connector type Connector interface {
 Connect(context.Context) (Conn, error)
 Driver() Driver
 }

Slide 39

Slide 39 text

Connector type Config struct {
 tlsConfig *tls.Config
 // […]
 } func (c *Config) Connect(ctx) (Conn, error)

Slide 40

Slide 40 text

SessionResetter type SessionResetter interface {
 ResetSession(ctx context.Context) error
 }

Slide 41

Slide 41 text

SessionResetter db.SetMaxIdleConns(1)
 db.SetMaxOpenConns(1)
 db.SetConnMaxLifetime(0)

Slide 42

Slide 42 text

Наддрайверы

Slide 43

Slide 43 text

Зачем наддрайверы?

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Зачем наддрайверы? • Логирование • Метрики

Slide 46

Slide 46 text

Зачем наддрайверы? • Логирование • Метрики • Профилирование

Slide 47

Slide 47 text

Зачем наддрайверы? • Логирование • Метрики • Профилирование • Трейсинг

Slide 48

Slide 48 text

Зачем наддрайверы? • Логирование • Метрики • Профилирование • Трейсинг • Отладка

Slide 49

Slide 49 text

Опциональные интерфейсы 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)
 }
 // ???

Slide 50

Slide 50 text

Опциональные интерфейсы 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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

Опциональные интерфейсы • reflect • Кодогенерация

Slide 54

Slide 54 text

Опциональные интерфейсы • reflect • Кодогенерация • Реализация как в database/sql

Slide 55

Slide 55 text

Context // Deprecated: Drivers should implement ExecerContext instead.
 type Execer interface {
 Exec(string, []Value) (Result, error)
 } type ExecerContext interface {
 ExecContext(ctx, string, []NamedValue)
 (Result, error)
 }

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Тестируйте

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Тестируйте • На реальных системах, с минимумом моков • На всех комбинациях Go и версий систем

Slide 60

Slide 60 text

Тестируйте • На реальных системах, с минимумом моков • На всех комбинациях Go и версий систем • Docker Compose отлично для этого подходит

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Спасибо спонсорам

Slide 63

Slide 63 text

Работа в Percona Писать Open Source за деньги https://www.percona.com
 /about-percona
 /careers
 /golang-software-engineer Go Software Engineer
 Distributed / remote
 $2500-$4000

Slide 64

Slide 64 text

GopherCon Russia 2019 13 апреля, Москва • Приём заявок на доклады открыт! • Первые билеты по цене прошлого года уже в продаже! https://www.gophercon-russia.ru