2022-06-14 18:25:52 +00:00
|
|
|
package paths
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-11-12 09:29:42 +00:00
|
|
|
"errors"
|
2021-05-11 16:14:01 +00:00
|
|
|
"fmt"
|
2020-03-23 11:40:02 +00:00
|
|
|
"net/url"
|
|
|
|
gopath "path"
|
|
|
|
"sort"
|
|
|
|
"sync"
|
2020-05-08 16:08:48 +00:00
|
|
|
"time"
|
2020-03-23 11:40:02 +00:00
|
|
|
|
2021-10-01 12:37:27 +00:00
|
|
|
"go.opencensus.io/stats"
|
|
|
|
"go.opencensus.io/tag"
|
2020-03-23 11:40:02 +00:00
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
"github.com/filecoin-project/go-address"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
"github.com/filecoin-project/go-state-types/big"
|
2020-09-30 17:32:19 +00:00
|
|
|
|
2022-07-12 11:55:18 +00:00
|
|
|
"github.com/filecoin-project/lotus/journal/alerting"
|
2021-10-01 12:37:27 +00:00
|
|
|
"github.com/filecoin-project/lotus/metrics"
|
2022-06-14 18:03:38 +00:00
|
|
|
"github.com/filecoin-project/lotus/storage/sealer/fsutil"
|
2022-06-15 10:06:22 +00:00
|
|
|
"github.com/filecoin-project/lotus/storage/sealer/storiface"
|
2020-03-23 11:40:02 +00:00
|
|
|
)
|
|
|
|
|
2020-05-08 16:54:06 +00:00
|
|
|
var HeartbeatInterval = 10 * time.Second
|
|
|
|
var SkippedHeartbeatThresh = HeartbeatInterval * 5
|
2020-05-08 16:08:48 +00:00
|
|
|
|
2021-10-11 19:05:05 +00:00
|
|
|
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/index.go -package=mocks . SectorIndex
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
type SectorIndex interface { // part of storage-miner api
|
2022-01-18 10:57:04 +00:00
|
|
|
StorageAttach(context.Context, storiface.StorageInfo, fsutil.FsStat) error
|
2022-07-13 18:09:21 +00:00
|
|
|
StorageDetach(ctx context.Context, id storiface.ID, url string) error
|
2022-01-18 10:57:04 +00:00
|
|
|
StorageInfo(context.Context, storiface.ID) (storiface.StorageInfo, error)
|
|
|
|
StorageReportHealth(context.Context, storiface.ID, storiface.HealthReport) error
|
2020-03-23 11:40:02 +00:00
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
StorageDeclareSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType, primary bool) error
|
|
|
|
StorageDropSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType) error
|
|
|
|
StorageFindSector(ctx context.Context, sector abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error)
|
2020-03-23 11:40:02 +00:00
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error)
|
2020-06-03 19:21:27 +00:00
|
|
|
|
|
|
|
// atomically acquire locks on all sector file types. close ctx to unlock
|
2020-09-06 16:54:00 +00:00
|
|
|
StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error
|
|
|
|
StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error)
|
2021-12-03 11:33:23 +00:00
|
|
|
StorageGetLocks(ctx context.Context) (storiface.SectorLocks, error)
|
2021-05-20 11:01:14 +00:00
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error)
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 16:36:46 +00:00
|
|
|
type declMeta struct {
|
2022-01-18 10:57:04 +00:00
|
|
|
storage storiface.ID
|
2020-05-20 16:36:46 +00:00
|
|
|
primary bool
|
|
|
|
}
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
type storageEntry struct {
|
2022-01-18 10:57:04 +00:00
|
|
|
info *storiface.StorageInfo
|
2020-07-08 14:58:09 +00:00
|
|
|
fsi fsutil.FsStat
|
2020-05-08 16:08:48 +00:00
|
|
|
|
|
|
|
lastHeartbeat time.Time
|
|
|
|
heartbeatErr error
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
// MemIndex represents an in-memory index of storage sectors and storage entries.
|
2023-11-15 13:59:35 +00:00
|
|
|
type MemIndex struct {
|
2020-06-03 19:21:27 +00:00
|
|
|
*indexLocks
|
2020-03-23 11:40:02 +00:00
|
|
|
lk sync.RWMutex
|
|
|
|
|
2022-07-12 11:55:18 +00:00
|
|
|
// optional
|
|
|
|
alerting *alerting.Alerting
|
|
|
|
pathAlerts map[storiface.ID]alerting.AlertType
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
sectors map[storiface.Decl][]*declMeta
|
|
|
|
stores map[storiface.ID]*storageEntry
|
2023-08-04 15:35:31 +00:00
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func NewMemIndex(al *alerting.Alerting) *MemIndex {
|
|
|
|
return &MemIndex{
|
2020-06-03 19:21:27 +00:00
|
|
|
indexLocks: &indexLocks{
|
|
|
|
locks: map[abi.SectorID]*sectorLock{},
|
|
|
|
},
|
2022-07-12 11:55:18 +00:00
|
|
|
|
|
|
|
alerting: al,
|
|
|
|
pathAlerts: map[storiface.ID]alerting.AlertType{},
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
sectors: map[storiface.Decl][]*declMeta{},
|
|
|
|
stores: map[storiface.ID]*storageEntry{},
|
2023-08-04 15:35:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) {
|
2020-04-29 12:13:21 +00:00
|
|
|
i.lk.RLock()
|
|
|
|
defer i.lk.RUnlock()
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
byID := map[storiface.ID]map[abi.SectorID]storiface.SectorFileType{}
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
for id := range i.stores {
|
2020-09-06 16:54:00 +00:00
|
|
|
byID[id] = map[abi.SectorID]storiface.SectorFileType{}
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
for decl, ids := range i.sectors {
|
|
|
|
for _, id := range ids {
|
2020-05-20 16:36:46 +00:00
|
|
|
byID[id.storage][decl.SectorID] |= decl.SectorFileType
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
out := map[storiface.ID][]storiface.Decl{}
|
2020-03-23 11:40:02 +00:00
|
|
|
for id, m := range byID {
|
2022-01-18 10:57:04 +00:00
|
|
|
out[id] = []storiface.Decl{}
|
2020-03-23 11:40:02 +00:00
|
|
|
for sectorID, fileType := range m {
|
2022-01-18 10:57:04 +00:00
|
|
|
out[id] = append(out[id], storiface.Decl{
|
2020-03-23 11:40:02 +00:00
|
|
|
SectorID: sectorID,
|
|
|
|
SectorFileType: fileType,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-09 00:59:21 +00:00
|
|
|
return out, nil
|
2023-08-04 15:35:31 +00:00
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo, st fsutil.FsStat) error {
|
2022-07-12 11:55:18 +00:00
|
|
|
var allow, deny = make([]string, 0, len(si.AllowTypes)), make([]string, 0, len(si.DenyTypes))
|
|
|
|
|
|
|
|
if _, hasAlert := i.pathAlerts[si.ID]; i.alerting != nil && !hasAlert {
|
|
|
|
i.pathAlerts[si.ID] = i.alerting.AddAlertType("sector-index", "pathconf-"+string(si.ID))
|
|
|
|
}
|
|
|
|
|
2022-07-15 12:20:37 +00:00
|
|
|
var hasConfigIssues bool
|
2022-07-12 11:55:18 +00:00
|
|
|
|
|
|
|
for id, typ := range si.AllowTypes {
|
2022-07-01 16:02:10 +00:00
|
|
|
_, err := storiface.TypeFromString(typ)
|
|
|
|
if err != nil {
|
2022-07-15 10:46:20 +00:00
|
|
|
// No need to hard-fail here, just warn the user
|
2022-07-01 16:02:10 +00:00
|
|
|
// (note that even with all-invalid entries we'll deny all types, so nothing unexpected should enter the path)
|
2022-07-15 12:20:37 +00:00
|
|
|
hasConfigIssues = true
|
2022-07-12 11:55:18 +00:00
|
|
|
|
|
|
|
if i.alerting != nil {
|
|
|
|
i.alerting.Raise(i.pathAlerts[si.ID], map[string]interface{}{
|
|
|
|
"message": "bad path type in AllowTypes",
|
|
|
|
"path": string(si.ID),
|
|
|
|
"idx": id,
|
|
|
|
"path_type": typ,
|
|
|
|
"error": err.Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
2022-07-01 16:02:10 +00:00
|
|
|
}
|
2022-07-12 11:55:18 +00:00
|
|
|
allow = append(allow, typ)
|
2022-07-01 16:02:10 +00:00
|
|
|
}
|
2022-07-12 11:55:18 +00:00
|
|
|
for id, typ := range si.DenyTypes {
|
2022-07-01 16:02:10 +00:00
|
|
|
_, err := storiface.TypeFromString(typ)
|
|
|
|
if err != nil {
|
2022-07-15 10:46:20 +00:00
|
|
|
// No need to hard-fail here, just warn the user
|
2022-07-15 12:20:37 +00:00
|
|
|
hasConfigIssues = true
|
2022-07-12 11:55:18 +00:00
|
|
|
|
|
|
|
if i.alerting != nil {
|
|
|
|
i.alerting.Raise(i.pathAlerts[si.ID], map[string]interface{}{
|
|
|
|
"message": "bad path type in DenyTypes",
|
|
|
|
"path": string(si.ID),
|
|
|
|
"idx": id,
|
|
|
|
"path_type": typ,
|
|
|
|
"error": err.Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
2022-07-01 16:02:10 +00:00
|
|
|
}
|
2022-07-12 11:55:18 +00:00
|
|
|
deny = append(deny, typ)
|
|
|
|
}
|
|
|
|
si.AllowTypes = allow
|
|
|
|
si.DenyTypes = deny
|
|
|
|
|
2022-07-15 12:20:37 +00:00
|
|
|
if i.alerting != nil && !hasConfigIssues && i.alerting.IsRaised(i.pathAlerts[si.ID]) {
|
2022-07-12 11:55:18 +00:00
|
|
|
i.alerting.Resolve(i.pathAlerts[si.ID], map[string]string{
|
|
|
|
"message": "path config is now correct",
|
|
|
|
})
|
2022-07-01 16:02:10 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.Lock()
|
|
|
|
defer i.lk.Unlock()
|
|
|
|
|
|
|
|
log.Infof("New sector storage: %s", si.ID)
|
|
|
|
|
|
|
|
if _, ok := i.stores[si.ID]; ok {
|
|
|
|
for _, u := range si.URLs {
|
|
|
|
if _, err := url.Parse(u); err != nil {
|
|
|
|
return xerrors.Errorf("failed to parse url %s: %w", si.URLs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-23 12:12:43 +00:00
|
|
|
uloop:
|
|
|
|
for _, u := range si.URLs {
|
|
|
|
for _, l := range i.stores[si.ID].info.URLs {
|
|
|
|
if u == l {
|
|
|
|
continue uloop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i.stores[si.ID].info.URLs = append(i.stores[si.ID].info.URLs, u)
|
|
|
|
}
|
|
|
|
|
2020-12-22 09:46:58 +00:00
|
|
|
i.stores[si.ID].info.Weight = si.Weight
|
2021-02-18 15:44:34 +00:00
|
|
|
i.stores[si.ID].info.MaxStorage = si.MaxStorage
|
2020-12-22 09:46:58 +00:00
|
|
|
i.stores[si.ID].info.CanSeal = si.CanSeal
|
|
|
|
i.stores[si.ID].info.CanStore = si.CanStore
|
2021-10-06 11:54:25 +00:00
|
|
|
i.stores[si.ID].info.Groups = si.Groups
|
|
|
|
i.stores[si.ID].info.AllowTo = si.AllowTo
|
2022-07-12 11:55:18 +00:00
|
|
|
i.stores[si.ID].info.AllowTypes = allow
|
|
|
|
i.stores[si.ID].info.DenyTypes = deny
|
2024-04-01 18:19:00 +00:00
|
|
|
i.stores[si.ID].info.AllowMiners = si.AllowMiners
|
|
|
|
i.stores[si.ID].info.DenyMiners = si.DenyMiners
|
2020-12-22 09:46:58 +00:00
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
i.stores[si.ID] = &storageEntry{
|
|
|
|
info: &si,
|
|
|
|
fsi: st,
|
2020-05-08 16:08:48 +00:00
|
|
|
|
|
|
|
lastHeartbeat: time.Now(),
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
2023-08-04 15:35:31 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageDetach(ctx context.Context, id storiface.ID, url string) error {
|
2022-07-13 18:09:21 +00:00
|
|
|
i.lk.Lock()
|
|
|
|
defer i.lk.Unlock()
|
|
|
|
|
|
|
|
// ent: *storageEntry
|
|
|
|
ent, ok := i.stores[id]
|
|
|
|
if !ok {
|
2022-07-15 10:56:03 +00:00
|
|
|
return xerrors.Errorf("storage '%s' isn't registered", id)
|
2022-07-13 18:09:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if this is the only path provider/url for this pathID
|
|
|
|
drop := true
|
|
|
|
if len(ent.info.URLs) > 0 {
|
|
|
|
drop = len(ent.info.URLs) == 1 // only one url
|
|
|
|
|
2022-08-02 13:46:35 +00:00
|
|
|
if drop && ent.info.URLs[0] != url {
|
2022-07-13 18:09:21 +00:00
|
|
|
return xerrors.Errorf("not dropping path, requested and index urls don't match ('%s' != '%s')", url, ent.info.URLs[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if drop {
|
|
|
|
if a, hasAlert := i.pathAlerts[id]; hasAlert && i.alerting != nil {
|
|
|
|
if i.alerting.IsRaised(a) {
|
|
|
|
i.alerting.Resolve(a, map[string]string{
|
|
|
|
"message": "path detached",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
delete(i.pathAlerts, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// stats
|
|
|
|
var droppedEntries, primaryEntries, droppedDecls int
|
|
|
|
|
|
|
|
// drop declarations
|
|
|
|
for decl, dms := range i.sectors {
|
|
|
|
var match bool
|
|
|
|
for _, dm := range dms {
|
|
|
|
if dm.storage == id {
|
|
|
|
match = true
|
|
|
|
droppedEntries++
|
|
|
|
if dm.primary {
|
|
|
|
primaryEntries++
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no entries match, nothing to do here
|
|
|
|
if !match {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there's a match, and only one entry, drop the whole declaration
|
|
|
|
if len(dms) <= 1 {
|
|
|
|
delete(i.sectors, decl)
|
|
|
|
droppedDecls++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// rewrite entries with the path we're dropping filtered out
|
|
|
|
filtered := make([]*declMeta, 0, len(dms)-1)
|
|
|
|
for _, dm := range dms {
|
|
|
|
if dm.storage != id {
|
|
|
|
filtered = append(filtered, dm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i.sectors[decl] = filtered
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(i.stores, id)
|
|
|
|
|
|
|
|
log.Warnw("Dropping sector storage", "path", id, "url", url, "droppedEntries", droppedEntries, "droppedPrimaryEntries", primaryEntries, "droppedDecls", droppedDecls)
|
|
|
|
} else {
|
|
|
|
newUrls := make([]string, 0, len(ent.info.URLs))
|
|
|
|
for _, u := range ent.info.URLs {
|
|
|
|
if u != url {
|
|
|
|
newUrls = append(newUrls, u)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ent.info.URLs = newUrls
|
|
|
|
|
|
|
|
log.Warnw("Dropping sector path endpoint", "path", id, "url", url)
|
|
|
|
}
|
|
|
|
|
2023-08-04 15:35:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageReportHealth(ctx context.Context, id storiface.ID, report storiface.HealthReport) error {
|
2020-05-08 16:08:48 +00:00
|
|
|
i.lk.Lock()
|
|
|
|
defer i.lk.Unlock()
|
|
|
|
|
|
|
|
ent, ok := i.stores[id]
|
|
|
|
if !ok {
|
|
|
|
return xerrors.Errorf("health report for unknown storage: %s", id)
|
|
|
|
}
|
|
|
|
|
|
|
|
ent.fsi = report.Stat
|
2020-11-12 09:29:42 +00:00
|
|
|
if report.Err != "" {
|
|
|
|
ent.heartbeatErr = errors.New(report.Err)
|
2021-04-11 06:47:04 +00:00
|
|
|
} else {
|
|
|
|
ent.heartbeatErr = nil
|
2020-11-12 09:29:42 +00:00
|
|
|
}
|
2020-05-08 16:08:48 +00:00
|
|
|
ent.lastHeartbeat = time.Now()
|
|
|
|
|
2021-10-01 12:37:27 +00:00
|
|
|
if report.Stat.Capacity > 0 {
|
2022-11-29 12:06:56 +00:00
|
|
|
ctx, _ = tag.New(ctx,
|
|
|
|
tag.Upsert(metrics.StorageID, string(id)),
|
|
|
|
tag.Upsert(metrics.PathStorage, fmt.Sprint(ent.info.CanStore)),
|
|
|
|
tag.Upsert(metrics.PathSeal, fmt.Sprint(ent.info.CanSeal)),
|
|
|
|
)
|
2021-10-01 12:37:27 +00:00
|
|
|
|
|
|
|
stats.Record(ctx, metrics.StorageFSAvailable.M(float64(report.Stat.FSAvailable)/float64(report.Stat.Capacity)))
|
|
|
|
stats.Record(ctx, metrics.StorageAvailable.M(float64(report.Stat.Available)/float64(report.Stat.Capacity)))
|
|
|
|
stats.Record(ctx, metrics.StorageReserved.M(float64(report.Stat.Reserved)/float64(report.Stat.Capacity)))
|
|
|
|
|
|
|
|
stats.Record(ctx, metrics.StorageCapacityBytes.M(report.Stat.Capacity))
|
|
|
|
stats.Record(ctx, metrics.StorageFSAvailableBytes.M(report.Stat.FSAvailable))
|
|
|
|
stats.Record(ctx, metrics.StorageAvailableBytes.M(report.Stat.Available))
|
|
|
|
stats.Record(ctx, metrics.StorageReservedBytes.M(report.Stat.Reserved))
|
|
|
|
|
|
|
|
if report.Stat.Max > 0 {
|
|
|
|
stats.Record(ctx, metrics.StorageLimitUsed.M(float64(report.Stat.Used)/float64(report.Stat.Max)))
|
|
|
|
stats.Record(ctx, metrics.StorageLimitUsedBytes.M(report.Stat.Used))
|
|
|
|
stats.Record(ctx, metrics.StorageLimitMaxBytes.M(report.Stat.Max))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 16:08:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageDeclareSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType, primary bool) error {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.Lock()
|
|
|
|
defer i.lk.Unlock()
|
|
|
|
|
2020-05-20 16:36:46 +00:00
|
|
|
loop:
|
2020-09-06 16:54:00 +00:00
|
|
|
for _, fileType := range storiface.PathTypes {
|
2020-03-23 11:40:02 +00:00
|
|
|
if fileType&ft == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-01-18 11:11:59 +00:00
|
|
|
d := storiface.Decl{SectorID: s, SectorFileType: fileType}
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
for _, sid := range i.sectors[d] {
|
2020-08-16 10:40:35 +00:00
|
|
|
if sid.storage == storageID {
|
2020-05-20 16:36:46 +00:00
|
|
|
if !sid.primary && primary {
|
|
|
|
sid.primary = true
|
|
|
|
} else {
|
2023-09-21 15:37:02 +00:00
|
|
|
log.Debugf("sector %v redeclared in %s", s, storageID)
|
2020-05-20 16:36:46 +00:00
|
|
|
}
|
|
|
|
continue loop
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 16:36:46 +00:00
|
|
|
i.sectors[d] = append(i.sectors[d], &declMeta{
|
2020-08-16 10:40:35 +00:00
|
|
|
storage: storageID,
|
2020-05-20 16:36:46 +00:00
|
|
|
primary: primary,
|
|
|
|
})
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2023-08-04 15:35:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageDropSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType) error {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.Lock()
|
|
|
|
defer i.lk.Unlock()
|
|
|
|
|
2020-09-06 16:54:00 +00:00
|
|
|
for _, fileType := range storiface.PathTypes {
|
2020-03-23 11:40:02 +00:00
|
|
|
if fileType&ft == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-01-18 11:11:59 +00:00
|
|
|
d := storiface.Decl{SectorID: s, SectorFileType: fileType}
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
if len(i.sectors[d]) == 0 {
|
2021-02-18 04:00:28 +00:00
|
|
|
continue
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-20 16:36:46 +00:00
|
|
|
rewritten := make([]*declMeta, 0, len(i.sectors[d])-1)
|
2020-03-23 11:40:02 +00:00
|
|
|
for _, sid := range i.sectors[d] {
|
2020-08-16 10:40:35 +00:00
|
|
|
if sid.storage == storageID {
|
2020-03-23 11:40:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
rewritten = append(rewritten, sid)
|
|
|
|
}
|
|
|
|
if len(rewritten) == 0 {
|
|
|
|
delete(i.sectors, d)
|
2021-02-18 04:00:28 +00:00
|
|
|
continue
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
i.sectors[d] = rewritten
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error) {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.RLock()
|
|
|
|
defer i.lk.RUnlock()
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
storageIDs := map[storiface.ID]uint64{}
|
|
|
|
isprimary := map[storiface.ID]bool{}
|
2020-03-23 11:40:02 +00:00
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
allowTo := map[storiface.Group]struct{}{}
|
2021-10-06 12:06:04 +00:00
|
|
|
|
2020-09-06 16:54:00 +00:00
|
|
|
for _, pathType := range storiface.PathTypes {
|
2020-03-23 11:40:02 +00:00
|
|
|
if ft&pathType == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-01-18 11:11:59 +00:00
|
|
|
for _, id := range i.sectors[storiface.Decl{SectorID: s, SectorFileType: pathType}] {
|
2020-05-20 16:36:46 +00:00
|
|
|
storageIDs[id.storage]++
|
|
|
|
isprimary[id.storage] = isprimary[id.storage] || id.primary
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
out := make([]storiface.SectorStorageInfo, 0, len(storageIDs))
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
for id, n := range storageIDs {
|
|
|
|
st, ok := i.stores[id]
|
|
|
|
if !ok {
|
|
|
|
log.Warnf("storage %s is not present in sector index (referenced by sector %v)", id, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-01-14 13:11:04 +00:00
|
|
|
urls, burls := make([]string, len(st.info.URLs)), make([]string, len(st.info.URLs))
|
2020-03-23 11:40:02 +00:00
|
|
|
for k, u := range st.info.URLs {
|
|
|
|
rl, err := url.Parse(u)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to parse url: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:54:00 +00:00
|
|
|
rl.Path = gopath.Join(rl.Path, ft.String(), storiface.SectorName(s))
|
2020-03-23 11:40:02 +00:00
|
|
|
urls[k] = rl.String()
|
2022-01-14 13:11:04 +00:00
|
|
|
burls[k] = u
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 12:06:04 +00:00
|
|
|
if allowTo != nil && len(st.info.AllowTo) > 0 {
|
|
|
|
for _, group := range st.info.AllowTo {
|
|
|
|
allowTo[group] = struct{}{}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
allowTo = nil // allow to any
|
|
|
|
}
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
out = append(out, storiface.SectorStorageInfo{
|
2022-01-14 13:11:04 +00:00
|
|
|
ID: id,
|
|
|
|
URLs: urls,
|
|
|
|
BaseURLs: burls,
|
|
|
|
Weight: st.info.Weight * n, // storage with more sector types is better
|
2020-05-20 16:36:46 +00:00
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
CanSeal: st.info.CanSeal,
|
|
|
|
CanStore: st.info.CanStore,
|
2020-05-20 16:36:46 +00:00
|
|
|
|
|
|
|
Primary: isprimary[id],
|
2022-07-01 16:02:10 +00:00
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
AllowTypes: st.info.AllowTypes,
|
|
|
|
DenyTypes: st.info.DenyTypes,
|
|
|
|
AllowMiners: st.info.AllowMiners,
|
|
|
|
DenyMiners: st.info.DenyMiners,
|
2020-03-23 11:40:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if allowFetch {
|
2020-10-21 01:30:56 +00:00
|
|
|
spaceReq, err := ft.SealSpaceUse(ssize)
|
2020-08-11 07:27:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("estimating required space: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
for id, st := range i.stores {
|
2020-08-11 07:27:03 +00:00
|
|
|
if !st.info.CanSeal {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:42:21 +00:00
|
|
|
proceed, msg, err := MinerFilter(st.info.AllowMiners, st.info.DenyMiners, s.Miner)
|
2024-04-02 20:34:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2024-04-01 18:19:00 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 20:34:03 +00:00
|
|
|
if !proceed {
|
2024-04-04 13:42:21 +00:00
|
|
|
log.Debugf("not allocating on %s, miner %s %s", st.info.ID, s.Miner.String(), msg)
|
2024-04-02 20:34:03 +00:00
|
|
|
continue
|
2024-04-01 18:19:00 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 07:27:03 +00:00
|
|
|
if spaceReq > uint64(st.fsi.Available) {
|
|
|
|
log.Debugf("not selecting on %s, out of space (available: %d, need: %d)", st.info.ID, st.fsi.Available, spaceReq)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Since(st.lastHeartbeat) > SkippedHeartbeatThresh {
|
|
|
|
log.Debugf("not selecting on %s, didn't receive heartbeats for %s", st.info.ID, time.Since(st.lastHeartbeat))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if st.heartbeatErr != nil {
|
|
|
|
log.Debugf("not selecting on %s, heartbeat error: %s", st.info.ID, st.heartbeatErr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
if _, ok := storageIDs[id]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-07-01 16:02:10 +00:00
|
|
|
if !ft.AnyAllowed(st.info.AllowTypes, st.info.DenyTypes) {
|
|
|
|
log.Debugf("not selecting on %s, not allowed by file type filters", st.info.ID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-10-06 12:06:04 +00:00
|
|
|
if allowTo != nil {
|
|
|
|
allow := false
|
|
|
|
for _, group := range st.info.Groups {
|
|
|
|
if _, found := allowTo[group]; found {
|
|
|
|
log.Debugf("path %s in allowed group %s", st.info.ID, group)
|
|
|
|
allow = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !allow {
|
2021-10-06 12:36:15 +00:00
|
|
|
log.Debugf("not selecting on %s, not in allowed group, allow %+v; path has %+v", st.info.ID, allowTo, st.info.Groups)
|
2021-10-06 12:06:04 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 13:11:04 +00:00
|
|
|
urls, burls := make([]string, len(st.info.URLs)), make([]string, len(st.info.URLs))
|
2020-03-23 11:40:02 +00:00
|
|
|
for k, u := range st.info.URLs {
|
|
|
|
rl, err := url.Parse(u)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("failed to parse url: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:54:00 +00:00
|
|
|
rl.Path = gopath.Join(rl.Path, ft.String(), storiface.SectorName(s))
|
2020-03-23 11:40:02 +00:00
|
|
|
urls[k] = rl.String()
|
2022-01-14 13:11:04 +00:00
|
|
|
burls[k] = u
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
out = append(out, storiface.SectorStorageInfo{
|
2022-01-14 13:11:04 +00:00
|
|
|
ID: id,
|
|
|
|
URLs: urls,
|
|
|
|
BaseURLs: burls,
|
|
|
|
Weight: st.info.Weight * 0, // TODO: something better than just '0'
|
2020-05-20 16:36:46 +00:00
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
CanSeal: st.info.CanSeal,
|
|
|
|
CanStore: st.info.CanStore,
|
2020-05-20 16:36:46 +00:00
|
|
|
|
|
|
|
Primary: false,
|
2022-07-01 16:02:10 +00:00
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
AllowTypes: st.info.AllowTypes,
|
|
|
|
DenyTypes: st.info.DenyTypes,
|
|
|
|
AllowMiners: st.info.AllowMiners,
|
|
|
|
DenyMiners: st.info.DenyMiners,
|
2020-03-23 11:40:02 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-09 00:59:21 +00:00
|
|
|
return out, nil
|
2023-08-04 15:35:31 +00:00
|
|
|
}
|
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
// StorageInfo retrieves the storage information for a given storage ID.
|
|
|
|
//
|
|
|
|
// The method first acquires a read lock on the MemIndex to ensure thread-safety.
|
|
|
|
// It then checks if the storage ID exists in the stores map. If not, it returns
|
|
|
|
// an error indicating that the sector store was not found.
|
|
|
|
//
|
|
|
|
// Finally, it returns the storage information of the selected storage.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - ctx: the context.Context object for cancellation and timeouts
|
|
|
|
// - id: the ID of the storage to retrieve information for
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// - storiface.StorageInfo: the storage information of the selected storage ID
|
|
|
|
// - error: an error indicating any issues encountered during the process
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.RLock()
|
|
|
|
defer i.lk.RUnlock()
|
|
|
|
|
|
|
|
si, found := i.stores[id]
|
|
|
|
if !found {
|
2022-01-18 10:57:04 +00:00
|
|
|
return storiface.StorageInfo{}, xerrors.Errorf("sector store not found")
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return *si.info, nil
|
|
|
|
}
|
|
|
|
|
2024-04-01 18:19:00 +00:00
|
|
|
// StorageBestAlloc selects the best available storage options for allocating
|
|
|
|
// a sector file. It takes into account the allocation type (sealing or storage),
|
|
|
|
// sector size, and path type (sealing or storage).
|
|
|
|
//
|
|
|
|
// The method first estimates the required space for the allocation based on the
|
|
|
|
// sector size and path type. It then iterates through all available storage options
|
|
|
|
// and filters out those that cannot be used for the given path type. It also filters
|
|
|
|
// out storage options that do not have enough available space or have not received
|
|
|
|
// heartbeats within a certain threshold.
|
|
|
|
//
|
|
|
|
// The remaining storage options are sorted based on their available space and weight,
|
|
|
|
// with higher availability and weight being prioritized. The method then returns
|
|
|
|
// the information of the selected storage options.
|
|
|
|
//
|
|
|
|
// If no suitable storage options are found, it returns an error indicating that
|
|
|
|
// no good path is available.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// - ctx: the context.Context object for cancellation and timeouts
|
|
|
|
// - allocate: the type of allocation (sealing or storage)
|
|
|
|
// - ssize: the size of the sector file
|
|
|
|
// - pathType: the path type (sealing or storage)
|
|
|
|
//
|
|
|
|
// Returns:
|
|
|
|
// - []storiface.StorageInfo: the information of the selected storage options
|
|
|
|
// - error: an error indicating any issues encountered during the process
|
|
|
|
func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.RLock()
|
|
|
|
defer i.lk.RUnlock()
|
|
|
|
|
|
|
|
var candidates []storageEntry
|
|
|
|
|
2021-05-11 11:19:26 +00:00
|
|
|
var err error
|
2021-05-11 16:14:01 +00:00
|
|
|
var spaceReq uint64
|
2021-05-11 11:19:26 +00:00
|
|
|
switch pathType {
|
|
|
|
case storiface.PathSealing:
|
|
|
|
spaceReq, err = allocate.SealSpaceUse(ssize)
|
|
|
|
case storiface.PathStorage:
|
|
|
|
spaceReq, err = allocate.StoreSpaceUse(ssize)
|
2021-05-11 16:14:01 +00:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unexpected pathType: %s", pathType))
|
2021-05-11 11:19:26 +00:00
|
|
|
}
|
2020-05-08 16:54:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("estimating required space: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-03-23 11:40:02 +00:00
|
|
|
for _, p := range i.stores {
|
2020-09-06 16:54:00 +00:00
|
|
|
if (pathType == storiface.PathSealing) && !p.info.CanSeal {
|
2020-03-23 11:40:02 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-09-06 16:54:00 +00:00
|
|
|
if (pathType == storiface.PathStorage) && !p.info.CanStore {
|
2020-03-23 11:40:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:42:21 +00:00
|
|
|
proceed, msg, err := MinerFilter(p.info.AllowMiners, p.info.DenyMiners, miner)
|
2024-04-02 20:34:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2024-04-01 18:19:00 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 20:34:03 +00:00
|
|
|
if !proceed {
|
2024-04-04 13:42:21 +00:00
|
|
|
log.Debugf("not allocating on %s, miner %s %s", p.info.ID, miner.String(), msg)
|
2024-04-02 20:34:03 +00:00
|
|
|
continue
|
2024-04-01 18:19:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-06 16:36:44 +00:00
|
|
|
if spaceReq > uint64(p.fsi.Available) {
|
2020-05-08 16:54:06 +00:00
|
|
|
log.Debugf("not allocating on %s, out of space (available: %d, need: %d)", p.info.ID, p.fsi.Available, spaceReq)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if time.Since(p.lastHeartbeat) > SkippedHeartbeatThresh {
|
|
|
|
log.Debugf("not allocating on %s, didn't receive heartbeats for %s", p.info.ID, time.Since(p.lastHeartbeat))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.heartbeatErr != nil {
|
|
|
|
log.Debugf("not allocating on %s, heartbeat error: %s", p.info.ID, p.heartbeatErr)
|
|
|
|
continue
|
|
|
|
}
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
candidates = append(candidates, *p)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(candidates) == 0 {
|
|
|
|
return nil, xerrors.New("no good path found")
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(candidates, func(i, j int) bool {
|
2020-08-16 10:40:35 +00:00
|
|
|
iw := big.Mul(big.NewInt(candidates[i].fsi.Available), big.NewInt(int64(candidates[i].info.Weight)))
|
|
|
|
jw := big.Mul(big.NewInt(candidates[j].fsi.Available), big.NewInt(int64(candidates[j].info.Weight)))
|
2020-03-23 11:40:02 +00:00
|
|
|
|
|
|
|
return iw.GreaterThan(jw)
|
|
|
|
})
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
out := make([]storiface.StorageInfo, len(candidates))
|
2020-03-23 11:40:02 +00:00
|
|
|
for i, candidate := range candidates {
|
|
|
|
out[i] = *candidate.info
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
func (i *MemIndex) FindSector(id abi.SectorID, typ storiface.SectorFileType) ([]storiface.ID, error) {
|
2020-03-23 11:40:02 +00:00
|
|
|
i.lk.RLock()
|
|
|
|
defer i.lk.RUnlock()
|
|
|
|
|
2022-01-18 10:57:04 +00:00
|
|
|
f, ok := i.sectors[storiface.Decl{
|
2020-03-23 11:40:02 +00:00
|
|
|
SectorID: id,
|
|
|
|
SectorFileType: typ,
|
2020-05-20 16:36:46 +00:00
|
|
|
}]
|
|
|
|
if !ok {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2022-01-18 10:57:04 +00:00
|
|
|
out := make([]storiface.ID, 0, len(f))
|
2020-05-20 16:36:46 +00:00
|
|
|
for _, meta := range f {
|
|
|
|
out = append(out, meta.storage)
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
2020-03-23 11:40:02 +00:00
|
|
|
}
|
|
|
|
|
2023-11-15 13:59:35 +00:00
|
|
|
var _ SectorIndex = &MemIndex{}
|
2024-04-02 20:34:03 +00:00
|
|
|
|
2024-04-04 13:42:21 +00:00
|
|
|
func MinerFilter(allowMiners, denyMiners []string, miner abi.ActorID) (bool, string, error) {
|
|
|
|
checkMinerInList := func(minersList []string, miner abi.ActorID) (bool, error) {
|
|
|
|
for _, m := range minersList {
|
2024-04-02 20:34:03 +00:00
|
|
|
minerIDStr := m
|
|
|
|
maddr, err := address.NewFromString(minerIDStr)
|
|
|
|
if err != nil {
|
|
|
|
return false, xerrors.Errorf("parsing miner address: %w", err)
|
|
|
|
}
|
|
|
|
mid, err := address.IDFromAddress(maddr)
|
|
|
|
if err != nil {
|
|
|
|
return false, xerrors.Errorf("converting miner address to ID: %w", err)
|
|
|
|
}
|
|
|
|
if abi.ActorID(mid) == miner {
|
2024-04-04 13:42:21 +00:00
|
|
|
return true, nil
|
2024-04-02 20:34:03 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-04 13:42:21 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(allowMiners) > 0 {
|
|
|
|
found, err := checkMinerInList(allowMiners, miner)
|
|
|
|
if err != nil || !found {
|
|
|
|
return false, "not allowed", err
|
2024-04-02 20:34:03 +00:00
|
|
|
}
|
2024-04-04 13:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(denyMiners) > 0 {
|
|
|
|
found, err := checkMinerInList(denyMiners, miner)
|
|
|
|
if err != nil || found {
|
|
|
|
return false, "denied", err
|
2024-04-02 20:34:03 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-04 13:42:21 +00:00
|
|
|
return true, "", nil
|
2024-04-02 20:34:03 +00:00
|
|
|
}
|