Skip to content

Commit b58a1ac

Browse files
committed
feat: allow setting a timeout for mysql try lock
1 parent c378583 commit b58a1ac

File tree

2 files changed

+29
-10
lines changed

2 files changed

+29
-10
lines changed

database/mysql/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
| `x-migrations-table` | `MigrationsTable` | Name of the migrations table |
88
| `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. |
99
| `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. |
10+
| `x-try-lock-timeout` | `TryLockTimeoutSec` | Timeout in seconds to try to obtain migrations lock. A negative value means infinite timeout. Default value is `10` |
1011
| `dbname` | `DatabaseName` | The name of the database to connect to |
1112
| `user` | | The user to sign in as |
1213
| `password` | | The user's password |

database/mysql/mysql.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ func init() {
2929
database.Register("mysql", &Mysql{})
3030
}
3131

32+
const DefaultTryLockTimeoutSec = 10
33+
3234
var DefaultMigrationsTable = "schema_migrations"
3335

3436
var (
@@ -40,10 +42,11 @@ var (
4042
)
4143

4244
type Config struct {
43-
MigrationsTable string
44-
DatabaseName string
45-
NoLock bool
46-
StatementTimeout time.Duration
45+
MigrationsTable string
46+
DatabaseName string
47+
NoLock bool
48+
StatementTimeout time.Duration
49+
TryLockTimeoutSec int
4750
}
4851

4952
type Mysql struct {
@@ -253,16 +256,26 @@ func (m *Mysql) Open(url string) (database.Driver, error) {
253256
}
254257
}
255258

259+
tryLockTimeoutParam := customParams["x-try-lock-timeout"]
260+
tryLockTimeout := DefaultTryLockTimeoutSec
261+
if tryLockTimeoutParam != "" {
262+
tryLockTimeout, err = strconv.Atoi(tryLockTimeoutParam)
263+
if err != nil {
264+
return nil, fmt.Errorf("could not parse x-try-lock-timeout as int: %w", err)
265+
}
266+
}
267+
256268
db, err := sql.Open("mysql", config.FormatDSN())
257269
if err != nil {
258270
return nil, err
259271
}
260272

261273
mx, err := WithInstance(db, &Config{
262-
DatabaseName: config.DBName,
263-
MigrationsTable: customParams["x-migrations-table"],
264-
NoLock: noLock,
265-
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
274+
DatabaseName: config.DBName,
275+
MigrationsTable: customParams["x-migrations-table"],
276+
NoLock: noLock,
277+
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
278+
TryLockTimeoutSec: tryLockTimeout,
266279
})
267280
if err != nil {
268281
return nil, err
@@ -295,9 +308,14 @@ func (m *Mysql) Lock() error {
295308
return err
296309
}
297310

298-
query := "SELECT GET_LOCK(?, 10)"
311+
tryLockTimeout := DefaultTryLockTimeoutSec
312+
if m.config.TryLockTimeoutSec != 0 {
313+
tryLockTimeout = m.config.TryLockTimeoutSec
314+
}
315+
316+
query := "SELECT GET_LOCK(?, ?)"
299317
var success bool
300-
if err := m.conn.QueryRowContext(context.Background(), query, aid).Scan(&success); err != nil {
318+
if err := m.conn.QueryRowContext(context.Background(), query, aid, tryLockTimeout).Scan(&success); err != nil {
301319
return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
302320
}
303321

0 commit comments

Comments
 (0)