8000 Fix lockyloo golang by rdallman · Pull Request #8 · golang-migrate/migrate · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Fix lockyloo golang #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions database/mysql/mysql.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// +build go1.9

package mysql

import (
"context"
"crypto/tls"
"crypto/x509"
"database/sql"
Expand Down Expand Up @@ -35,7 +38,9 @@ type Config struct {
}

type Mysql struct {
db *sql.DB
// mysql RELEASE_LOCK must be called from the same conn, so
// just do everything over a single conn anyway.
conn *sql.Conn
isLocked bool

config *Config
Expand Down Expand Up @@ -67,8 +72,13 @@ func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
config.MigrationsTable = DefaultMigrationsTable
}

conn, err := instance.Conn(context.Background())
if err != nil {
return nil, err
}

mx := &Mysql{
db: instance,
conn: conn,
config: config,
}

Expand Down Expand Up @@ -148,7 +158,7 @@ func (m *Mysql) Open(url string) (database.Driver, error) {
}

func (m *Mysql) Close() error {
return m.db.Close()
return m.conn.Close()
}

func (m *Mysql) Lock() error {
Expand All @@ -162,9 +172,9 @@ func (m *Mysql) Lock() error {
return err
}

query := "SELECT GET_LOCK(?, 1)"
query := "SELECT GET_LOCK(?, 10)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably make the timeout configurable, but I think it's fine to make that change in a separate PR

var success bool
if err := m.db.QueryRow(query, aid).Scan(&success); err != nil {
if err := m.conn.QueryRowContext(context.Background(), query, aid).Scan(&success); err != nil {
return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
}

Expand All @@ -188,10 +198,14 @@ func (m *Mysql) Unlock() error {
}

query := `SELECT RELEASE_LOCK(?)`
if _, err := m.db.Exec(query, aid); err != nil {
if _, err := m.conn.ExecContext(context.Background(), query, aid); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}

// NOTE: RELEASE_LOCK could return NULL or (or 0 if the code is changed),
// in which case isLocked should be true until the timeout expires -- synchronizing
// these states is likely not worth trying to do; reconsider the necessity of isLocked.

m.isLocked = false
return nil
}
Expand All @@ -203,27 +217,28 @@ func (m *Mysql) Run(migration io.Reader) error {
}

query := string(migr[:])
if _, err := m.db.Exec(query); err != nil {
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
return database.Error{OrigErr: err, Err: "migration failed", Query: migr}
}

return nil
}

func (m *Mysql) SetVersion(version int, dirty bool) error {
tx, err := m.db.Begin()
tx, err := m.conn.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return &database.Error{OrigErr: err, Err: "transaction start failed"}
}

query := "TRUNCATE `" + m.config.MigrationsTable + "`"
if _, err := m.db.Exec(query); err != nil {
if _, err := tx.ExecContext(context.Background(), query); err != nil {
tx.Rollback()
return &database.Error{OrigErr: err, Query: []byte(query)}
}

if version >= 0 {
query := "INSERT INTO `" + m.config.MigrationsTable + "` (version, dirty) VALUES (?, ?)"
if _, err := m.db.Exec(query, version, dirty); err != nil {
if _, err := tx.ExecContext(context.Background(), query, version, dirty); err != nil {
tx.Rollback()
return &database.Error{OrigErr: err, Query: []byte(query)}
}
Expand All @@ -238,7 +253,7 @@ func (m *Mysql) SetVersion(version int, dirty bool) error {

func (m *Mysql) Version() (version int, dirty bool, err error) {
query := "SELECT version, dirty FROM `" + m.config.MigrationsTable + "` LIMIT 1"
err = m.db.QueryRow(query).Scan(&version, &dirty)
err = m.conn.QueryRowContext(context.Background(), query).Scan(&version, &dirty)
switch {
case err == sql.ErrNoRows:
return database.NilVersion, false, nil
Expand All @@ -259,7 +274,7 @@ func (m *Mysql) Version() (version int, dirty bool, err error) {
func (m *Mysql) Drop() error {
// select all tables
query := `SHOW TABLES LIKE '%'`
tables, err := m.db.Query(query)
tables, err := m.conn.QueryContext(context.Background(), query)
if err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
Expand All @@ -281,7 +296,7 @@ func (m *Mysql) Drop() error {
// delete one by one ...
for _, t := range tableNames {
query = "DROP TABLE IF EXISTS `" + t + "` CASCADE"
if _, err := m.db.Exec(query); err != nil {
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
}
Expand All @@ -297,7 +312,7 @@ func (m *Mysql) ensureVersionTable() error {
// check if migration table exists
var result string
query := `SHOW TABLES LIKE "` + m.config.MigrationsTable + `"`
if err := m.db.QueryRow(query).Scan(&result); err != nil {
if err := m.conn.QueryRowContext(context.Background(), query).Scan(&result); err != nil {
if err != sql.ErrNoRows {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
Expand All @@ -307,7 +322,7 @@ func (m *Mysql) ensureVersionTable() error {

// if not, create the empty migration table
query = "CREATE TABLE `" + m.config.MigrationsTable + "` (version bigint not null primary key, dirty boolean not null)"
if _, err := m.db.Exec(query); err != nil {
if _, err := m.conn.ExecContext(context.Background(), query); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
return nil
Expand Down
34 changes: 34 additions & 0 deletions database/mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,37 @@ func Test(t *testing.T) {
}
})
}

func TestLockWorks(t *testing.T) {
mt.ParallelTest(t, versions, isReady,
func(t *testing.T, i mt.Instance) {
p := &Mysql{}
addr := fmt.Sprintf("mysql://root:r 7398 oot@tcp(%v:%v)/public", i.Host(), i.Port())
d, err := p.Open(addr)
if err != nil {
t.Fatalf("%v", err)
}
dt.Test(t, d, []byte("SELECT 1"))

ms := d.(*Mysql)

err = ms.Lock()
if err != nil {
t.Fatal(err)
}
err = ms.Unlock()
if err != nil {
t.Fatal(err)
}

// make sure the 2nd lock works (RELEASE_LOCK is very finicky)
err = ms.Lock()
if err != nil {
t.Fatal(err)
}
err = ms.Unlock()
if err != nil {
t.Fatal(err)
}
})
}
0