Stricter pg backend locking during workspace creation
This commit is contained in:
parent
31c9776d55
commit
ccf549a62d
|
@ -72,15 +72,27 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
||||||
info.ID = lockID
|
info.ID = lockID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to acquire lock for the existing row.
|
// Local helper function so we can call it multiple places
|
||||||
query := `SELECT %s.id, pg_try_advisory_lock(%s.id) FROM %s.%s WHERE %s.name = $1`
|
//
|
||||||
|
lockUnlock := func(pgLockId string) error {
|
||||||
|
query := `SELECT pg_advisory_unlock(%s)`
|
||||||
|
row := c.Client.QueryRow(fmt.Sprintf(query, pgLockId))
|
||||||
|
var didUnlock []byte
|
||||||
|
err := row.Scan(&didUnlock)
|
||||||
|
if err != nil {
|
||||||
|
return &state.LockError{Info: c.info, Err: err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to acquire locks for the existing row `id` and the creation lock `-1`.
|
||||||
|
query := `SELECT %s.id, pg_try_advisory_lock(%s.id), pg_try_advisory_lock(-1) FROM %s.%s WHERE %s.name = $1`
|
||||||
row := c.Client.QueryRow(fmt.Sprintf(query, statesTableName, statesTableName, c.SchemaName, statesTableName, statesTableName), c.Name)
|
row := c.Client.QueryRow(fmt.Sprintf(query, statesTableName, statesTableName, c.SchemaName, statesTableName, statesTableName), c.Name)
|
||||||
var pgLockId, didLock []byte
|
var pgLockId, didLock, didLockForCreate []byte
|
||||||
err = row.Scan(&pgLockId, &didLock)
|
err = row.Scan(&pgLockId, &didLock, &didLockForCreate)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
// When the row does not yet exist in state, take
|
// No rows means we're creating the workspace. Take the creation lock.
|
||||||
// the `-1` lock to create the new row.
|
|
||||||
innerRow := c.Client.QueryRow(`SELECT pg_try_advisory_lock(-1)`)
|
innerRow := c.Client.QueryRow(`SELECT pg_try_advisory_lock(-1)`)
|
||||||
var innerDidLock []byte
|
var innerDidLock []byte
|
||||||
err := innerRow.Scan(&innerDidLock)
|
err := innerRow.Scan(&innerDidLock)
|
||||||
|
@ -94,8 +106,16 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return "", &state.LockError{Info: info, Err: err}
|
return "", &state.LockError{Info: info, Err: err}
|
||||||
case string(didLock) == "false":
|
case string(didLock) == "false":
|
||||||
|
// Existing workspace is already locked. Release the attempted creation lock.
|
||||||
|
lockUnlock("-1")
|
||||||
return "", &state.LockError{Info: info, Err: fmt.Errorf("Workspace is already locked: %s", c.Name)}
|
return "", &state.LockError{Info: info, Err: fmt.Errorf("Workspace is already locked: %s", c.Name)}
|
||||||
|
case string(didLockForCreate) == "false":
|
||||||
|
// Someone has the creation lock already. Release the existing workspace because it might not be safe to touch.
|
||||||
|
lockUnlock(string(pgLockId))
|
||||||
|
return "", &state.LockError{Info: info, Err: fmt.Errorf("Cannot lock workspace; already locked for workspace creation: %s", c.Name)}
|
||||||
default:
|
default:
|
||||||
|
// Existing workspace is now locked. Release the attempted creation lock.
|
||||||
|
lockUnlock("-1")
|
||||||
info.Path = string(pgLockId)
|
info.Path = string(pgLockId)
|
||||||
}
|
}
|
||||||
c.info = info
|
c.info = info
|
||||||
|
@ -116,9 +136,6 @@ func (c *RemoteClient) Unlock(id string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &state.LockError{Info: c.info, Err: err}
|
return &state.LockError{Info: c.info, Err: err}
|
||||||
}
|
}
|
||||||
if string(didUnlock) == "false" {
|
|
||||||
return &state.LockError{Info: c.info, Err: fmt.Errorf("Workspace is already unlocked: %s", c.Name)}
|
|
||||||
}
|
|
||||||
c.info = nil
|
c.info = nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue