mirror of
https://github.com/octoleo/restic.git
synced 2024-11-29 08:14:03 +00:00
lock: rework stale lock refresh to avoid data race
This commit is contained in:
parent
05e5e29a8c
commit
b2fcbc21cb
@ -110,12 +110,12 @@ retryLoop:
|
|||||||
}
|
}
|
||||||
lockInfo.refreshWG.Add(2)
|
lockInfo.refreshWG.Add(2)
|
||||||
refreshChan := make(chan struct{})
|
refreshChan := make(chan struct{})
|
||||||
forcedRefreshChan := make(chan struct{})
|
forceRefreshChan := make(chan refreshLockRequest)
|
||||||
|
|
||||||
globalLocks.Lock()
|
globalLocks.Lock()
|
||||||
globalLocks.locks[lock] = lockInfo
|
globalLocks.locks[lock] = lockInfo
|
||||||
go refreshLocks(ctx, lockInfo, refreshChan, forcedRefreshChan)
|
go refreshLocks(ctx, repo.Backend(), lockInfo, refreshChan, forceRefreshChan)
|
||||||
go monitorLockRefresh(ctx, repo.Backend(), lockInfo, refreshChan, forcedRefreshChan)
|
go monitorLockRefresh(ctx, lockInfo, refreshChan, forceRefreshChan)
|
||||||
globalLocks.Unlock()
|
globalLocks.Unlock()
|
||||||
|
|
||||||
return lock, ctx, err
|
return lock, ctx, err
|
||||||
@ -127,7 +127,11 @@ var refreshInterval = 5 * time.Minute
|
|||||||
// the difference allows to compensate for a small time drift between clients.
|
// the difference allows to compensate for a small time drift between clients.
|
||||||
var refreshabilityTimeout = restic.StaleLockTimeout - refreshInterval*3/2
|
var refreshabilityTimeout = restic.StaleLockTimeout - refreshInterval*3/2
|
||||||
|
|
||||||
func refreshLocks(ctx context.Context, lockInfo *lockContext, refreshed chan<- struct{}, forcedRefresh <-chan struct{}) {
|
type refreshLockRequest struct {
|
||||||
|
result chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshLocks(ctx context.Context, backend restic.Backend, lockInfo *lockContext, refreshed chan<- struct{}, forceRefresh <-chan refreshLockRequest) {
|
||||||
debug.Log("start")
|
debug.Log("start")
|
||||||
lock := lockInfo.lock
|
lock := lockInfo.lock
|
||||||
ticker := time.NewTicker(refreshInterval)
|
ticker := time.NewTicker(refreshInterval)
|
||||||
@ -154,9 +158,19 @@ func refreshLocks(ctx context.Context, lockInfo *lockContext, refreshed chan<- s
|
|||||||
debug.Log("terminate")
|
debug.Log("terminate")
|
||||||
return
|
return
|
||||||
|
|
||||||
case <-forcedRefresh:
|
case req := <-forceRefresh:
|
||||||
// update lock refresh time
|
// keep on going if our current lock still exists
|
||||||
lastRefresh = lock.Time
|
success := tryRefreshStaleLock(ctx, backend, lock, lockInfo.cancel)
|
||||||
|
// inform refresh goroutine about forced refresh
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case req.result <- success:
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
|
// update lock refresh time
|
||||||
|
lastRefresh = lock.Time
|
||||||
|
}
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if time.Since(lastRefresh) > refreshabilityTimeout {
|
if time.Since(lastRefresh) > refreshabilityTimeout {
|
||||||
@ -180,7 +194,7 @@ func refreshLocks(ctx context.Context, lockInfo *lockContext, refreshed chan<- s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func monitorLockRefresh(ctx context.Context, backend restic.Backend, lockInfo *lockContext, refreshed <-chan struct{}, forcedRefresh chan<- struct{}) {
|
func monitorLockRefresh(ctx context.Context, lockInfo *lockContext, refreshed <-chan struct{}, forceRefresh chan<- refreshLockRequest) {
|
||||||
// time.Now() might use a monotonic timer which is paused during standby
|
// time.Now() might use a monotonic timer which is paused during standby
|
||||||
// convert to unix time to ensure we compare real time values
|
// convert to unix time to ensure we compare real time values
|
||||||
lastRefresh := time.Now().UnixNano()
|
lastRefresh := time.Now().UnixNano()
|
||||||
@ -212,14 +226,22 @@ func monitorLockRefresh(ctx context.Context, backend restic.Backend, lockInfo *l
|
|||||||
}
|
}
|
||||||
|
|
||||||
// keep on going if our current lock still exists
|
// keep on going if our current lock still exists
|
||||||
if tryRefreshStaleLock(ctx, backend, lockInfo.lock, lockInfo.cancel) {
|
refreshReq := refreshLockRequest{
|
||||||
lastRefresh = time.Now().UnixNano()
|
result: make(chan bool),
|
||||||
|
}
|
||||||
|
// inform refresh goroutine about forced refresh
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case forceRefresh <- refreshReq:
|
||||||
|
}
|
||||||
|
var success bool
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case success = <-refreshReq.result:
|
||||||
|
}
|
||||||
|
|
||||||
// inform refresh gorountine about forced refresh
|
if success {
|
||||||
select {
|
lastRefresh = time.Now().UnixNano()
|
||||||
case <-ctx.Done():
|
|
||||||
case forcedRefresh <- struct{}{}:
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user