Skip to content

Commit

Permalink
unittest: add lock random test (#4929)
Browse files Browse the repository at this point in the history
Signed-off-by: jiefenghuang <jiefeng@juicedata.io>
  • Loading branch information
jiefenghuang authored Jun 13, 2024
1 parent 8aa2dab commit 398974d
Showing 1 changed file with 254 additions and 3 deletions.
257 changes: 254 additions & 3 deletions pkg/meta/random_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (

aclAPI "github.com/juicedata/juicefs/pkg/acl"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
)

type tSlice struct {
Expand Down Expand Up @@ -66,9 +67,10 @@ type tNode struct {
target string
xattrs map[string][]byte
quota *tQuota

accACL *aclAPI.Rule
defACL *aclAPI.Rule
flocks map[ownerKey]byte
plocks map[ownerKey][]plockRecord
accACL *aclAPI.Rule
defACL *aclAPI.Rule
}

func (n *tNode) accessMode(uid uint32, gids []uint32) uint8 {
Expand Down Expand Up @@ -116,10 +118,12 @@ func (n *tNode) stickyAccess(child *tNode, uid uint32) bool {
type fsMachine struct {
nodes map[Ino]*tNode
meta Meta
sid uint64
ctx Context
}

func (m *fsMachine) Init(t *rapid.T) {
m.sid = rapid.Uint64().Draw(t, "sid")
m.nodes = make(map[Ino]*tNode)
m.nodes[1] = &tNode{
_type: TypeDirectory,
Expand All @@ -135,6 +139,7 @@ func (m *fsMachine) Init(t *rapid.T) {
if err := m.meta.Init(testFormat(), true); err != nil {
t.Fatalf("initialize failed: %s", err)
}
m.meta.getBase().sid = m.sid
registry := prometheus.NewRegistry() // replace default so only JuiceFS metrics are exposed
registerer := prometheus.WrapRegistererWithPrefix("juicefs_",
prometheus.WrapRegistererWith(prometheus.Labels{"mp": "virtual-mp", "vol_name": "test-vol"}, registry))
Expand Down Expand Up @@ -1884,7 +1889,253 @@ func (m *fsMachine) Chown(t *rapid.T) {
}
}

func (m *fsMachine) flock(inode Ino, owner uint64, typ uint32) syscall.Errno {
n := m.nodes[inode]
if n == nil {
return syscall.ENOENT
}
// m.openfiles[inode] = true
if n.flocks == nil {
n.flocks = make(map[ownerKey]byte)
}
lowner := ownerKey{Sid: m.sid, Owner: owner}
switch typ {
case F_UNLCK:
delete(n.flocks, lowner)
case F_RDLCK:
for o, l := range n.flocks {
if l == 'W' && o != lowner {
return syscall.EAGAIN
}
}
n.flocks[lowner] = 'R'
case F_WRLCK:
for o := range n.flocks {
if o == lowner {
continue
}
return syscall.EAGAIN
}
n.flocks[lowner] = 'W'
default:
return syscall.EINVAL
}
return 0
}

func (m *fsMachine) Flock(t *rapid.T) {
inode := m.pickNode(t)
owner := rapid.Uint64().Draw(t, "owner")
typ := rapid.Uint32Range(0, 3).Draw(t, "typ")
st := m.flock(inode, owner, typ)
st2 := m.meta.Flock(m.ctx, inode, owner, typ, false)
if st != st2 {
t.Fatalf("expect %s but got %s", st2, st)
}

if st == 0 {
plocks1, flocks1, err1 := m.meta.ListLocks(m.ctx, inode)
plocks2, flocks2, err2 := m.listLocks(inode)
if err1 != nil && err2 == nil || err1 == nil && err2 != nil {
t.Fatalf("expect %s but got %s", err2, err1)
}
if err1 == nil {
sort.Slice(flocks1, func(i, j int) bool {
return flocks1[i].Owner < flocks1[j].Owner
})
sort.Slice(flocks2, func(i, j int) bool {
return flocks2[i].Owner < flocks2[j].Owner
})
if !reflect.DeepEqual(flocks1, flocks2) {
t.Fatalf("expect %+v but got %+v", flocks2, flocks1)
}
sort.Slice(plocks1, func(i, j int) bool {
if plocks1[i].Owner != plocks1[j].Owner {
return plocks1[i].Owner < plocks1[j].Owner
}
if plocks1[i].Start != plocks1[j].Start {
return plocks1[i].Start < plocks1[j].Start
}
return plocks1[i].End < plocks1[j].End
})
sort.Slice(plocks2, func(i, j int) bool {
if plocks2[i].Owner != plocks2[j].Owner {
return plocks2[i].Owner < plocks2[j].Owner
}
if plocks2[i].Start != plocks2[j].Start {
return plocks2[i].Start < plocks2[j].Start
}
return plocks2[i].End < plocks2[j].End
})
if !reflect.DeepEqual(plocks1, plocks2) {
t.Fatalf("expect %+v but got %+v", plocks2, plocks1)
}
}
}
}

func (m *fsMachine) listLocks(inode Ino) ([]PLockItem, []FLockItem, error) {
var flocks []FLockItem
var plocks []PLockItem
n := m.nodes[inode]
if n == nil {
return plocks, flocks, syscall.ENOENT
}
for o, l := range n.flocks {
flocks = append(flocks, FLockItem{ownerKey: ownerKey{
Sid: o.Sid,
Owner: o.Owner,
}, Type: string(l)})
}
for o, ls := range n.plocks {
for _, l := range ls {
plocks = append(plocks, PLockItem{ownerKey: ownerKey{
Sid: o.Sid,
Owner: o.Owner,
}, plockRecord: l})
}
}
return plocks, flocks, nil
}

func (m *fsMachine) ListLocks(t *rapid.T) {
inode := m.pickNode(t)
plocks1, flocks1, err1 := m.meta.ListLocks(m.ctx, inode)
plocks2, flocks2, err2 := m.listLocks(inode)
if err1 != nil && err2 == nil || err1 == nil && err2 != nil {
t.Fatalf("expect %s but got %s", err2, err1)
}
if err1 == nil {
// sort flocks by owner
sort.Slice(flocks1, func(i, j int) bool {
return flocks1[i].Owner < flocks1[j].Owner
})
sort.Slice(flocks2, func(i, j int) bool {
return flocks2[i].Owner < flocks2[j].Owner
})
if !reflect.DeepEqual(flocks1, flocks2) {
t.Fatalf("expect %+v but got %+v", flocks2, flocks1)
}
// sort plocks by owner
sort.Slice(plocks1, func(i, j int) bool {
return plocks1[i].Owner < plocks1[j].Owner
})
sort.Slice(plocks2, func(i, j int) bool {
return plocks2[i].Owner < plocks2[j].Owner
})
if !reflect.DeepEqual(plocks1, plocks2) {
t.Fatalf("expect %+v but got %+v", plocks2, plocks1)
}
}
}

func (m *fsMachine) getlk(inode Ino, owner uint64, ltype *uint32, start *uint64, end *uint64, pid *uint32) syscall.Errno {
if *ltype == F_UNLCK {
*start = 0
*end = 0
*pid = 0
return 0
}
n := m.nodes[inode]
if n == nil {
return syscall.ENOENT
}
for o, ls := range n.plocks {
for _, l := range ls {
if o.Owner != owner && (*ltype == F_WRLCK || l.Type == F_WRLCK) && *end >= l.Start && *start <= l.End {
*ltype = l.Type
*start = l.Start
*end = l.End
if o.Sid == m.sid {
*pid = l.Pid
} else {
*pid = 0
}
return 0
}
}
}
*ltype = F_UNLCK
*start = 0
*end = 0
*pid = 0
return 0
}

func (m *fsMachine) Getlk(t *rapid.T) {
inode := m.pickNode(t)
owner := rapid.Uint64().Draw(t, "owner")
ltype := rapid.Uint32Range(0, 2).Draw(t, "ltype")
start := rapid.Uint64Range(0, 500<<20).Draw(t, "start")
length := rapid.Uint64Range(1, 500<<20).Draw(t, "len")
end := start + length - 1

var pid1, pid2 uint32
ftype1, ftype2 := ltype, ltype
fstart1, fstart2 := start, start
fend1, fend2 := end, end
st := m.getlk(inode, owner, &ftype1, &fstart1, &fend1, &pid1)
st2 := m.meta.Getlk(m.ctx, inode, owner, &ftype2, &fstart2, &fend2, &pid2)
if st != st2 {
t.Fatalf("expect %s but got %s", st2, st)
}
if st == 0 && ltype != F_UNLCK && (ftype1 == F_UNLCK && ftype2 != F_UNLCK || ftype1 != F_UNLCK && ftype2 == F_UNLCK) {
t.Fatalf("status not right, %d %d", ftype1, ftype2)
}
}

func (m *fsMachine) setlk(inode Ino, owner uint64, ltype uint32, start uint64, end uint64, pid uint32) syscall.Errno {
n := m.nodes[inode]
if n == nil {
return syscall.ENOENT
}
if ltype != F_UNLCK {
// m.openfiles[inode] = true
}
if n.plocks == nil {
n.plocks = make(map[ownerKey][]plockRecord)
}
lowner := ownerKey{Sid: m.sid, Owner: owner}
if ltype == F_UNLCK {
if n.plocks[lowner] == nil {
return 0
}
} else {
for o, ls := range n.plocks {
for _, l := range ls {
if o != lowner && (ltype == F_WRLCK || l.Type == F_WRLCK) && end >= l.Start && start <= l.End {
return syscall.EAGAIN
}
}
}
}
ls := updateLocks(n.plocks[lowner], plockRecord{ltype, pid, start, end})
if len(ls) == 0 {
delete(n.plocks, lowner)
} else {
n.plocks[lowner] = ls
}
return 0
}

func (m *fsMachine) Setlk(t *rapid.T) {
inode := m.pickNode(t)
owner := rapid.Uint64().Draw(t, "owner")
ltype := rapid.Uint32Range(0, 2).Draw(t, "ltype")
start := rapid.Uint64Range(0, 500<<20).Draw(t, "start")
len := rapid.Uint64Range(1, 500<<20).Draw(t, "len")
pid := rapid.Uint32Range(1, 10000).Draw(t, "pid")
var end = start + len - 1
st := m.meta.Setlk(m.ctx, inode, owner, false, ltype, start, end, pid)
st2 := m.setlk(inode, owner, ltype, start, end, pid)
if st != st2 {
t.Fatalf("expect %s but got %s", st2, st)
}
}

func TestFSOps(t *testing.T) {
logger.SetLevel(logrus.ErrorLevel)
defer logger.SetLevel(logrus.InfoLevel)
flag.Set("timeout", "10s")
flag.Set("rapid.steps", "200")
flag.Set("rapid.checks", "5000")
Expand Down

0 comments on commit 398974d

Please sign in to comment.