git-lfs/tools/robustio_windows.go
Marat Radchenko 662a624819 Implement retry logic to fix LFS storage race conditions on Windows
Testing showed that while race condition analysis in #3880 was correct, the way it tries to fix that
does not work for the *first* git-lfs process that will actually perform file move.

Instead, this commit performs multiple attempts when working with files in LFS storage.

Similar logic is already implemented in "cmd/go/internal/robustio" and "cmd/go/internal/renameio" packages.
However, they are not public, so we cannot use them.
2019-11-05 17:30:13 +03:00

60 lines
1.1 KiB
Go

// +build windows
package tools
import (
"os"
"syscall"
"github.com/avast/retry-go"
)
const (
// This is private in [go]/src/internal/syscall/windows/syscall_windows.go :(
ERROR_SHARING_VIOLATION syscall.Errno = 32
)
func underlyingError(err error) error {
switch err := err.(type) {
case *os.PathError:
return err.Err
case *os.LinkError:
return err.Err
case *os.SyscallError:
return err.Err
}
return err
}
// isEphemeralError returns true if err may be resolved by waiting.
func isEphemeralError(err error) bool {
// TODO: Use this instead for Go >= 1.13
// return errors.Is(err, ERROR_SHARING_VIOLATION)
err = underlyingError(err)
return err == ERROR_SHARING_VIOLATION
}
func RobustRename(oldpath, newpath string) error {
return retry.Do(
func() error {
return os.Rename(oldpath, newpath)
},
retry.RetryIf(isEphemeralError),
retry.LastErrorOnly(true),
)
}
func RobustOpen(name string) (*os.File, error) {
var result *os.File
return result, retry.Do(
func() error {
f, err := os.Open(name)
result = f
return err
},
retry.RetryIf(isEphemeralError),
retry.LastErrorOnly(true),
)
}