forked from TrueCloudLab/restic
lock: checkForOtherLocks processes each lock at most once
If a lock could not be loaded, then restic would check all lock files again. These repeated checks are not useful as the status of a lock file cannot change unless its ID changes too. Thus, skip already check lock files on retries.
This commit is contained in:
parent
6696195f38
commit
f1f34eb3e5
1 changed files with 15 additions and 3 deletions
|
@ -163,9 +163,16 @@ func (l *Lock) fillUserInfo() error {
|
||||||
// exclusive lock is found.
|
// exclusive lock is found.
|
||||||
func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
checkedIDs := NewIDSet()
|
||||||
|
if l.lockID != nil {
|
||||||
|
checkedIDs.Insert(*l.lockID)
|
||||||
|
}
|
||||||
// retry locking a few times
|
// retry locking a few times
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
err = ForAllLocks(ctx, l.repo, l.lockID, func(id ID, lock *Lock, err error) error {
|
// Store updates in new IDSet to prevent data races
|
||||||
|
var m sync.Mutex
|
||||||
|
newCheckedIDs := NewIDSet(checkedIDs.List()...)
|
||||||
|
err = ForAllLocks(ctx, l.repo, checkedIDs, func(id ID, lock *Lock, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we cannot load a lock then it is unclear whether it can be ignored
|
// if we cannot load a lock then it is unclear whether it can be ignored
|
||||||
// it could either be invalid or just unreadable due to network/permission problems
|
// it could either be invalid or just unreadable due to network/permission problems
|
||||||
|
@ -181,8 +188,13 @@ func (l *Lock) checkForOtherLocks(ctx context.Context) error {
|
||||||
return &alreadyLockedError{otherLock: lock}
|
return &alreadyLockedError{otherLock: lock}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// valid locks will remain valid
|
||||||
|
m.Lock()
|
||||||
|
newCheckedIDs.Insert(id)
|
||||||
|
m.Unlock()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
checkedIDs = newCheckedIDs
|
||||||
// no lock detected
|
// no lock detected
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -417,12 +429,12 @@ func RemoveAllLocks(ctx context.Context, repo Repository) (uint, error) {
|
||||||
// It is guaranteed that the function is not run concurrently. If the
|
// It is guaranteed that the function is not run concurrently. If the
|
||||||
// callback returns an error, this function is cancelled and also returns that error.
|
// callback returns an error, this function is cancelled and also returns that error.
|
||||||
// If a lock ID is passed via excludeID, it will be ignored.
|
// If a lock ID is passed via excludeID, it will be ignored.
|
||||||
func ForAllLocks(ctx context.Context, repo Repository, excludeID *ID, fn func(ID, *Lock, error) error) error {
|
func ForAllLocks(ctx context.Context, repo Repository, excludeIDs IDSet, fn func(ID, *Lock, error) error) error {
|
||||||
var m sync.Mutex
|
var m sync.Mutex
|
||||||
|
|
||||||
// For locks decoding is nearly for free, thus just assume were only limited by IO
|
// For locks decoding is nearly for free, thus just assume were only limited by IO
|
||||||
return ParallelList(ctx, repo, LockFile, repo.Connections(), func(ctx context.Context, id ID, size int64) error {
|
return ParallelList(ctx, repo, LockFile, repo.Connections(), func(ctx context.Context, id ID, size int64) error {
|
||||||
if excludeID != nil && id.Equal(*excludeID) {
|
if excludeIDs.Has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
|
Loading…
Reference in a new issue