mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-07-24 17:12:54 +00:00
exclusive_locks: clear renew-running flag before dropping isLocked (#10413)
On renewal failure the renew goroutine stored isLocked=false before its deferred renewGoroutineRunning.Store(false) ran. A concurrent RequestLock interleaving there reacquires the lease (sees isLocked=false), sets isLocked=true, then its CompareAndSwap on renewGoroutineRunning fails because the old goroutine's flag is still set — so no replacement renewer starts. The lock is then held locally with nothing renewing it, and silently expires on the master after the lease TTL, admitting a second holder. Clear renewGoroutineRunning before isLocked on the failure path so the reacquire path always starts a fresh renewer. Builds and vets clean; no behavior change on the success path. Claude-Session: https://claude.ai/code/session_01Ks16jnt4S7gdDk8cheQ3xu
This commit is contained in:
@@ -93,13 +93,15 @@ func (l *ExclusiveLocker) RequestLock(clientName string) {
|
||||
if l.renewGoroutineRunning.CompareAndSwap(false, true) {
|
||||
// start a goroutine to renew the lease
|
||||
go func() {
|
||||
defer l.renewGoroutineRunning.Store(false)
|
||||
ctx2, cancel2 := context.WithCancel(context.Background())
|
||||
defer cancel2()
|
||||
|
||||
for {
|
||||
if err := l.renewLease(ctx2); err != nil {
|
||||
glog.Warningf("Failed to renew lock %s: %v", l.lockName, err)
|
||||
// clear the running flag before isLocked, so a RequestLock that
|
||||
// reacquires (once isLocked is false) starts a replacement renewer
|
||||
l.renewGoroutineRunning.Store(false)
|
||||
l.isLocked.Store(false)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user