lectures: 07-sql rc1

This commit is contained in:
Ilya Sinelnikov 2020-04-09 16:00:41 +03:00
parent f7fa86ecc3
commit 77a024a6bc
11 changed files with 291 additions and 21 deletions

3
go.mod
View file

@ -3,6 +3,9 @@ module gitlab.com/slon/shad-go
go 1.13 go 1.13
require ( require (
github.com/ClickHouse/clickhouse-go v1.4.0 // indirect
github.com/DATA-DOG/go-sqlmock v1.4.1
github.com/go-redis/redis v6.15.7+incompatible // indirect
github.com/go-resty/resty/v2 v2.1.0 github.com/go-resty/resty/v2 v2.1.0
github.com/gofrs/uuid v3.2.0+incompatible github.com/gofrs/uuid v3.2.0+incompatible
github.com/golang/mock v1.4.1 github.com/golang/mock v1.4.1

9
go.sum
View file

@ -1,10 +1,16 @@
cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.0.0-20170206221025-ce650573d812/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ClickHouse/clickhouse-go v1.4.0 h1:cC1DEZ1TL74QviZY4svlwow84X5r7/BGd78kf18swhI=
github.com/ClickHouse/clickhouse-go v1.4.0/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20190129172621-c8b1d7a94ddf/go.mod h1:aJ4qN3TfrelA6NZ6AXsXRfmEVaYin3EDbSPJrKS8OXo=
github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes= github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vcpkIuHowELi5C8e+1yUHtoLoOUR9QU5j7Tes=
github.com/aclements/go-moremath v0.0.0-20161014184102-0ff62e0875ff/go.mod h1:idZL3yvz4kzx1dsBOAC+oYv6L92P1oFEhUXUB1A/lwQ= github.com/aclements/go-moremath v0.0.0-20161014184102-0ff62e0875ff/go.mod h1:idZL3yvz4kzx1dsBOAC+oYv6L92P1oFEhUXUB1A/lwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@ -17,6 +23,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE= github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE=
github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -113,6 +121,7 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

View file

@ -0,0 +1,29 @@
package clickhouse
import (
"context"
"database/sql"
_ "github.com/ClickHouse/clickhouse-go"
)
func Example(ctx context.Context) {
db, _ := sql.Open("clickhouse", "tcp://127.0.0.1:9000?debug=true")
defer db.Close()
// Начало батча
tx, _ := db.BeginTx(ctx, nil)
defer tx.Rollback()
// Описание батча
stmt, _ := tx.PrepareContext(ctx, "INSERT INTO example (id) VALUES (?)")
defer stmt.Close()
// Добавление данных
for i := 0; i < 100; i++ {
_, _ = stmt.ExecContext(ctx, i)
}
// Отправка батча в ClickHouse
_ = tx.Commit()
}

View file

@ -7,7 +7,7 @@ import (
) )
func Query(ctx context.Context, db *sql.DB) { func Query(ctx context.Context, db *sql.DB) {
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users WHERE id = $1", 1) rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -0,0 +1,31 @@
package redis
import (
"log"
"time"
"github.com/go-redis/redis"
)
func Example() {
rdb := redis.NewUniversalClient(&redis.UniversalOptions{
MasterName: "master",
Addrs: []string{":26379"},
})
defer rdb.Close()
if err := rdb.Ping(); err != nil {
log.Fatal(err)
}
if err := rdb.Set("key", "value", time.Hour).Err(); err != nil {
log.Fatal(err)
}
value, err := rdb.Get("key").Result()
if err != nil {
log.Fatal(err)
}
log.Println(value)
}

View file

@ -0,0 +1,16 @@
package resources
import (
"context"
"database/sql"
"log"
)
func ConnExhaust(ctx context.Context, db *sql.DB) {
c, err := db.Conn(ctx)
if err != nil {
log.Fatal(err)
}
_ = c.PingContext(ctx)
}

View file

@ -0,0 +1,72 @@
package resources
import (
"context"
"database/sql"
"log"
)
func QueryDeadlock(ctx context.Context, db *sql.DB) {
rows, _ := db.QueryContext(ctx, "SELECT id, name FROM users")
defer rows.Close()
for rows.Next() {
var id int
var name string
_ = rows.Scan(&id, &name)
rowsAddrs, _ := db.QueryContext(
ctx,
"SELECT address FROM addresses WHERE user_id = $1",
id,
)
defer rowsAddrs.Close()
var addr string
_ = rowsAddrs.Scan(&addr)
log.Println(id, name, addr)
}
}
func QueryDeadlockFixOne(ctx context.Context, db *sql.DB) {
type Res struct {
ID int
Name string
Addr string
}
var values []Res
rows, _ := db.QueryContext(ctx, "SELECT id, name FROM users")
for rows.Next() {
var res Res
_ = rows.Scan(&res.ID, &res.Name)
values = append(values, res)
}
rows.Close()
for _, v := range values {
_ = db.QueryRowContext(
ctx,
"SELECT address FROM addresses WHERE user_id = $1",
v.ID,
).Scan(&v.Addr)
log.Println(v)
}
}
func QueryDeadlockFixTwo(ctx context.Context, db *sql.DB) {
rows, _ := db.QueryContext(
ctx,
"SELECT u.id, u.name, a.address FROM users AS u, addresses as a WHERE u.id == a.user_id",
)
defer rows.Close()
for rows.Next() {
var id int
var name string
var addr string
_ = rows.Scan(&id, &name, &addr)
log.Println(id, name, addr)
}
}

View file

@ -0,0 +1,24 @@
package resources
import (
"context"
"database/sql"
"log"
)
func RowsExhaust(ctx context.Context, db *sql.DB) {
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil {
log.Fatal(err)
}
if rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatal(err)
}
log.Println(id, name)
}
}

View file

@ -0,0 +1,35 @@
package resources
import (
"context"
"database/sql"
"log"
)
func TxExhaust(ctx context.Context, db *sql.DB) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Println(err)
return
}
if _, err = tx.ExecContext(ctx, `UPDATE users SET name = "Surl/Tesh-echer" WHERE id = 1`); err != nil {
log.Println(err)
return
}
if err = tx.Commit(); err != nil {
log.Println(err)
}
}
func TxDeadlock(ctx context.Context, db *sql.DB) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
_, _ = db.QueryContext(ctx, "SELECT id, name FROM users")
_, _ = db.QueryContext(ctx, "SELECT id, name FROM users")
}

View file

@ -101,19 +101,33 @@ database/sql
* Типовые ошибки и подводные камни * Типовые ошибки и подводные камни
* Deadlock * Запросы - deadlock
- ПРИМЕР много SQL вызовов подряд без вычитки данных .code resources/queries.go /^func QueryDeadlock/,/^}/
* Исчерпание ресурсов * Запросы - deadlock - fix 1
- ПРИМЕР незакрытие транзакций .code resources/queries.go /^func QueryDeadlockFixOne/,/^}/
- ПРИМЕР незакрытие rows
- ПРИМЕР неотдача коннектов
* Рефлексия * Запросы - deadlock - fix 2
ПРИМЕР ColumnType .code resources/queries.go /^func QueryDeadlockFixTwo/,/^}/
* Запросы - исчерпание ресурсов
.code resources/rows.go /^func RowsExhaust/,/^}/
* Транзакции - исчерпание ресурсов
.code resources/tx.go /^func TxExhaust/,/^}/
* Транзакции - deadlock
.code resources/tx.go /^func TxDeadlock/,/^}/
* Коннекты - исчерпание ресурсов
.code resources/conn.go /^func ConnExhaust/,/^}/
* Удобства и расширения * Удобства и расширения
@ -160,22 +174,24 @@ database/sql
.code sqlx/named.go /^func Insert/,/^}/ .code sqlx/named.go /^func Insert/,/^}/
* GORM
- ORM которая умеет всё
- ORM которая не умеет ничего
* Моки
Здесь будет пара слайдов о github.com/DATA-DOG/go-sqlmock
* Нетипичные драйверы * Нетипичные драйверы
- драйвер к кликхаусу использует транзакции как батчинг (ЗДЕСЬ БУДЕТ ПРИМЕР) * Нетипичные драйверы - github.com/DATA-DOG/go-sqlmock
* Пример не-sql драйвера .play sqlmock/sqlmock_test.go /^func TestSelect/,/^}/
ПРИМЕР go-redis * Нетипичные драйверы - github.com/ClickHouse/clickhouse-go
.code clickhouse/clickhouse.go /^func Example/,/^}/
* Не-SQL драйверы - Redis
.code redis/redis.go /^func Example/,/^}/
* ORM
- github.com/jinzhu/gorm
- github.com/go-pg/pg
* Популярные базы данных и их драйверы * Популярные базы данных и их драйверы

View file

@ -0,0 +1,35 @@
package sqlmock
import (
"database/sql"
"log"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/require"
)
func TestSelect(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
log.Fatal(err)
}
defer db.Close()
mock.ExpectBegin()
mock.ExpectExec("SELECT name FROM users WHERE id = ?").
WithArgs(1).
WillReturnError(sql.ErrNoRows)
mock.ExpectCommit()
tx, err := db.Begin()
require.NoError(t, err)
_, err = db.Exec("SELECT name FROM users WHERE id = ?", 1)
require.NotNil(t, err)
require.Equal(t, err, sql.ErrNoRows)
require.NoError(t, tx.Commit())
require.NoError(t, mock.ExpectationsWereMet())
}