Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[HttpKernel] Cache locking fails over NFS due to missing read on the lock file #52144

Closed
@driskell

Description

@driskell

Symfony version(s) affected

4.4, 5.4, 6.3

Description

When cache is mounted on NFS with full locking support available, the cache locking fails. Every request will begin to generate and build the container in a large race until one succeeds and successfully writes the cache file.

NFS with full locking support available refers to NFSv3 with recent kernels and rpc.statd running, and NFSv4 which natively supports it. Both support it through fcntl F_SETLK or F_SETLKW. Relevant flock calls are converted. So it always becomes advisory.

I'm aware there's lots of information around on lots of sites about PHP flock and NFS not compatible but it's all very old information and extremely out of date. In summary, it is compatible, and does indeed work as I believe the implementers intended, and has done for a very long while. It's just this one quirk on the file handle open mode requirements when it comes to NFS. Hopefully this issue will provide useful in providing a full outline of the support and how it works, as I put in the context all the main areas involved.

Thanks! Love to Symfony's Team ❤️

How to reproduce

Setup a Symfony framework skeleton and put it on an NFSv4 mount. Run the bin/console several times on different machines mounted to that NFS. You'll see multiple container builds appear in the cache folder. (There is a single PHP entry for the cache - e.g. appAppKernelProdContainer.php - but it loads a container folder with a random name - e.g. ContainerFUJetMW - so you can see when it doesn't lock as it'll have multiple ContainerXXXXXXX in place.)

Performing strace shows that LOCK_EX | LOCK_NB flock call is successful for the first cache build. The next that races in results in a EAGAIN ($wouldBlock is then set to true). It then attempts to acquire a read lock with LOCK_SH but that subsequently fails as the flock calls flock system call and the NFS client translates to fcntl which results in EBADF as per the documentation for fcntl because the original fopen did not include read mode, only write mode.

Possible Solution

Open the lock file with w+.

Confirmed to resolve the problem completely.

Additional Context

Code:

https://github.com/symfony/http-kernel/blob/6.3/Kernel.php#L430-L431

man nfs on most OS shows following support - this is from a CentOS 7 machine:

The Network Lock Manager protocol is a separate sideband protocol used to manage file locks in NFS version 2 and version 3. To support lock recovery after a client or server reboot, a second sideband protocol -- known as the Network Status Manager protocol -- is also required. In NFS version 4, file locking is supported directly in the main NFS protocol, and the NLM and NSM sideband protocols are not used.
[...]
NLM supports advisory file locks only. To lock NFS files, use fcntl(2) with the F_GETLK and F_SETLK commands. The NFS client converts file locks obtained via flock(2) to advisory locks.

https://linux.die.net/man/2/fcntl

In order to place a read lock, fd must be open for reading. In order to place a write lock, fd must be open for writing. To place both types of lock, open a file read-write.
[...]
EBADF
fd is not an open file descriptor, or the command was F_SETLK or F_SETLKW and the file descriptor open mode doesn't match with the type of lock requested.

PHP is using flock under the hood for flock for BC reasons - fcntl is added via other means I think an extension. So Symfony is using flock which calls flock system call and NFS client translates to fcntl.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions