Merge pull request #5624 from filecoin-project/feat/miner-storage-limit

Configurable storage path storage limit
This commit is contained in:
Łukasz Magiera 2021-03-10 11:15:28 +01:00 committed by GitHub
commit 40fdf6c180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 171 additions and 53 deletions

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/docker/go-units"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -46,6 +47,10 @@ var storageAttachCmd = &cli.Command{
Name: "store", Name: "store",
Usage: "(for init) use path for long-term storage", Usage: "(for init) use path for long-term storage",
}, },
&cli.StringFlag{
Name: "max-storage",
Usage: "(for init) limit storage space for sectors (expensive for very large paths!)",
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetWorkerAPI(cctx) nodeApi, closer, err := lcli.GetWorkerAPI(cctx)
@ -79,11 +84,20 @@ var storageAttachCmd = &cli.Command{
return err return err
} }
var maxStor int64
if cctx.IsSet("max-storage") {
maxStor, err = units.RAMInBytes(cctx.String("max-storage"))
if err != nil {
return xerrors.Errorf("parsing max-storage: %w", err)
}
}
cfg := &stores.LocalStorageMeta{ cfg := &stores.LocalStorageMeta{
ID: stores.ID(uuid.New().String()), ID: stores.ID(uuid.New().String()),
Weight: cctx.Uint64("weight"), Weight: cctx.Uint64("weight"),
CanSeal: cctx.Bool("seal"), CanSeal: cctx.Bool("seal"),
CanStore: cctx.Bool("store"), CanStore: cctx.Bool("store"),
MaxStorage: uint64(maxStor),
} }
if !(cfg.CanStore || cfg.CanSeal) { if !(cfg.CanStore || cfg.CanSeal) {

View File

@ -12,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/docker/go-units"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
@ -88,6 +89,10 @@ over time
Name: "store", Name: "store",
Usage: "(for init) use path for long-term storage", Usage: "(for init) use path for long-term storage",
}, },
&cli.StringFlag{
Name: "max-storage",
Usage: "(for init) limit storage space for sectors (expensive for very large paths!)",
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
@ -121,11 +126,20 @@ over time
return err return err
} }
var maxStor int64
if cctx.IsSet("max-storage") {
maxStor, err = units.RAMInBytes(cctx.String("max-storage"))
if err != nil {
return xerrors.Errorf("parsing max-storage: %w", err)
}
}
cfg := &stores.LocalStorageMeta{ cfg := &stores.LocalStorageMeta{
ID: stores.ID(uuid.New().String()), ID: stores.ID(uuid.New().String()),
Weight: cctx.Uint64("weight"), Weight: cctx.Uint64("weight"),
CanSeal: cctx.Bool("seal"), CanSeal: cctx.Bool("seal"),
CanStore: cctx.Bool("store"), CanStore: cctx.Bool("store"),
MaxStorage: uint64(maxStor),
} }
if !(cfg.CanStore || cfg.CanSeal) { if !(cfg.CanStore || cfg.CanSeal) {
@ -220,26 +234,66 @@ var storageListCmd = &cli.Command{
} }
ping := time.Now().Sub(pingStart) ping := time.Now().Sub(pingStart)
usedPercent := (st.Capacity - st.Available) * 100 / st.Capacity safeRepeat := func(s string, count int) string {
if count < 0 {
percCol := color.FgGreen return ""
switch { }
case usedPercent > 98: return strings.Repeat(s, count)
percCol = color.FgRed
case usedPercent > 90:
percCol = color.FgYellow
} }
var barCols = int64(50) var barCols = int64(50)
set := (st.Capacity - st.Available) * barCols / st.Capacity
used := (st.Capacity - (st.Available + st.Reserved)) * barCols / st.Capacity
reserved := set - used
bar := strings.Repeat("#", int(used)) + strings.Repeat("*", int(reserved)) + strings.Repeat(" ", int(barCols-set))
fmt.Printf("\t[%s] %s/%s %s\n", color.New(percCol).Sprint(bar), // filesystem use bar
types.SizeStr(types.NewInt(uint64(st.Capacity-st.Available))), {
types.SizeStr(types.NewInt(uint64(st.Capacity))), usedPercent := (st.Capacity - st.FSAvailable) * 100 / st.Capacity
color.New(percCol).Sprintf("%d%%", usedPercent))
percCol := color.FgGreen
switch {
case usedPercent > 98:
percCol = color.FgRed
case usedPercent > 90:
percCol = color.FgYellow
}
set := (st.Capacity - st.FSAvailable) * barCols / st.Capacity
used := (st.Capacity - (st.FSAvailable + st.Reserved)) * barCols / st.Capacity
reserved := set - used
bar := safeRepeat("#", int(used)) + safeRepeat("*", int(reserved)) + safeRepeat(" ", int(barCols-set))
desc := ""
if st.Max > 0 {
desc = " (filesystem)"
}
fmt.Printf("\t[%s] %s/%s %s%s\n", color.New(percCol).Sprint(bar),
types.SizeStr(types.NewInt(uint64(st.Capacity-st.FSAvailable))),
types.SizeStr(types.NewInt(uint64(st.Capacity))),
color.New(percCol).Sprintf("%d%%", usedPercent), desc)
}
// optional configured limit bar
if st.Max > 0 {
usedPercent := st.Used * 100 / st.Max
percCol := color.FgGreen
switch {
case usedPercent > 98:
percCol = color.FgRed
case usedPercent > 90:
percCol = color.FgYellow
}
set := st.Used * barCols / st.Max
used := (st.Used + st.Reserved) * barCols / st.Max
reserved := set - used
bar := safeRepeat("#", int(used)) + safeRepeat("*", int(reserved)) + safeRepeat(" ", int(barCols-set))
fmt.Printf("\t[%s] %s/%s %s (limit)\n", color.New(percCol).Sprint(bar),
types.SizeStr(types.NewInt(uint64(st.Used))),
types.SizeStr(types.NewInt(uint64(st.Max))),
color.New(percCol).Sprintf("%d%%", usedPercent))
}
fmt.Printf("\t%s; %s; %s; Reserved: %s\n", fmt.Printf("\t%s; %s; %s; Reserved: %s\n",
color.YellowString("Unsealed: %d", cnt[0]), color.YellowString("Unsealed: %d", cnt[0]),
color.GreenString("Sealed: %d", cnt[1]), color.GreenString("Sealed: %d", cnt[1]),

View File

@ -1812,13 +1812,17 @@ Inputs:
"ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8", "ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8",
"URLs": null, "URLs": null,
"Weight": 42, "Weight": 42,
"MaxStorage": 42,
"CanSeal": true, "CanSeal": true,
"CanStore": true "CanStore": true
}, },
{ {
"Capacity": 9, "Capacity": 9,
"Available": 9, "Available": 9,
"Reserved": 9 "FSAvailable": 9,
"Reserved": 9,
"Max": 9,
"Used": 9
} }
] ]
``` ```
@ -1918,6 +1922,7 @@ Response:
"ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8", "ID": "76f1988b-ef30-4d7e-b3ec-9a627f4ba5a8",
"URLs": null, "URLs": null,
"Weight": 42, "Weight": 42,
"MaxStorage": 42,
"CanSeal": true, "CanSeal": true,
"CanStore": true "CanStore": true
} }
@ -1989,7 +1994,10 @@ Inputs:
"Stat": { "Stat": {
"Capacity": 9, "Capacity": 9,
"Available": 9, "Available": 9,
"Reserved": 9 "FSAvailable": 9,
"Reserved": 9,
"Max": 9,
"Used": 9
}, },
"Err": "string value" "Err": "string value"
} }
@ -2015,7 +2023,10 @@ Response:
{ {
"Capacity": 9, "Capacity": 9,
"Available": 9, "Available": 9,
"Reserved": 9 "FSAvailable": 9,
"Reserved": 9,
"Max": 9,
"Used": 9
} }
``` ```

