« Back to Index

Go: file lock

View original Gist on GitHub

Tags: #go #network

main.go

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)
		}
	}
}