in middleware/dblock/lock_client.go [71:116]
func (l MysqlLocker) ObtainTimeoutContext(ctx context.Context, key string, timeout int) (*Lock, error) {
cancellableContext, cancelFunc := context.WithCancel(context.Background())
dbConn, err := l.db.Conn(ctx)
if err != nil {
cancelFunc()
return nil, fmt.Errorf("failed to get a db connection: %w", err)
}
row := dbConn.QueryRowContext(ctx, "SELECT COALESCE(GET_LOCK(?, ?), 2)", key, timeout)
var res int
err = row.Scan(&res)
if err != nil {
// mysql error does not tell if it was due to context closing, checking it manually
select {
case <-ctx.Done():
cancelFunc()
return nil, ErrGetLockContextCancelled
default:
break
}
cancelFunc()
return nil, fmt.Errorf("could not read mysql response: %w", err)
} else if res == 2 {
// Internal MySQL error occurred, such as out-of-memory, thread killed or others (the doc is not clear)
// Note: some MySQL/MariaDB versions (like MariaDB 10.1) does not support -1 as timeout parameters
cancelFunc()
return nil, ErrMySQLInternalError
} else if res == 0 {
// MySQL Timeout
cancelFunc()
return nil, ErrMySQLTimeout
}
lock := &Lock{
key: key,
conn: dbConn,
unlocker: make(chan struct{}, 1),
lostLockContext: cancellableContext,
cancelFunc: cancelFunc,
}
go lock.refresher(l.refreshInterval, cancelFunc)
return lock, nil
}