View File

@ -1,7 +1,12 @@
package fsutil package fsutil
type FsStat struct { type FsStat struct {
Capacity int64 Capacity int64
Available int64 // Available to use for sector storage Available int64 // Available to use for sector storage
Reserved int64 FSAvailable int64 // Available in the filesystem
Reserved int64
// non-zero when storage has configured MaxStorage
Max int64
Used int64
} }

View File

@ -15,7 +15,9 @@ func Statfs(path string) (FsStat, error) {
// force int64 to handle platform specific differences // force int64 to handle platform specific differences
//nolint:unconvert //nolint:unconvert
return FsStat{ return FsStat{
Capacity: int64(stat.Blocks) * int64(stat.Bsize), Capacity: int64(stat.Blocks) * int64(stat.Bsize),
Available: int64(stat.Bavail) * int64(stat.Bsize),
Available: int64(stat.Bavail) * int64(stat.Bsize),
FSAvailable: int64(stat.Bavail) * int64(stat.Bsize),
}, nil }, nil
} }

View File

@ -22,7 +22,8 @@ func Statfs(volumePath string) (FsStat, error) {
uintptr(unsafe.Pointer(&availBytes))) uintptr(unsafe.Pointer(&availBytes)))
return FsStat{ return FsStat{
Capacity: totalBytes, Capacity: totalBytes,
Available: availBytes, Available: availBytes,
FSAvailable: availBytes,
}, nil }, nil
} }

View File

@ -154,9 +154,10 @@ func addTestWorker(t *testing.T, sched *scheduler, index *stores.Index, name str
CanSeal: path.CanSeal, CanSeal: path.CanSeal,
CanStore: path.CanStore, CanStore: path.CanStore,
}, fsutil.FsStat{ }, fsutil.FsStat{
Capacity: 1 << 40, Capacity: 1 << 40,
Available: 1 << 40, Available: 1 << 40,
Reserved: 3, FSAvailable: 1 << 40,
Reserved: 3,
}) })
require.NoError(t, err) require.NoError(t, err)
} }

View File

@ -26,9 +26,10 @@ var SkippedHeartbeatThresh = HeartbeatInterval * 5
type ID string type ID string
type StorageInfo struct { type StorageInfo struct {
ID ID ID ID
URLs []string // TODO: Support non-http transports URLs []string // TODO: Support non-http transports
Weight uint64 Weight uint64
MaxStorage uint64
CanSeal bool CanSeal bool
CanStore bool CanStore bool
@ -156,6 +157,7 @@ func (i *Index) StorageAttach(ctx context.Context, si StorageInfo, st fsutil.FsS
} }
i.stores[si.ID].info.Weight = si.Weight i.stores[si.ID].info.Weight = si.Weight
i.stores[si.ID].info.MaxStorage = si.MaxStorage
i.stores[si.ID].info.CanSeal = si.CanSeal i.stores[si.ID].info.CanSeal = si.CanSeal
i.stores[si.ID].info.CanStore = si.CanStore i.stores[si.ID].info.CanStore = si.CanStore

View File

@ -42,6 +42,10 @@ type LocalStorageMeta struct {
// Finalized sectors that will be proved over time will be stored here // Finalized sectors that will be proved over time will be stored here
CanStore bool CanStore bool
// MaxStorage specifies the maximum number of bytes to use for sector storage
// (0 = unlimited)
MaxStorage uint64
} }
// StorageConfig .lotusstorage/storage.json // StorageConfig .lotusstorage/storage.json
@ -77,7 +81,8 @@ type Local struct {
} }
type path struct { type path struct {
local string // absolute local path local string // absolute local path
maxStorage uint64
reserved int64 reserved int64
reservations map[abi.SectorID]storiface.SectorFileType reservations map[abi.SectorID]storiface.SectorFileType
@ -127,6 +132,25 @@ func (p *path) stat(ls LocalStorage) (fsutil.FsStat, error) {
stat.Available = 0 stat.Available = 0
} }
if p.maxStorage > 0 {
used, err := ls.DiskUsage(p.local)
if err != nil {
return fsutil.FsStat{}, err
}
stat.Max = int64(p.maxStorage)
stat.Used = used
avail := int64(p.maxStorage) - used
if uint64(used) > p.maxStorage {
avail = 0
}
if avail < stat.Available {
stat.Available = avail
}
}
return stat, err return stat, err
} }
@ -164,6 +188,7 @@ func (st *Local) OpenPath(ctx context.Context, p string) error {
out := &path{ out := &path{
local: p, local: p,
maxStorage: meta.MaxStorage,
reserved: 0, reserved: 0,
reservations: map[abi.SectorID]storiface.SectorFileType{}, reservations: map[abi.SectorID]storiface.SectorFileType{},
} }
@ -174,11 +199,12 @@ func (st *Local) OpenPath(ctx context.Context, p string) error {
} }
err = st.index.StorageAttach(ctx, StorageInfo{ err = st.index.StorageAttach(ctx, StorageInfo{
ID: meta.ID, ID: meta.ID,
URLs: st.urls, URLs: st.urls,
Weight: meta.Weight, Weight: meta.Weight,
CanSeal: meta.CanSeal, MaxStorage: meta.MaxStorage,
CanStore: meta.CanStore, CanSeal: meta.CanSeal,
CanStore: meta.CanStore,
}, fst) }, fst)
if err != nil { if err != nil {
return xerrors.Errorf("declaring storage in index: %w", err) return xerrors.Errorf("declaring storage in index: %w", err)
@ -237,11 +263,12 @@ func (st *Local) Redeclare(ctx context.Context) error {
} }
err = st.index.StorageAttach(ctx, StorageInfo{ err = st.index.StorageAttach(ctx, StorageInfo{
ID: id, ID: id,
URLs: st.urls, URLs: st.urls,
Weight: meta.Weight, Weight: meta.Weight,
CanSeal: meta.CanSeal, MaxStorage: meta.MaxStorage,
CanStore: meta.CanStore, CanSeal: meta.CanSeal,
CanStore: meta.CanStore,
}, fst) }, fst)
if err != nil { if err != nil {
return xerrors.Errorf("redeclaring storage in index: %w", err) return xerrors.Errorf("redeclaring storage in index: %w", err)

View File

@ -36,8 +36,9 @@ func (t *TestingLocalStorage) SetStorage(f func(*StorageConfig)) error {
func (t *TestingLocalStorage) Stat(path string) (fsutil.FsStat, error) { func (t *TestingLocalStorage) Stat(path string) (fsutil.FsStat, error) {
return fsutil.FsStat{ return fsutil.FsStat{
Capacity: pathSize, Capacity: pathSize,
Available: pathSize, Available: pathSize,
FSAvailable: pathSize,
}, nil }, nil
} }