Why You See “Duplicate” Locks
Case 1. Lock Retry after Timeout (Retransmission)
If the client sends a LOCK request and doesn’t get a reply (e.g., network timeout, retransmission), it will resend the same LOCK with:
same filehandle
same offset/length
same lock_owner
same seqid
Server behavior
NFSv4 is idempotent for stateful operations.
The server checks (lock_owner, seqid) — if it’s a duplicate, it just returns the same success result again (or detects replay).
Case 2. Lock Reclaim (Server reboot / Grace period)
After a server reboot, the client must reclaim its previous locks.
During the grace period, the client re-sends all the locks it held before:
LOCK offset=0 length=100 reclaim=TRUE
So you may see what looks like a “duplicate lock” in the trace — same region, same file, same owner — but it’s actually the reclaim phase.
Case 3. Lock Merge / Refresh (Client-level POSIX emulation)
On POSIX systems, fcntl(F_SETLK) or flock() may cause:
Multiple overlapping locks,
Re-locking the same range,
Or converting lock types (READ → WRITE).
The NFS client (e.g., Linux kernel lockd or nfsd) translates those POSIX calls into NFSv4 LOCK requests.
So, when an application does this:
fcntl(fd, F_SETLK, lock {READ, offset=0, len=100});
fcntl(fd, F_SETLK, lock {READ, offset=0, len=100}); // same region again
The NFS client might:
Combine them (lock coalescing), or
Send another identical LOCK to refresh or confirm.
Case 4. Multiple Lock Owners (Different processes on same client)
NFSv4 identifies locks by (clientid, lock_owner) pair — not just the file.
If two processes on the same host (with different lock_owners) both lock the same region, you’ll see:
LOCK owner=A offset=0 len=100
LOCK owner=B offset=0 len=100
They look identical in offset, fileid, length — but the lock_owner differs.
Wireshark may not always show lock_owner clearly, so it looks like “duplicates”.
Case 5. Lock Upgrade or Downgrade
Client might send the same offset/length but with different lock type:
READ → WRITE
WRITE → READ
These appear identical except for type, but they are actually lock conversions.
✅ Example:
LOCK type=READ offset=0 len=100
LOCK type=WRITE offset=0 len=100