Tags: #go #network
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/gofrs/flock"
)
const (
// FileLockTimeout is the amount of time to wait trying to acquire a lock.
FileLockTimeout = 30 * time.Second
// FileLockRetryDelay is the mount of time to wait before attempting a retry.
FileLockRetryDelay = 500 * time.Millisecond
)
func main() {
filename := "go.mod"
// NOTE: We stat the file because if we didn't, and we provided a file path
// that didn't exist, then flock will cause the file to exist!
// That is confusing behaviour obviously.
if _, err := os.Stat(filename); err != nil {
fmt.Printf("error stating file '%s': %v", filename, err)
return
}
fileLock := flock.New(filename)
lockCtx, cancel := context.WithTimeout(context.Background(), FileLockTimeout)
defer cancel()
locked, err := fileLock.TryLockContext(lockCtx, FileLockRetryDelay)
if err != nil {
fmt.Printf("error acquiring file lock for '%s': %v", fileLock.Path(), err)
return
}
if locked {
data, err := os.ReadFile(fileLock.Path())
if err != nil {
fmt.Printf("error reading file '%s': %v", fileLock.Path(), err)
return
}
fmt.Printf("file content: %+v\n", string(data))
// Add some friction to demonstrate how multiple instances of this app
// cannot access the go.mod file at the same time.
time.Sleep(20 * time.Second)
if err := fileLock.Unlock(); err != nil {
fmt.Printf("error releasing file lock for '%s': %v", fileLock.Path(), err)
}
}
}