Skip to content

Commit

Permalink
cmd/config: add option to enable/disable dir stats (#3686)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hexilee authored May 29, 2023
1 parent 03b63f1 commit 51fed1e
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 26 deletions.
25 changes: 25 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/juicedata/juicefs/pkg/meta"
"github.com/juicedata/juicefs/pkg/version"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -80,6 +81,10 @@ func configManagementFlags() []cli.Flag {
Name: "download-limit",
Usage: "default bandwidth limit of the volume for download in Mbps",
},
&cli.BoolFlag{
Name: "dir-stats",
Usage: "enable dir stats, which is necessary for fast summary and dir quota",
},
})
}

Expand Down Expand Up @@ -130,6 +135,7 @@ func config(ctx *cli.Context) error {
return nil
}

originDirStats := format.DirStats
var quota, storage, trash, clientVer bool
var msg strings.Builder
encrypted := format.KeyEncrypted
Expand Down Expand Up @@ -211,6 +217,11 @@ func config(ctx *cli.Context) error {
format.TrashDays = new
trash = true
}
case "dir-stats":
if new := ctx.Bool(flag); new != format.DirStats {
msg.WriteString(fmt.Sprintf("%10s: %t -> %t\n", flag, format.DirStats, new))
format.DirStats = new
}
case "min-client-version":
if new := ctx.String(flag); new != format.MinClientVersion {
if version.Parse(new) == nil {
Expand Down Expand Up @@ -266,6 +277,20 @@ func config(ctx *cli.Context) error {
return fmt.Errorf("Aborted.")
}
}
if originDirStats && !format.DirStats {
qs := make(map[string]*meta.Quota)
err := m.HandleQuota(meta.Background, meta.QuotaList, "", qs, false, false)
if err != nil {
return errors.Wrap(err, "list quotas")
}
if len(qs) != 0 {
paths := make([]string, 0, len(qs))
for path := range qs {
paths = append(paths, path)
}
return fmt.Errorf("cannot disable dir stats when there are still %d dir quotas: %v", len(qs), paths)
}
}
if clientVer && format.CheckVersion() != nil {
warn("Clients with the same version of this will be rejected after modification.")
if !yes && !userConfirmed() {
Expand Down
1 change: 1 addition & 0 deletions cmd/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ func format(c *cli.Context) error {
BlockSize: fixObjectSize(c.Int("block-size")),
Compression: c.String("compress"),
TrashDays: c.Int("trash-days"),
DirStats: true,
MetaVersion: meta.MaxVersion,
}
if format.AccessKey == "" && os.Getenv("ACCESS_KEY") != "" {
Expand Down
1 change: 1 addition & 0 deletions cmd/mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func Test_exposeMetrics(t *testing.T) {
Name: "test",
BlockSize: 4096,
Capacity: 1 << 30,
DirStats: true,
}
_ = client.Init(format, true)
var appCtx *cli.Context
Expand Down
1 change: 1 addition & 0 deletions cmd/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func TestJFS(t *testing.T) {
Name: "test",
BlockSize: 4096,
Capacity: 1 << 30,
DirStats: true,
}
_ = m.Init(format, true)
var conf = vfs.Config{
Expand Down
1 change: 1 addition & 0 deletions pkg/fs/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ func createTestFS(t *testing.T) *FileSystem {
Name: "test",
BlockSize: 4096,
Capacity: 1 << 30,
DirStats: true,
}
_ = m.Init(format, true)
var conf = vfs.Config{
Expand Down
1 change: 1 addition & 0 deletions pkg/fuse/fuse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func format(url string) {
Storage: "file",
Bucket: os.TempDir() + "/",
BlockSize: 4096,
DirStats: true,
}
err := m.Init(format, true)
if err != nil {
Expand Down
46 changes: 46 additions & 0 deletions pkg/meta/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type engine interface {
doRefreshSession() error
doFindStaleSessions(limit int) ([]uint64, error) // limit < 0 means all
doCleanStaleSession(sid uint64) error
doInit(format *Format, force bool) error

scanAllChunks(ctx Context, ch chan<- cchunk, bar *utils.Bar) error
compactChunk(inode Ino, indx uint32, force bool)
Expand Down Expand Up @@ -332,6 +333,9 @@ func (m *baseMeta) GetDirStat(ctx Context, inode Ino) (stat *dirStat, st syscall
}

func (m *baseMeta) updateDirStat(ctx Context, ino Ino, length, space, inodes int64) {
if !m.GetFormat().DirStats {
return
}
m.dirStatsLock.Lock()
defer m.dirStatsLock.Unlock()
stat := m.dirStats[ino]
Expand All @@ -346,6 +350,9 @@ func (m *baseMeta) updateParentStat(ctx Context, inode, parent Ino, length, spac
return
}
m.en.updateStats(space, 0)
if !m.GetFormat().DirStats {
return
}
if parent > 0 {
m.updateDirStat(ctx, parent, length, space, 0)
m.updateDirQuota(ctx, parent, space, 0)
Expand All @@ -371,6 +378,9 @@ func (m *baseMeta) flushDirStat() {
}

func (m *baseMeta) doFlushDirStat() {
if !m.GetFormat().DirStats {
return
}
m.dirStatsLock.Lock()
if len(m.dirStats) == 0 {
m.dirStatsLock.Unlock()
Expand Down Expand Up @@ -610,6 +620,9 @@ func (m *baseMeta) checkQuota(ctx Context, space, inodes int64, parents ...Ino)
if inodes > 0 && m.fmt.Inodes > 0 && atomic.LoadInt64(&m.usedInodes)+atomic.LoadInt64(&m.newInodes)+inodes > int64(m.fmt.Inodes) {
return syscall.ENOSPC
}
if !m.GetFormat().DirStats {
return 0
}
for _, ino := range parents {
if m.checkDirQuota(ctx, ino, space, inodes) {
return syscall.EDQUOT
Expand Down Expand Up @@ -663,6 +676,9 @@ func (m *baseMeta) getDirParent(ctx Context, inode Ino) (Ino, syscall.Errno) {
}

func (m *baseMeta) hasDirQuota(ctx Context, inode Ino) bool {
if !m.GetFormat().DirStats {
return false
}
var q *Quota
var st syscall.Errno
for {
Expand All @@ -684,6 +700,9 @@ func (m *baseMeta) hasDirQuota(ctx Context, inode Ino) bool {
}

func (m *baseMeta) checkDirQuota(ctx Context, inode Ino, space, inodes int64) bool {
if !m.GetFormat().DirStats {
return false
}
var q *Quota
var st syscall.Errno
for {
Expand All @@ -705,6 +724,9 @@ func (m *baseMeta) checkDirQuota(ctx Context, inode Ino, space, inodes int64) bo
}

func (m *baseMeta) updateDirQuota(ctx Context, inode Ino, space, inodes int64) {
if !m.GetFormat().DirStats {
return
}
var q *Quota
var st syscall.Errno
for {
Expand All @@ -729,6 +751,9 @@ func (m *baseMeta) flushQuotas() {
var newSpace, newInodes int64
for {
time.Sleep(time.Second * 3)
if !m.GetFormat().DirStats {
continue
}
m.quotaMu.RLock()
for ino, q := range m.dirQuotas {
newSpace = atomic.LoadInt64(&q.newSpace)
Expand Down Expand Up @@ -761,6 +786,10 @@ func (m *baseMeta) flushQuotas() {
}
}

func (m *baseMeta) Init(format *Format, force bool) error {
return m.en.doInit(format, force)
}

func (m *baseMeta) HandleQuota(ctx Context, cmd uint8, dpath string, quotas map[string]*Quota, strict, repair bool) error {
var inode Ino
if cmd != QuotaList {
Expand All @@ -774,6 +803,16 @@ func (m *baseMeta) HandleQuota(ctx Context, cmd uint8, dpath string, quotas map[

switch cmd {
case QuotaSet:
format, err := m.Load(false)
if err != nil {
return errors.Wrap(err, "load format")
}
if !format.DirStats {
format.DirStats = true
if err := m.en.doInit(format, false); err != nil {
return err
}
}
q, err := m.en.doGetQuota(ctx, inode)
if err != nil {
return err
Expand Down Expand Up @@ -2035,6 +2074,13 @@ func (m *baseMeta) resolve(ctx Context, dpath string, inode *Ino) syscall.Errno
}

func (m *baseMeta) GetFormat() Format {
if m.fmt == nil {
var err error
m.fmt, err = m.Load(false)
if err != nil {
logger.Fatalf("Load format: %s", err)
}
}
return *m.fmt
}

Expand Down
40 changes: 23 additions & 17 deletions pkg/meta/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ func testConfig() *Config {
return conf
}

func testFormat() *Format {
return &Format{Name: "test", DirStats: true}
}

func TestRedisClient(t *testing.T) {
m, err := newRedisMeta("redis", "127.0.0.1:6379/10", testConfig())
if err != nil || m.Name() != "redis" {
Expand Down Expand Up @@ -153,7 +157,7 @@ func testMetaClient(t *testing.T, m Meta) {
t.Fatalf("getattr root: %s", st)
}

if err := m.Init(&Format{Name: "test"}, true); err != nil {
if err := m.Init(testFormat(), true); err != nil {
t.Fatalf("initialize failed: %s", err)
}
if err := m.Init(&Format{Name: "test2"}, false); err == nil { // not allowed
Expand Down Expand Up @@ -700,7 +704,6 @@ func testMetaClient(t *testing.T, m Meta) {
}

func testStickyBit(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
ctx := Background
var sticky, normal, inode Ino
var attr = &Attr{}
Expand Down Expand Up @@ -767,7 +770,6 @@ func testStickyBit(t *testing.T, m Meta) {
}

func testListLocks(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
ctx := Background
var inode Ino
var attr = &Attr{}
Expand Down Expand Up @@ -842,7 +844,6 @@ func testListLocks(t *testing.T, m Meta) {
}

func testLocks(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
ctx := Background
var inode Ino
var attr = &Attr{}
Expand Down Expand Up @@ -979,7 +980,6 @@ func testLocks(t *testing.T, m Meta) {
}

func testRemove(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
ctx := Background
var inode, parent Ino
var attr = &Attr{}
Expand Down Expand Up @@ -1021,7 +1021,6 @@ func testRemove(t *testing.T, m Meta) {
}

func testCaseIncensi(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
ctx := Background
var inode Ino
var attr = &Attr{}
Expand Down Expand Up @@ -1070,9 +1069,16 @@ type compactor interface {

func testCompaction(t *testing.T, m Meta, trash bool) {
if trash {
_ = m.Init(&Format{Name: "test", TrashDays: 1}, false)
format := testFormat()
format.TrashDays = 1
_ = m.Init(format, false)
defer func() {
if err := m.Init(testFormat(), false); err != nil {
t.Fatalf("init: %v", err)
}
}()
} else {
_ = m.Init(&Format{Name: "test"}, false)
_ = m.Init(testFormat(), false)
}
var l sync.Mutex
deleted := make(map[uint64]int)
Expand Down Expand Up @@ -1186,7 +1192,6 @@ func testConcurrentWrite(t *testing.T, m Meta) {
m.OnMsg(CompactChunk, func(args ...interface{}) error {
return nil
})
_ = m.Init(&Format{Name: "test"}, false)

ctx := Background
var inode Ino
Expand Down Expand Up @@ -1289,7 +1294,6 @@ func testCopyFileRange(t *testing.T, m Meta) {
m.OnMsg(DeleteSlice, func(args ...interface{}) error {
return nil
})
_ = m.Init(&Format{Name: "test"}, false)

ctx := Background
var iin, iout Ino
Expand Down Expand Up @@ -1339,7 +1343,6 @@ func testCopyFileRange(t *testing.T, m Meta) {
}

func testCloseSession(t *testing.T, m Meta) {
_ = m.Init(&Format{Name: "test"}, false)
if err := m.NewSession(); err != nil {
t.Fatalf("new session: %s", err)
}
Expand Down Expand Up @@ -1399,9 +1402,16 @@ func testCloseSession(t *testing.T, m Meta) {
}

func testTrash(t *testing.T, m Meta) {
if err := m.Init(&Format{Name: "test", TrashDays: 1}, false); err != nil {
t.Fatalf("init: %s", err)
format := testFormat()
format.TrashDays = 1
if err := m.Init(format, false); err != nil {
t.Fatalf("init: %v", err)
}
defer func() {
if err := m.Init(testFormat(), false); err != nil {
t.Fatalf("init: %v", err)
}
}()
ctx := Background
var inode, parent Ino
var attr = &Attr{}
Expand Down Expand Up @@ -1495,9 +1505,6 @@ func testTrash(t *testing.T, m Meta) {
}

func testParents(t *testing.T, m Meta) {
if err := m.Init(&Format{Name: "test"}, false); err != nil {
t.Fatalf("init: %s", err)
}
ctx := Background
var inode, parent Ino
var attr = &Attr{}
Expand Down Expand Up @@ -2001,7 +2008,6 @@ func testCheckAndRepair(t *testing.T, m Meta) {
func testDirStat(t *testing.T, m Meta) {
testDir := "testDirStat"
var testInode Ino

// test empty dir
if st := m.Mkdir(Background, RootInode, testDir, 0640, 022, 0, &testInode, nil); st != 0 {
t.Fatalf("mkdir: %s", st)
Expand Down
2 changes: 1 addition & 1 deletion pkg/meta/benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ func benchmarkData(b *testing.B, m Meta) {
}

func benchmarkAll(b *testing.B, m Meta) {
_ = m.Init(&Format{Name: "benchmarkAll"}, true)
_ = m.Init(&Format{Name: "benchmarkAll", DirStats: true}, true)
_ = m.NewSession()
benchmarkDir(b, m)
benchmarkFile(b, m)
Expand Down
1 change: 1 addition & 0 deletions pkg/meta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Format struct {
MetaVersion int `json:",omitempty"`
MinClientVersion string `json:",omitempty"`
MaxClientVersion string `json:",omitempty"`
DirStats bool `json:",omitempty"`
}

func (f *Format) update(old *Format, force bool) error {
Expand Down
Loading

0 comments on commit 51fed1e

Please sign in to comment.