Unverified 提交 07feeec9 作者: Whyrusleeping 提交者: GitHub

Merge pull request #5010 from ipfs/feat/diskusage2

Efficient "repo stat" (DiskUsage) and "--size-only" flag
...@@ -150,14 +150,20 @@ var repoStatCmd = &cmds.Command{ ...@@ -150,14 +150,20 @@ var repoStatCmd = &cmds.Command{
Helptext: cmdkit.HelpText{ Helptext: cmdkit.HelpText{
Tagline: "Get stats for the currently used repo.", Tagline: "Get stats for the currently used repo.",
ShortDescription: ` ShortDescription: `
'ipfs repo stat' is a plumbing command that will scan the local 'ipfs repo stat' provides information about the local set of
set of stored objects and print repo statistics. It outputs to stdout: stored objects. It outputs:
RepoSize int Size in bytes that the repo is currently taking.
StorageMax string Maximum datastore size (from configuration)
NumObjects int Number of objects in the local repo. NumObjects int Number of objects in the local repo.
RepoPath string The path to the repo being currently used. RepoPath string The path to the repo being currently used.
RepoSize int Size in bytes that the repo is currently taking.
Version string The repo version. Version string The repo version.
`, `,
}, },
Options: []cmdkit.Option{
cmdkit.BoolOption("size-only", "Only report RepoSize and StorageMax."),
cmdkit.BoolOption("human", "Output sizes in MiB."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) { Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
n, err := GetNode(env) n, err := GetNode(env)
if err != nil { if err != nil {
...@@ -165,18 +171,28 @@ Version string The repo version. ...@@ -165,18 +171,28 @@ Version string The repo version.
return return
} }
stat, err := corerepo.RepoStat(n, req.Context) sizeOnly, _ := req.Options["size-only"].(bool)
if sizeOnly {
sizeStat, err := corerepo.RepoSize(req.Context, n)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
cmds.EmitOnce(res, &corerepo.Stat{
SizeStat: sizeStat,
})
return
}
stat, err := corerepo.RepoStat(req.Context, n)
if err != nil { if err != nil {
res.SetError(err, cmdkit.ErrNormal) res.SetError(err, cmdkit.ErrNormal)
return return
} }
cmds.EmitOnce(res, stat) cmds.EmitOnce(res, &stat)
},
Options: []cmdkit.Option{
cmdkit.BoolOption("human", "Output RepoSize in MiB."),
}, },
Type: corerepo.Stat{}, Type: &corerepo.Stat{},
Encoders: cmds.EncoderMap{ Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error { cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
stat, ok := v.(*corerepo.Stat) stat, ok := v.(*corerepo.Stat)
...@@ -184,31 +200,34 @@ Version string The repo version. ...@@ -184,31 +200,34 @@ Version string The repo version.
return e.TypeErr(stat, v) return e.TypeErr(stat, v)
} }
human, _ := req.Options["human"].(bool)
wtr := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) wtr := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
defer wtr.Flush()
fmt.Fprintf(wtr, "NumObjects:\t%d\n", stat.NumObjects) human, _ := req.Options["human"].(bool)
sizeInMiB := stat.RepoSize / (1024 * 1024) sizeOnly, _ := req.Options["size-only"].(bool)
if human && sizeInMiB > 0 {
fmt.Fprintf(wtr, "RepoSize (MiB):\t%d\n", sizeInMiB) printSize := func(name string, size uint64) {
} else { sizeInMiB := size / (1024 * 1024)
fmt.Fprintf(wtr, "RepoSize:\t%d\n", stat.RepoSize) if human && sizeInMiB > 0 {
} fmt.Fprintf(wtr, "%s (MiB):\t%d\n", name, sizeInMiB)
if stat.StorageMax != corerepo.NoLimit {
maxSizeInMiB := stat.StorageMax / (1024 * 1024)
if human && maxSizeInMiB > 0 {
fmt.Fprintf(wtr, "StorageMax (MiB):\t%d\n", maxSizeInMiB)
} else { } else {
fmt.Fprintf(wtr, "StorageMax:\t%d\n", stat.StorageMax) fmt.Fprintf(wtr, "%s:\t%d\n", name, size)
} }
} }
fmt.Fprintf(wtr, "RepoPath:\t%s\n", stat.RepoPath)
fmt.Fprintf(wtr, "Version:\t%s\n", stat.Version)
wtr.Flush()
return nil if !sizeOnly {
fmt.Fprintf(wtr, "NumObjects:\t%d\n", stat.NumObjects)
}
printSize("RepoSize", stat.RepoSize)
printSize("StorageMax", stat.StorageMax)
if !sizeOnly {
fmt.Fprintf(wtr, "RepoPath:\t%s\n", stat.RepoPath)
fmt.Fprintf(wtr, "Version:\t%s\n", stat.Version)
}
return nil
}), }),
}, },
} }
......
...@@ -5,34 +5,40 @@ import ( ...@@ -5,34 +5,40 @@ import (
"math" "math"
context "context" context "context"
"github.com/ipfs/go-ipfs/core" "github.com/ipfs/go-ipfs/core"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
humanize "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize" humanize "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize"
) )
// SizeStat wraps information about the repository size and its limit.
type SizeStat struct {
RepoSize uint64 // size in bytes
StorageMax uint64 // size in bytes
}
// Stat wraps information about the objects stored on disk.
type Stat struct { type Stat struct {
SizeStat
NumObjects uint64 NumObjects uint64
RepoSize uint64 // size in bytes
RepoPath string RepoPath string
Version string Version string
StorageMax uint64 // size in bytes
} }
// NoLimit represents the value for unlimited storage // NoLimit represents the value for unlimited storage
const NoLimit uint64 = math.MaxUint64 const NoLimit uint64 = math.MaxUint64
func RepoStat(n *core.IpfsNode, ctx context.Context) (*Stat, error) { // RepoStat returns a *Stat object with all the fields set.
r := n.Repo func RepoStat(ctx context.Context, n *core.IpfsNode) (Stat, error) {
sizeStat, err := RepoSize(ctx, n)
usage, err := r.GetStorageUsage()
if err != nil { if err != nil {
return nil, err return Stat{}, err
} }
allKeys, err := n.Blockstore.AllKeysChan(ctx) allKeys, err := n.Blockstore.AllKeysChan(ctx)
if err != nil { if err != nil {
return nil, err return Stat{}, err
} }
count := uint64(0) count := uint64(0)
...@@ -42,27 +48,44 @@ func RepoStat(n *core.IpfsNode, ctx context.Context) (*Stat, error) { ...@@ -42,27 +48,44 @@ func RepoStat(n *core.IpfsNode, ctx context.Context) (*Stat, error) {
path, err := fsrepo.BestKnownPath() path, err := fsrepo.BestKnownPath()
if err != nil { if err != nil {
return nil, err return Stat{}, err
} }
return Stat{
SizeStat: SizeStat{
RepoSize: sizeStat.RepoSize,
StorageMax: sizeStat.StorageMax,
},
NumObjects: count,
RepoPath: path,
Version: fmt.Sprintf("fs-repo@%d", fsrepo.RepoVersion),
}, nil
}
// RepoSize returns a *Stat object with the RepoSize and StorageMax fields set.
func RepoSize(ctx context.Context, n *core.IpfsNode) (SizeStat, error) {
r := n.Repo
cfg, err := r.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return nil, err return SizeStat{}, err
}
usage, err := r.GetStorageUsage()
if err != nil {
return SizeStat{}, err
} }
storageMax := NoLimit storageMax := NoLimit
if cfg.Datastore.StorageMax != "" { if cfg.Datastore.StorageMax != "" {
storageMax, err = humanize.ParseBytes(cfg.Datastore.StorageMax) storageMax, err = humanize.ParseBytes(cfg.Datastore.StorageMax)
if err != nil { if err != nil {
return nil, err return SizeStat{}, err
} }
} }
return &Stat{ return SizeStat{
NumObjects: count,
RepoSize: usage, RepoSize: usage,
RepoPath: path,
Version: fmt.Sprintf("fs-repo@%d", fsrepo.RepoVersion),
StorageMax: storageMax, StorageMax: storageMax,
}, nil }, nil
} }
...@@ -27,6 +27,7 @@ import ( ...@@ -27,6 +27,7 @@ import (
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
lockfile "gx/ipfs/QmYzCZUe9CBDkyPNPcRNqXQK8KKhtUfXvc88PkFujAEJPe/go-fs-lock" lockfile "gx/ipfs/QmYzCZUe9CBDkyPNPcRNqXQK8KKhtUfXvc88PkFujAEJPe/go-fs-lock"
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log" logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore"
) )
// LockFile is the filename of the repo lock, relative to config dir // LockFile is the filename of the repo lock, relative to config dir
...@@ -674,29 +675,7 @@ func (r *FSRepo) Datastore() repo.Datastore { ...@@ -674,29 +675,7 @@ func (r *FSRepo) Datastore() repo.Datastore {
// GetStorageUsage computes the storage space taken by the repo in bytes // GetStorageUsage computes the storage space taken by the repo in bytes
func (r *FSRepo) GetStorageUsage() (uint64, error) { func (r *FSRepo) GetStorageUsage() (uint64, error) {
pth, err := config.PathRoot() return ds.DiskUsage(r.Datastore())
if err != nil {
return 0, err
}
pth, err = filepath.EvalSymlinks(pth)
if err != nil {
log.Debugf("filepath.EvalSymlinks error: %s", err)
return 0, err
}
var du uint64
err = filepath.Walk(pth, func(p string, f os.FileInfo, err error) error {
if err != nil {
log.Debugf("filepath.Walk error: %s", err)
return nil
}
if f != nil {
du += uint64(f.Size())
}
return nil
})
return du, err
} }
func (r *FSRepo) SwarmKey() ([]byte, error) { func (r *FSRepo) SwarmKey() ([]byte, error) {
......
...@@ -245,6 +245,18 @@ test_expect_success "repo stats are updated correctly" ' ...@@ -245,6 +245,18 @@ test_expect_success "repo stats are updated correctly" '
test $(get_field_num "RepoSize" repo-stats-2) -ge $(get_field_num "RepoSize" repo-stats) test $(get_field_num "RepoSize" repo-stats-2) -ge $(get_field_num "RepoSize" repo-stats)
' '
test_expect_success "'ipfs repo stat --size-only' succeeds" '
ipfs repo stat --size-only > repo-stats-size-only
'
test_expect_success "repo stats came out correct for --size-only" '
grep "RepoSize" repo-stats-size-only &&
grep "StorageMax" repo-stats-size-only &&
grep -v "RepoPath" repo-stats-size-only &&
grep -v "NumObjects" repo-stats-size-only &&
grep -v "Version" repo-stats-size-only
'
test_expect_success "'ipfs repo version' succeeds" ' test_expect_success "'ipfs repo version' succeeds" '
ipfs repo version > repo-version ipfs repo version > repo-version
' '
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论