Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behaviour: file created when acquiring lock #59

Closed
Integralist opened this issue Jun 30, 2022 · 2 comments · Fixed by #76
Closed

Unexpected behaviour: file created when acquiring lock #59

Integralist opened this issue Jun 30, 2022 · 2 comments · Fixed by #76

Comments

@Integralist
Copy link

Integralist commented Jun 30, 2022

👋🏻

I'm not sure if this is expected behaviour for this package or not, it was unexpected to me at least, but if I pass a file path that doesn't exist, I will successfully be able to acquire a lock and then when reading the contents of the file it returns empty content.

The reason this was unexpected was that in my project I have a test that validates an error is returned if a path fails to be read, but it was failing because it expected an error and now (since implementing this package) it was no longer getting an error because the file did exist (and for my use case that was unexpected).

An example of what I'm describing is:

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 = 10 * time.Second

	// FileLockRetryDelay is the mount of time to wait before attempting a retry.
	FileLockRetryDelay = 500 * time.Millisecond
)

func main() {
	filename := "does_not_exist"

	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 {
		fmt.Println("got a lock", fileLock.Path())

		fi, err := os.Stat(fileLock.Path())
		if err != nil {
			fmt.Printf("error stating file '%s': %v", fileLock.Path(), err)
			return
		}
		fmt.Printf("%+v\n", fi) // seems the file now exists

		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)) // empty file content

		if err := fileLock.Unlock(); err != nil {
			fmt.Printf("error releasing file lock for '%s': %v", fileLock.Path(), err)
		}
	}
}

The solution in my case was to add a .Stat() call up front before calling flock.New() but I wanted to be sure this wasn't a bug. Maybe it's I just don't understand how file locks are supposed to work (very likely).

Any feedback/guidance appreciated.

Thanks!

@jnoxon
Copy link

jnoxon commented Apr 27, 2023

I came here because of this. This behavior has been causing corruption in a production app I maintain.

I came across this fork which corrects the behavior: AODocs-Dev@3c69484

I don't think this project is maintained, but if this affects you, you can try this:

go mod edit -replace github.com/gofrs/flock@latest=github.com/AODocs-Dev/flock@3c69484
go mod tidy

@ldez
Copy link
Contributor

ldez commented Jun 30, 2024

I extended the constructor, now you can set the flag with an option:

_ = flock.New("example", flock.SetFlag(os.O_CREATE|os.O_RDWR)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants