lotus/storage/sectorstorage/stores/local.go

385 lines
9.2 KiB
Go
Raw Normal View History

package stores
import (
2020-03-13 01:37:38 +00:00
"context"
"encoding/json"
"io/ioutil"
"math/bits"
"os"
"path/filepath"
"sync"
"github.com/filecoin-project/specs-actors/actors/abi"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/lotus/node/config"
2020-03-23 11:40:02 +00:00
"github.com/filecoin-project/lotus/storage/sectorstorage/sectorutil"
)
2020-03-13 11:59:19 +00:00
type StoragePath struct {
ID ID
Weight uint64
LocalPath string
CanSeal bool
CanStore bool
}
// [path]/sectorstore.json
2020-03-19 15:10:19 +00:00
type LocalStorageMeta struct {
2020-03-13 11:59:19 +00:00
ID ID
Weight uint64 // 0 = readonly
CanSeal bool
CanStore bool
}
type LocalStorage interface {
GetStorage() (config.StorageConfig, error)
SetStorage(func(*config.StorageConfig)) error
}
const MetaFile = "sectorstore.json"
2020-03-05 19:21:06 +00:00
var pathTypes = []sectorbuilder.SectorFileType{sectorbuilder.FTUnsealed, sectorbuilder.FTSealed, sectorbuilder.FTCache}
type Local struct {
localStorage LocalStorage
2020-03-19 15:10:19 +00:00
index SectorIndex
urls []string
2020-03-19 15:10:19 +00:00
paths map[ID]*path
localLk sync.RWMutex
}
type path struct {
2020-03-19 15:10:19 +00:00
local string // absolute local path
}
2020-03-19 15:10:19 +00:00
func NewLocal(ctx context.Context, ls LocalStorage, index SectorIndex, urls []string) (*Local, error) {
l := &Local{
localStorage: ls,
2020-03-19 15:10:19 +00:00
index: index,
urls: urls,
paths: map[ID]*path{},
}
2020-03-19 15:10:19 +00:00
return l, l.open(ctx)
}
2020-03-19 15:10:19 +00:00
func (st *Local) OpenPath(ctx context.Context, p string) error {
st.localLk.Lock()
defer st.localLk.Unlock()
mb, err := ioutil.ReadFile(filepath.Join(p, MetaFile))
if err != nil {
return xerrors.Errorf("reading storage metadata for %s: %w", p, err)
}
2020-03-19 15:10:19 +00:00
var meta LocalStorageMeta
if err := json.Unmarshal(mb, &meta); err != nil {
return xerrors.Errorf("unmarshalling storage metadata for %s: %w", p, err)
}
// TODO: Check existing / dedupe
out := &path{
2020-03-19 15:10:19 +00:00
local: p,
}
fst, err := Stat(p)
if err != nil {
return err
}
err = st.index.StorageAttach(ctx, StorageInfo{
ID: meta.ID,
URLs: st.urls,
Weight: meta.Weight,
CanSeal: meta.CanSeal,
CanStore: meta.CanStore,
}, fst)
if err != nil {
return xerrors.Errorf("declaring storage in index: %w", err)
}
for _, t := range pathTypes {
ents, err := ioutil.ReadDir(filepath.Join(p, t.String()))
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Join(p, t.String()), 0755); err != nil {
2020-03-06 22:23:21 +00:00
return xerrors.Errorf("openPath mkdir '%s': %w", filepath.Join(p, t.String()), err)
}
continue
}
return xerrors.Errorf("listing %s: %w", filepath.Join(p, t.String()), err)
}
for _, ent := range ents {
sid, err := sectorutil.ParseSectorID(ent.Name())
if err != nil {
return xerrors.Errorf("parse sector id %s: %w", ent.Name(), err)
}
2020-03-19 15:10:19 +00:00
if err := st.index.StorageDeclareSector(ctx, meta.ID, sid, t); err != nil {
return xerrors.Errorf("declare sector %d(t:%d) -> %s: %w", sid, t, meta.ID, err)
}
}
}
2020-03-19 15:10:19 +00:00
st.paths[meta.ID] = out
return nil
}
2020-03-19 15:10:19 +00:00
func (st *Local) open(ctx context.Context) error {
cfg, err := st.localStorage.GetStorage()
if err != nil {
return xerrors.Errorf("getting local storage config: %w", err)
}
for _, path := range cfg.StoragePaths {
2020-03-19 15:10:19 +00:00
err := st.OpenPath(ctx, path.Path)
if err != nil {
return xerrors.Errorf("opening path %s: %w", path.Path, err)
}
}
return nil
}
func (st *Local) AcquireSector(ctx context.Context, sid abi.SectorID, existing sectorbuilder.SectorFileType, allocate sectorbuilder.SectorFileType, sealing bool) (sectorbuilder.SectorPaths, sectorbuilder.SectorPaths, func(), error) {
2020-03-05 19:21:06 +00:00
if existing|allocate != existing^allocate {
return sectorbuilder.SectorPaths{}, sectorbuilder.SectorPaths{}, nil, xerrors.New("can't both find and allocate a sector")
}
st.localLk.RLock()
var out sectorbuilder.SectorPaths
var storageIDs sectorbuilder.SectorPaths
for _, fileType := range pathTypes {
2020-03-05 19:21:06 +00:00
if fileType&existing == 0 {
continue
}
si, err := st.index.StorageFindSector(ctx, sid, fileType, false)
2020-03-19 15:10:19 +00:00
if err != nil {
log.Warnf("finding existing sector %d(t:%d) failed: %+v", sid, fileType, err)
continue
}
for _, info := range si {
p, ok := st.paths[info.ID]
if !ok {
continue
}
2020-03-19 15:10:19 +00:00
if p.local == "" { // TODO: can that even be the case?
continue
2020-03-11 05:49:17 +00:00
}
spath := filepath.Join(p.local, fileType.String(), sectorutil.SectorName(sid))
2020-03-13 00:23:05 +00:00
sectorutil.SetPathByType(&out, fileType, spath)
2020-03-19 15:10:19 +00:00
sectorutil.SetPathByType(&storageIDs, fileType, string(info.ID))
existing ^= fileType
break
}
}
for _, fileType := range pathTypes {
2020-03-05 19:21:06 +00:00
if fileType&allocate == 0 {
continue
}
2020-03-19 15:10:19 +00:00
sis, err := st.index.StorageBestAlloc(ctx, fileType, sealing)
if err != nil {
st.localLk.RUnlock()
return sectorbuilder.SectorPaths{}, sectorbuilder.SectorPaths{}, nil, xerrors.Errorf("finding best storage for allocating : %w", err)
}
2020-03-13 11:59:19 +00:00
var best string
var bestID ID
2020-03-19 15:10:19 +00:00
for _, si := range sis {
p, ok := st.paths[si.ID]
if !ok {
continue
}
2020-03-19 15:10:19 +00:00
if p.local == "" { // TODO: can that even be the case?
continue
}
2020-03-09 19:22:30 +00:00
2020-03-19 15:10:19 +00:00
if sealing && !si.CanSeal {
continue
}
if !sealing && !si.CanStore {
continue
}
// TODO: Check free space
best = filepath.Join(p.local, fileType.String(), sectorutil.SectorName(sid))
2020-03-19 15:10:19 +00:00
bestID = si.ID
}
if best == "" {
st.localLk.RUnlock()
return sectorbuilder.SectorPaths{}, sectorbuilder.SectorPaths{}, nil, xerrors.Errorf("couldn't find a suitable path for a sector")
}
2020-03-13 00:23:05 +00:00
sectorutil.SetPathByType(&out, fileType, best)
2020-03-13 11:59:19 +00:00
sectorutil.SetPathByType(&storageIDs, fileType, string(bestID))
allocate ^= fileType
}
return out, storageIDs, st.localLk.RUnlock, nil
}
2020-03-19 15:10:19 +00:00
func (st *Local) Local(ctx context.Context) ([]StoragePath, error) {
2020-03-18 23:23:28 +00:00
st.localLk.RLock()
defer st.localLk.RUnlock()
2020-03-13 11:59:19 +00:00
var out []StoragePath
2020-03-19 15:10:19 +00:00
for id, p := range st.paths {
2020-03-05 02:18:22 +00:00
if p.local == "" {
continue
}
2020-03-19 15:10:19 +00:00
si, err := st.index.StorageInfo(ctx, id)
if err != nil {
return nil, xerrors.Errorf("get storage info for %s: %w", id, err)
}
2020-03-13 11:59:19 +00:00
out = append(out, StoragePath{
2020-03-19 15:10:19 +00:00
ID: id,
Weight: si.Weight,
2020-03-05 02:18:22 +00:00
LocalPath: p.local,
2020-03-19 15:10:19 +00:00
CanSeal: si.CanSeal,
CanStore: si.CanStore,
2020-03-05 02:18:22 +00:00
})
}
2020-03-19 15:10:19 +00:00
return out, nil
2020-03-05 02:18:22 +00:00
}
2020-03-16 17:50:07 +00:00
func (st *Local) Remove(ctx context.Context, sid abi.SectorID, typ sectorbuilder.SectorFileType) error {
if bits.OnesCount(uint(typ)) != 1 {
return xerrors.New("delete expects one file type")
}
si, err := st.index.StorageFindSector(ctx, sid, typ, false)
if err != nil {
return xerrors.Errorf("finding existing sector %d(t:%d) failed: %w", sid, typ, err)
}
if len(si) == 0 {
return xerrors.Errorf("can't delete sector %v(%d), not found", sid, typ)
}
for _, info := range si {
p, ok := st.paths[info.ID]
if !ok {
continue
}
if p.local == "" { // TODO: can that even be the case?
continue
}
if err := st.index.StorageDropSector(ctx, info.ID, sid, typ); err != nil {
return xerrors.Errorf("dropping sector from index: %w", err)
}
spath := filepath.Join(p.local, typ.String(), sectorutil.SectorName(sid))
log.Infof("remove %s", spath)
if err := os.RemoveAll(spath); err != nil {
log.Errorf("removing sector (%v) from %s: %+v", sid, spath, err)
}
}
return nil
}
func (st *Local) MoveStorage(ctx context.Context, s abi.SectorID, types sectorbuilder.SectorFileType) error {
dest, destIds, sdone, err := st.AcquireSector(ctx, s, 0, types, false)
if err != nil {
return xerrors.Errorf("acquire dest storage: %w", err)
}
defer sdone()
src, srcIds, ddone, err := st.AcquireSector(ctx, s, types, 0, false)
if err != nil {
return xerrors.Errorf("acquire src storage: %w", err)
}
defer ddone()
for _, fileType := range pathTypes {
if fileType&types == 0 {
continue
}
sst, err := st.index.StorageInfo(ctx, ID(sectorutil.PathByType(srcIds, fileType)))
if err != nil {
return xerrors.Errorf("failed to get source storage info: %w", err)
}
dst, err := st.index.StorageInfo(ctx, ID(sectorutil.PathByType(destIds, fileType)))
if err != nil {
return xerrors.Errorf("failed to get source storage info: %w", err)
}
if sst.ID == dst.ID {
log.Debugf("not moving %v(%d); src and dest are the same", s, fileType)
continue
}
if sst.CanStore {
log.Debugf("not moving %v(%d); source supports storage", s, fileType)
continue
}
log.Debugf("moving %v(%d) to storage: %s(se:%t; st:%t) -> %s(se:%t; st:%t)", s, fileType, sst.ID, sst.CanSeal, sst.CanStore, dst.ID, dst.CanSeal, dst.CanStore)
if err := st.index.StorageDropSector(ctx, ID(sectorutil.PathByType(srcIds, fileType)), s, fileType); err != nil {
return xerrors.Errorf("dropping source sector from index: %w", err)
}
if err := move(sectorutil.PathByType(src, fileType), sectorutil.PathByType(dest, fileType)); err != nil {
// TODO: attempt some recovery (check if src is still there, re-declare)
return xerrors.Errorf("moving sector %v(%d): %w", s, fileType, err)
}
if err := st.index.StorageDeclareSector(ctx, ID(sectorutil.PathByType(destIds, fileType)), s, fileType); err != nil {
return xerrors.Errorf("declare sector %d(t:%d) -> %s: %w", s, fileType, ID(sectorutil.PathByType(destIds, fileType)), err)
}
}
return nil
}
var errPathNotFound = xerrors.Errorf("fsstat: path not found")
func (st *Local) FsStat(ctx context.Context, id ID) (FsStat, error) {
2020-03-19 15:10:19 +00:00
st.localLk.RLock()
defer st.localLk.RUnlock()
2020-03-16 17:50:07 +00:00
2020-03-19 15:10:19 +00:00
p, ok := st.paths[id]
if !ok {
return FsStat{}, errPathNotFound
2020-03-16 17:50:07 +00:00
}
2020-03-19 15:10:19 +00:00
return Stat(p.local)
2020-03-16 17:50:07 +00:00
}
var _ Store = &Local{}