Files
seaweedfs/weed/mount/weedfs_posix_lock_routed_test.go
T
Chris Lu 3481f13f54 mount: route POSIX advisory locks to the owner filer under -dlm (#9669)
With -dlm, GetLk/SetLk/SetLkw and the flush/release cleanup paths go to
the inode's owner filer via the PosixLock RPC instead of the local table,
so flock/fcntl are honored across mounts. Advisory locking rides the same
switch as whole-file write coordination — and is therefore off under
writeback cache, which implies single-writer. Keys are the inode identity
(HardLinkId else path); SetLkw is client-side polling with the FUSE cancel
channel (no server wait queue); a per-mount session id namespaces owners;
a local hint avoids a release RPC on every close. Background unlock/release
RPCs are bounded so a stuck filer can't hang close().
2026-05-24 23:56:37 -07:00

95 lines
2.2 KiB
Go

package mount
import (
"syscall"
"testing"
"time"
"github.com/seaweedfs/go-fuse/v2/fuse"
"github.com/seaweedfs/seaweedfs/weed/filer/posixlock"
)
func TestPosixLockTypeMapping(t *testing.T) {
cases := []struct {
sys uint32
wire uint32
}{
{syscall.F_RDLCK, posixlock.Read},
{syscall.F_WRLCK, posixlock.Write},
{syscall.F_UNLCK, posixlock.Unlock},
}
for _, c := range cases {
if got := posixLockTypeToWire(c.sys); got != c.wire {
t.Errorf("toWire(%d) = %d, want %d", c.sys, got, c.wire)
}
if got := posixLockTypeFromWire(c.wire); got != c.sys {
t.Errorf("fromWire(%d) = %d, want %d", c.wire, got, c.sys)
}
}
}
func TestPosixPollAcquireGrantedImmediately(t *testing.T) {
calls := 0
st := posixPollAcquire(nil, func() (bool, error) { calls++; return true, nil })
if st != fuse.OK || calls != 1 {
t.Fatalf("immediate grant: status=%v calls=%d", st, calls)
}
}
func TestPosixPollAcquireRetriesThenGrants(t *testing.T) {
calls := 0
st := posixPollAcquire(nil, func() (bool, error) {
calls++
return calls >= 3, nil
})
if st != fuse.OK || calls != 3 {
t.Fatalf("retry then grant: status=%v calls=%d", st, calls)
}
}
func TestPosixPollAcquireError(t *testing.T) {
if st := posixPollAcquire(nil, func() (bool, error) { return false, syscall.EIO }); st != fuse.EIO {
t.Fatalf("error should map to EIO, got %v", st)
}
}
func TestPosixPollAcquireCancel(t *testing.T) {
cancel := make(chan struct{})
close(cancel)
done := make(chan fuse.Status, 1)
go func() {
done <- posixPollAcquire(cancel, func() (bool, error) { return false, nil })
}()
select {
case st := <-done:
if st != fuse.EINTR {
t.Fatalf("cancel should map to EINTR, got %v", st)
}
case <-time.After(2 * time.Second):
t.Fatal("poll did not return on cancel")
}
}
func TestPosixLockHint(t *testing.T) {
h := newPosixLockHint()
if h.has(1, 2) {
t.Fatal("empty hint should not report a lock")
}
h.add(1, 2)
h.add(1, 3)
if !h.has(1, 2) || !h.has(1, 3) {
t.Fatal("added owners should be reported")
}
h.drop(1, 2)
if h.has(1, 2) {
t.Fatal("dropped owner should be gone")
}
if !h.has(1, 3) {
t.Fatal("sibling owner should remain")
}
h.drop(1, 3)
if _, ok := h.m[1]; ok {
t.Fatal("inode entry should be removed when its last owner drops")
}
}