2020-03-11 07:22:21 +00:00
|
|
|
package stores
|
2020-03-04 02:24:08 +00:00
|
|
|
|
|
|
|
import (
|
2020-03-13 01:37:38 +00:00
|
|
|
"context"
|
2020-03-04 02:24:08 +00:00
|
|
|
"encoding/json"
|
|
|
|
"io/ioutil"
|
2020-03-05 01:12:52 +00:00
|
|
|
"os"
|
2020-03-04 02:24:08 +00:00
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
2020-03-04 02:24:08 +00:00
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-sectorbuilder"
|
|
|
|
"github.com/filecoin-project/lotus/node/config"
|
2020-03-11 07:22:21 +00:00
|
|
|
"github.com/filecoin-project/lotus/storage/sealmgr/sectorutil"
|
2020-03-04 02:24:08 +00:00
|
|
|
)
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
type StoragePath struct {
|
|
|
|
ID ID
|
|
|
|
Weight uint64
|
|
|
|
|
|
|
|
LocalPath string
|
|
|
|
|
|
|
|
CanSeal bool
|
|
|
|
CanStore bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// [path]/sectorstore.json
|
|
|
|
type StorageMeta struct {
|
|
|
|
ID ID
|
|
|
|
Weight uint64 // 0 = readonly
|
|
|
|
|
|
|
|
CanSeal bool
|
|
|
|
CanStore bool
|
|
|
|
}
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
type LocalStorage interface {
|
|
|
|
GetStorage() (config.StorageConfig, error)
|
|
|
|
SetStorage(func(*config.StorageConfig)) error
|
|
|
|
}
|
|
|
|
|
|
|
|
const MetaFile = "sectorstore.json"
|
2020-03-05 19:21:06 +00:00
|
|
|
|
2020-03-04 02:24:08 +00:00
|
|
|
var pathTypes = []sectorbuilder.SectorFileType{sectorbuilder.FTUnsealed, sectorbuilder.FTSealed, sectorbuilder.FTCache}
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
type Local struct {
|
2020-03-05 19:21:06 +00:00
|
|
|
localLk sync.RWMutex
|
2020-03-04 02:24:08 +00:00
|
|
|
localStorage LocalStorage
|
|
|
|
|
2020-03-09 19:41:05 +00:00
|
|
|
paths []*path
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type path struct {
|
2020-03-09 19:22:30 +00:00
|
|
|
lk sync.Mutex
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
meta StorageMeta
|
2020-03-04 02:24:08 +00:00
|
|
|
local string
|
|
|
|
|
|
|
|
sectors map[abi.SectorID]sectorbuilder.SectorFileType
|
|
|
|
}
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
func NewLocal(ls LocalStorage) (*Local, error) {
|
|
|
|
l := &Local{
|
|
|
|
localStorage: ls,
|
|
|
|
}
|
|
|
|
return l, l.open()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (st *Local) OpenPath(p string) error {
|
|
|
|
mb, err := ioutil.ReadFile(filepath.Join(p, MetaFile))
|
2020-03-05 22:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("reading storage metadata for %s: %w", p, err)
|
|
|
|
}
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
var meta StorageMeta
|
2020-03-05 22:02:01 +00:00
|
|
|
if err := json.Unmarshal(mb, &meta); err != nil {
|
|
|
|
return xerrors.Errorf("unmarshalling storage metadata for %s: %w", p, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check existing / dedupe
|
|
|
|
|
2020-03-09 19:41:05 +00:00
|
|
|
out := &path{
|
2020-03-04 02:24:08 +00:00
|
|
|
meta: meta,
|
|
|
|
local: p,
|
|
|
|
sectors: map[abi.SectorID]sectorbuilder.SectorFileType{},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, t := range pathTypes {
|
|
|
|
ents, err := ioutil.ReadDir(filepath.Join(p, t.String()))
|
|
|
|
if err != nil {
|
2020-03-05 01:12:52 +00:00
|
|
|
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)
|
2020-03-05 01:12:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
2020-03-05 22:02:01 +00:00
|
|
|
return xerrors.Errorf("listing %s: %w", filepath.Join(p, t.String()), err)
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, ent := range ents {
|
2020-03-11 07:22:21 +00:00
|
|
|
sid, err := sectorutil.ParseSectorID(ent.Name())
|
2020-03-04 02:24:08 +00:00
|
|
|
if err != nil {
|
2020-03-05 22:02:01 +00:00
|
|
|
return xerrors.Errorf("parse sector id %s: %w", ent.Name(), err)
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out.sectors[sid] |= t
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 22:02:01 +00:00
|
|
|
st.paths = append(st.paths, out)
|
|
|
|
|
|
|
|
return nil
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
func (st *Local) open() error {
|
2020-03-04 02:24:08 +00:00
|
|
|
st.localLk.Lock()
|
|
|
|
defer st.localLk.Unlock()
|
|
|
|
|
|
|
|
cfg, err := st.localStorage.GetStorage()
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("getting local storage config: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, path := range cfg.StoragePaths {
|
2020-03-11 07:22:21 +00:00
|
|
|
err := st.OpenPath(path.Path)
|
2020-03-04 02:24:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("opening path %s: %w", path.Path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-13 16:54:55 +00:00
|
|
|
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 {
|
2020-03-13 10:40:24 +00:00
|
|
|
return sectorbuilder.SectorPaths{}, sectorbuilder.SectorPaths{}, nil, xerrors.New("can't both find and allocate a sector")
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 00:38:07 +00:00
|
|
|
st.localLk.RLock()
|
|
|
|
|
2020-03-04 02:24:08 +00:00
|
|
|
var out sectorbuilder.SectorPaths
|
2020-03-13 10:40:24 +00:00
|
|
|
var storageIDs sectorbuilder.SectorPaths
|
2020-03-04 02:24:08 +00:00
|
|
|
|
|
|
|
for _, fileType := range pathTypes {
|
2020-03-05 19:21:06 +00:00
|
|
|
if fileType&existing == 0 {
|
2020-03-04 02:24:08 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, p := range st.paths {
|
2020-03-09 19:22:30 +00:00
|
|
|
p.lk.Lock()
|
2020-03-11 07:22:21 +00:00
|
|
|
s, ok := p.sectors[sid]
|
2020-03-09 19:22:30 +00:00
|
|
|
p.lk.Unlock()
|
2020-03-04 02:24:08 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-05 19:21:06 +00:00
|
|
|
if s&fileType == 0 {
|
2020-03-04 02:24:08 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-03-11 05:49:17 +00:00
|
|
|
if p.local == "" {
|
2020-03-11 07:22:21 +00:00
|
|
|
continue
|
2020-03-11 05:49:17 +00:00
|
|
|
}
|
2020-03-04 02:24:08 +00:00
|
|
|
|
2020-03-11 07:22:21 +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-13 11:59:19 +00:00
|
|
|
sectorutil.SetPathByType(&storageIDs, fileType, string(p.meta.ID))
|
2020-03-04 02:24:08 +00:00
|
|
|
|
|
|
|
existing ^= fileType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 00:38:07 +00:00
|
|
|
for _, fileType := range pathTypes {
|
2020-03-05 19:21:06 +00:00
|
|
|
if fileType&allocate == 0 {
|
2020-03-05 00:38:07 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
var best string
|
|
|
|
var bestID ID
|
2020-03-05 00:38:07 +00:00
|
|
|
|
|
|
|
for _, p := range st.paths {
|
|
|
|
if sealing && !p.meta.CanSeal {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !sealing && !p.meta.CanStore {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-09 19:22:30 +00:00
|
|
|
|
|
|
|
p.lk.Lock()
|
2020-03-11 07:22:21 +00:00
|
|
|
p.sectors[sid] |= fileType
|
2020-03-09 19:22:30 +00:00
|
|
|
p.lk.Unlock()
|
2020-03-05 00:38:07 +00:00
|
|
|
|
|
|
|
// TODO: Check free space
|
|
|
|
// TODO: Calc weights
|
|
|
|
|
2020-03-11 07:22:21 +00:00
|
|
|
best = filepath.Join(p.local, fileType.String(), sectorutil.SectorName(sid))
|
2020-03-13 10:40:24 +00:00
|
|
|
bestID = p.meta.ID
|
2020-03-05 00:38:07 +00:00
|
|
|
break // todo: the first path won't always be the best
|
|
|
|
}
|
|
|
|
|
|
|
|
if best == "" {
|
|
|
|
st.localLk.RUnlock()
|
2020-03-13 10:40:24 +00:00
|
|
|
return sectorbuilder.SectorPaths{}, sectorbuilder.SectorPaths{}, nil, xerrors.Errorf("couldn't find a suitable path for a sector")
|
2020-03-05 00:38:07 +00:00
|
|
|
}
|
|
|
|
|
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))
|
2020-03-05 00:38:07 +00:00
|
|
|
allocate ^= fileType
|
|
|
|
}
|
|
|
|
|
2020-03-13 10:40:24 +00:00
|
|
|
return out, storageIDs, st.localLk.RUnlock, nil
|
2020-03-04 02:24:08 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
func (st *Local) FindBestAllocStorage(allocate sectorbuilder.SectorFileType, sealing bool) ([]StorageMeta, 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 []StorageMeta
|
2020-03-05 00:38:07 +00:00
|
|
|
|
|
|
|
for _, p := range st.paths {
|
|
|
|
if sealing && !p.meta.CanSeal {
|
2020-03-18 23:23:28 +00:00
|
|
|
log.Debugf("alloc: not considering %s; can't seal", p.meta.ID)
|
2020-03-05 00:38:07 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !sealing && !p.meta.CanStore {
|
2020-03-18 23:23:28 +00:00
|
|
|
log.Debugf("alloc: not considering %s; can't store", p.meta.ID)
|
2020-03-05 00:38:07 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: filter out of space
|
|
|
|
|
|
|
|
out = append(out, p.meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(out) == 0 {
|
|
|
|
return nil, xerrors.New("no good path found")
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: sort by some kind of preference
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2020-03-17 20:19:52 +00:00
|
|
|
func (st *Local) FindSector(id abi.SectorID, typ sectorbuilder.SectorFileType) ([]StorageMeta, 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 []StorageMeta
|
2020-03-05 00:38:07 +00:00
|
|
|
for _, p := range st.paths {
|
2020-03-09 19:22:30 +00:00
|
|
|
p.lk.Lock()
|
2020-03-17 20:19:52 +00:00
|
|
|
t := p.sectors[id]
|
2020-03-05 19:21:06 +00:00
|
|
|
if t|typ == 0 {
|
2020-03-05 00:38:07 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-03-09 19:22:30 +00:00
|
|
|
p.lk.Unlock()
|
2020-03-05 00:38:07 +00:00
|
|
|
out = append(out, p.meta)
|
|
|
|
}
|
|
|
|
if len(out) == 0 {
|
2020-03-17 20:19:52 +00:00
|
|
|
return nil, xerrors.Errorf("sector %s/s-t0%d-%d not found", typ, id.Miner, id.Number)
|
2020-03-05 00:38:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
func (st *Local) Local() []StoragePath {
|
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-05 02:18:22 +00:00
|
|
|
for _, p := range st.paths {
|
|
|
|
if p.local == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-03-13 11:59:19 +00:00
|
|
|
out = append(out, StoragePath{
|
2020-03-05 02:18:22 +00:00
|
|
|
ID: p.meta.ID,
|
|
|
|
Weight: p.meta.Weight,
|
|
|
|
LocalPath: p.local,
|
|
|
|
CanSeal: p.meta.CanSeal,
|
|
|
|
CanStore: p.meta.CanStore,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
2020-03-16 17:50:07 +00:00
|
|
|
|
|
|
|
func (st *Local) List(id ID) map[abi.SectorID]sectorbuilder.SectorFileType {
|
|
|
|
out := map[abi.SectorID]sectorbuilder.SectorFileType{}
|
|
|
|
for _, p := range st.paths {
|
|
|
|
if p.meta.ID != id { // TODO: not very efficient
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for id, fileType := range p.sectors {
|
|
|
|
out[id] |= fileType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|