Skip to content

Commit 6834b7b

Browse files
committed
feat: allow setting a timeout for mysql try lock
1 parent 257fa84 commit 6834b7b

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
@@ -27,6 +27,8 @@ func init() {
2727
database.Register("mysql", &Mysql{})
2828
}
2929

30+
const DefaultTryLockTimeoutSec = 10
31+
3032
var DefaultMigrationsTable = "schema_migrations"
3133

3234
var (
@@ -38,10 +40,11 @@ var (
3840
)
3941

4042
type Config struct {
41-
MigrationsTable string
42-
DatabaseName string
43-
NoLock bool
44-
StatementTimeout time.Duration
43+
MigrationsTable string
44+
DatabaseName string
45+
NoLock bool
46+
StatementTimeout time.Duration
47+
TryLockTimeoutSec int
4548
}
4649

4750
type Mysql struct {
@@ -251,16 +254,26 @@ func (m *Mysql) Open(url string) (database.Driver, error) {
251254
}
252255
}
253256

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

259271
mx, err := WithInstance(db, &Config{
260-
DatabaseName: config.DBName,
261-
MigrationsTable: customParams["x-migrations-table"],
262-
NoLock: noLock,
263-
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
272+
DatabaseName: config.DBName,
273+
MigrationsTable: customParams["x-migrations-table"],
274+
NoLock: noLock,
275+
StatementTimeout: time.Duration(statementTimeout) * time.Millisecond,
276+
TryLockTimeoutSec: tryLockTimeout,
264277
})
265278
if err != nil {
266279
return nil, err
@@ -293,9 +306,14 @@ func (m *Mysql) Lock() error {
293306
return err
294307
}
295308

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

0 commit comments

Comments
 (0)