Skip to content

Commit

Permalink
feat: allow setting a timeout for mysql try lock
Browse files Browse the repository at this point in the history
  • Loading branch information
vgarvardt committed Dec 4, 2024
1 parent c378583 commit b58a1ac
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 10 deletions.
1 change: 1 addition & 0 deletions database/mysql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
| `x-migrations-table` | `MigrationsTable` | Name of the migrations table |
| `x-no-lock` | `NoLock` | Set to `true` to skip `GET_LOCK`/`RELEASE_LOCK` statements. Useful for [multi-master MySQL flavors](https://www.percona.com/doc/percona-xtradb-cluster/LATEST/features/pxc-strict-mode.html#explicit-table-locking). Only run migrations from one host when this is enabled. |
| `x-statement-timeout` | `StatementTimeout` | Abort any statement that takes more than the specified number of milliseconds, functionally similar to [Server-side SELECT statement timeouts](https://dev.mysql.com/blog-archive/server-side-select-statement-timeouts/) but enforced by the client. Available for all versions of MySQL, not just >=5.7. |
| `x-try-lock-timeout` | `TryLockTimeoutSec` | Timeout in seconds to try to obtain migrations lock. A negative value means infinite timeout. Default value is `10` |
| `dbname` | `DatabaseName` | The name of the database to connect to |
| `user` | | The user to sign in as |
| `password` | | The user's password |
Expand Down
38 changes: 28 additions & 10 deletions database/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func init() {
database.Register("mysql", &Mysql{})
}

const DefaultTryLockTimeoutSec = 10

var DefaultMigrationsTable = "schema_migrations"

var (
Expand All @@ -40,10 +42,11 @@ var (
)

type Config struct {
MigrationsTable string
DatabaseName string
NoLock bool
StatementTimeout time.Duration
MigrationsTable string
DatabaseName string
NoLock bool
StatementTimeout time.Duration
TryLockTimeoutSec int
}

type Mysql struct {
Expand Down Expand Up @@ -253,16 +256,26 @@ func (m *Mysql) Open(url string) (database.Driver, error) {
}
}

tryLockTimeoutParam := customParams["x-try-lock-timeout"]
tryLockTimeout := DefaultTryLockTimeoutSec
if tryLockTimeoutParam != "" {
tryLockTimeout, err = strconv.Atoi(tryLockTimeoutParam)
if err != nil {
return nil, fmt.Errorf("could not parse x-try-lock-timeout as int: %w", err)
}
}

db, err := sql.Open("mysql", config.FormatDSN())
if err != nil {
return nil, err
}

mx, err := WithInstance(db, &Config{
DatabaseName: config.DBName,
MigrationsTable: customParams["x-migrations-table"],
NoLock: noLock,
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
DatabaseName: config.DBName,
MigrationsTable: customParams["x-migrations-table"],
NoLock: noLock,
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
TryLockTimeoutSec: tryLockTimeout,
})
if err != nil {
return nil, err
Expand Down Expand Up @@ -295,9 +308,14 @@ func (m *Mysql) Lock() error {
return err
}

query := "SELECT GET_LOCK(?, 10)"
tryLockTimeout := DefaultTryLockTimeoutSec
if m.config.TryLockTimeoutSec != 0 {
tryLockTimeout = m.config.TryLockTimeoutSec
}

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

Expand Down

0 comments on commit b58a1ac

Please sign in to comment.