Implementing File Locking in NFS: PHP flock() Alternatives and Workarounds


2 views

When working with PHP applications on an NFS (Network File System) mounted filesystem, you'll quickly discover that the standard flock() function often fails to work as expected. This occurs because traditional file locking mechanisms don't always propagate reliably across network filesystems.

NFS implements locking differently than local filesystems due to its distributed nature:

  • NFS v2/v3 requires a separate lock manager (rpc.lockd)
  • NFS v4 includes native locking but implementation varies
  • Network latency can cause timeout issues
  • Client caching may lead to stale locks

Here are several approaches that work effectively on NFS:

1. Using mkdir as Atomic Lock

Create locks using directory operations which are atomic in NFS:


function nfs_lock($lockfile) {
    $lockdir = $lockfile . '.lock';
    while (!@mkdir($lockdir)) {
        usleep(100000); // Sleep for 100ms
    }
    return true;
}

function nfs_unlock($lockfile) {
    $lockdir = $lockfile . '.lock';
    return @rmdir($lockdir);
}

2. Advisory Locking with lockf

For systems where lockf is available:


$fp = fopen('/path/to/file', 'r+');
if (lockf($fp, LOCK_EX)) {
    // Critical section
    lockf($fp, LOCK_UN);
}
fclose($fp);

3. Database-based Locking

When working with NFS-mounted files, consider moving locks to a database:


// Using MySQL for locking
function db_lock($name, $timeout = 30) {
    $result = db_query(
        "GET_LOCK('".db_escape($name)."', $timeout)"
    );
    return $result == 1;
}

function db_unlock($name) {
    db_query("RELEASE_LOCK('".db_escape($name)."')");
}

When implementing NFS file locks:

  • Always set appropriate timeout values
  • Implement proper cleanup in case of crashes
  • Consider using a combination of approaches
  • Profile performance impact on your specific NFS setup

Create a test script to verify lock behavior:


$lockfile = '/nfs/mount/test.lock';
if (nfs_lock($lockfile)) {
    echo "Lock acquired, working for 5 seconds...\n";
    sleep(5);
    nfs_unlock($lockfile);
    echo "Lock released\n";
} else {
    echo "Failed to acquire lock\n";
}

When working with PHP applications on NFS-mounted directories, many developers encounter unexpected behavior with the flock() function. The issue stems from fundamental differences in how local filesystems and NFS handle file locking.

PHP's flock() relies on advisory locking at the kernel level, but NFS implementations vary in their support for file locks. The key problems include:

  • Non-mandatory locking nature (advisory only)
  • Potential for locks to disappear after server reboots
  • Network latency affecting lock acquisition timing
  • Inconsistent implementation across NFS versions

Here are three approaches that have proven effective in production environments:

1. Using Lock Files with Unique Naming

Create uniquely named lock files as atomic operation indicators:


function nfsSafeLock($filePath) {
    $lockFile = $filePath . '.' . getmypid() . '.' . microtime(true) . '.lock';
    if (!@symlink($filePath, $lockFile)) {
        return false;
    }
    return $lockFile;
}

function nfsSafeUnlock($lockFile) {
    unlink($lockFile);
}

2. Database-Based Locking

For systems already using a database, leverage transactional locks:


// MySQL example
function acquireDbLock($name, $timeout = 30) {
    $conn = new PDO(...);
    $conn->exec("SELECT GET_LOCK('$name', $timeout)");
    return $conn;
}

function releaseDbLock($conn, $name) {
    $conn->exec("SELECT RELEASE_LOCK('$name')");
    $conn = null;
}

3. Using NFSv4 Lease-Based Locking

If your NFS server supports v4+, you can use its native locking mechanism:


$fp = fopen($file, 'r+');
if (flock($fp, LOCK_EX | LOCK_NB, $wouldblock)) {
    if ($wouldblock) {
        // Lock would block (contended)
    } else {
        // Exclusive lock obtained
    }
}

When implementing NFS file locking:

  • Always set reasonable timeout values
  • Implement proper lock cleanup in exception handlers
  • Monitor for stale locks in your application
  • Consider using distributed lock managers for critical systems

For high-availability systems, consider:

  • Redis-based distributed locks
  • ZooKeeper coordination services
  • Consul's lock mechanism