v1.27.0-a #10
@ -200,11 +200,11 @@ type StorageMiner interface {
|
|||||||
// StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference.
|
// StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference.
|
||||||
// Paths with more weight and more % of free space are preferred.
|
// Paths with more weight and more % of free space are preferred.
|
||||||
// Note: This method doesn't filter paths based on AllowTypes/DenyTypes.
|
// Note: This method doesn't filter paths based on AllowTypes/DenyTypes.
|
||||||
StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) //perm:admin
|
StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) //perm:admin
|
||||||
StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error //perm:admin
|
StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error //perm:admin
|
||||||
StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) //perm:admin
|
StorageTryLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) (bool, error) //perm:admin
|
||||||
StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin
|
StorageList(ctx context.Context) (map[storiface.ID][]storiface.Decl, error) //perm:admin
|
||||||
StorageGetLocks(ctx context.Context) (storiface.SectorLocks, error) //perm:admin
|
StorageGetLocks(ctx context.Context) (storiface.SectorLocks, error) //perm:admin
|
||||||
|
|
||||||
StorageLocal(ctx context.Context) (map[storiface.ID]string, error) //perm:admin
|
StorageLocal(ctx context.Context) (map[storiface.ID]string, error) //perm:admin
|
||||||
StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) //perm:admin
|
StorageStat(ctx context.Context, id storiface.ID) (fsutil.FsStat, error) //perm:admin
|
||||||
|
@ -1185,7 +1185,7 @@ type StorageMinerMethods struct {
|
|||||||
|
|
||||||
StorageAuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"`
|
StorageAuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"`
|
||||||
|
|
||||||
StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) `perm:"admin"`
|
StorageBestAlloc func(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) `perm:"admin"`
|
||||||
|
|
||||||
StorageDeclareSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType, p4 bool) error `perm:"admin"`
|
StorageDeclareSector func(p0 context.Context, p1 storiface.ID, p2 abi.SectorID, p3 storiface.SectorFileType, p4 bool) error `perm:"admin"`
|
||||||
|
|
||||||
@ -6988,14 +6988,14 @@ func (s *StorageMinerStub) StorageAuthVerify(p0 context.Context, p1 string) ([]a
|
|||||||
return *new([]auth.Permission), ErrNotSupported
|
return *new([]auth.Permission), ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) {
|
func (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) {
|
||||||
if s.Internal.StorageBestAlloc == nil {
|
if s.Internal.StorageBestAlloc == nil {
|
||||||
return *new([]storiface.StorageInfo), ErrNotSupported
|
return *new([]storiface.StorageInfo), ErrNotSupported
|
||||||
}
|
}
|
||||||
return s.Internal.StorageBestAlloc(p0, p1, p2, p3)
|
return s.Internal.StorageBestAlloc(p0, p1, p2, p3, p4)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StorageMinerStub) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) {
|
func (s *StorageMinerStub) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) {
|
||||||
return *new([]storiface.StorageInfo), ErrNotSupported
|
return *new([]storiface.StorageInfo), ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8897,11 +8897,23 @@
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"AllowMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"AllowTo": {
|
"AllowTo": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -8920,6 +8932,12 @@
|
|||||||
"CanStore": {
|
"CanStore": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"DenyMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"DenyTypes": {
|
"DenyTypes": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9078,7 +9096,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Filecoin.StorageBestAlloc",
|
"name": "Filecoin.StorageBestAlloc",
|
||||||
"description": "```go\nfunc (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType) ([]storiface.StorageInfo, error) {\n\tif s.Internal.StorageBestAlloc == nil {\n\t\treturn *new([]storiface.StorageInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StorageBestAlloc(p0, p1, p2, p3)\n}\n```",
|
"description": "```go\nfunc (s *StorageMinerStruct) StorageBestAlloc(p0 context.Context, p1 storiface.SectorFileType, p2 abi.SectorSize, p3 storiface.PathType, p4 abi.ActorID) ([]storiface.StorageInfo, error) {\n\tif s.Internal.StorageBestAlloc == nil {\n\t\treturn *new([]storiface.StorageInfo), ErrNotSupported\n\t}\n\treturn s.Internal.StorageBestAlloc(p0, p1, p2, p3, p4)\n}\n```",
|
||||||
"summary": "StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference.\nPaths with more weight and more % of free space are preferred.\nNote: This method doesn't filter paths based on AllowTypes/DenyTypes.\n",
|
"summary": "StorageBestAlloc returns list of paths where sector files of the specified type can be allocated, ordered by preference.\nPaths with more weight and more % of free space are preferred.\nNote: This method doesn't filter paths based on AllowTypes/DenyTypes.\n",
|
||||||
"paramStructure": "by-position",
|
"paramStructure": "by-position",
|
||||||
"params": [
|
"params": [
|
||||||
@ -9130,6 +9148,23 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"deprecated": false
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p4",
|
||||||
|
"description": "abi.ActorID",
|
||||||
|
"summary": "",
|
||||||
|
"schema": {
|
||||||
|
"title": "number",
|
||||||
|
"description": "Number is a number",
|
||||||
|
"examples": [
|
||||||
|
1000
|
||||||
|
],
|
||||||
|
"type": [
|
||||||
|
"number"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"result": {
|
"result": {
|
||||||
@ -9159,6 +9194,12 @@
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -9167,6 +9208,12 @@
|
|||||||
{
|
{
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"AllowMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"AllowTo": {
|
"AllowTo": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9185,6 +9232,12 @@
|
|||||||
"CanStore": {
|
"CanStore": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"DenyMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"DenyTypes": {
|
"DenyTypes": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9620,6 +9673,12 @@
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -9628,6 +9687,12 @@
|
|||||||
{
|
{
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"AllowMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"AllowTypes": {
|
"AllowTypes": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9646,6 +9711,12 @@
|
|||||||
"CanStore": {
|
"CanStore": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"DenyMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"DenyTypes": {
|
"DenyTypes": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9833,11 +9904,23 @@
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"AllowMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"AllowTo": {
|
"AllowTo": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -9856,6 +9939,12 @@
|
|||||||
"CanStore": {
|
"CanStore": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"DenyMiners": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
"DenyTypes": {
|
"DenyTypes": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/cmd/curio/deps"
|
"github.com/filecoin-project/lotus/cmd/curio/deps"
|
||||||
curio "github.com/filecoin-project/lotus/curiosrc"
|
curio "github.com/filecoin-project/lotus/curiosrc"
|
||||||
"github.com/filecoin-project/lotus/curiosrc/chainsched"
|
"github.com/filecoin-project/lotus/curiosrc/chainsched"
|
||||||
@ -37,6 +39,20 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task
|
|||||||
si := dependencies.Si
|
si := dependencies.Si
|
||||||
var activeTasks []harmonytask.TaskInterface
|
var activeTasks []harmonytask.TaskInterface
|
||||||
|
|
||||||
|
// Get all miner address from config
|
||||||
|
var miners []address.Address
|
||||||
|
for _, ad := range cfg.Addresses {
|
||||||
|
ad := ad
|
||||||
|
for _, m := range ad.MinerAddresses {
|
||||||
|
m := m
|
||||||
|
maddr, err := address.NewFromString(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to parse the miner address: %w", err)
|
||||||
|
}
|
||||||
|
miners = append(miners, maddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sender, sendTask := message.NewSender(full, full, db)
|
sender, sendTask := message.NewSender(full, full, db)
|
||||||
activeTasks = append(activeTasks, sendTask)
|
activeTasks = append(activeTasks, sendTask)
|
||||||
|
|
||||||
@ -76,7 +92,7 @@ func StartTasks(ctx context.Context, dependencies *deps.Deps) (*harmonytask.Task
|
|||||||
{
|
{
|
||||||
// Piece handling
|
// Piece handling
|
||||||
if cfg.Subsystems.EnableParkPiece {
|
if cfg.Subsystems.EnableParkPiece {
|
||||||
parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks)
|
parkPieceTask := piece.NewParkPieceTask(db, must.One(slrLazy.Val()), cfg.Subsystems.ParkPieceMaxTasks, miners)
|
||||||
cleanupPieceTask := piece.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0)
|
cleanupPieceTask := piece.NewCleanupPieceTask(db, must.One(slrLazy.Val()), 0)
|
||||||
activeTasks = append(activeTasks, parkPieceTask, cleanupPieceTask)
|
activeTasks = append(activeTasks, parkPieceTask, cleanupPieceTask)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func (sb *SealCalls) Storage(taskToSectorRef func(taskID harmonytask.TaskID) (Se
|
|||||||
func (t *TaskStorage) HasCapacity() bool {
|
func (t *TaskStorage) HasCapacity() bool {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType)
|
paths, err := t.sc.sectors.sindex.StorageBestAlloc(ctx, t.alloc, t.ssize, t.pathType, abi.ActorID(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("finding best alloc in HasCapacity: %+v", err)
|
log.Errorf("finding best alloc in HasCapacity: %+v", err)
|
||||||
return false
|
return false
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
logging "github.com/ipfs/go-log/v2"
|
logging "github.com/ipfs/go-log/v2"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/curiosrc/ffi"
|
"github.com/filecoin-project/lotus/curiosrc/ffi"
|
||||||
"github.com/filecoin-project/lotus/curiosrc/seal"
|
"github.com/filecoin-project/lotus/curiosrc/seal"
|
||||||
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
|
"github.com/filecoin-project/lotus/lib/harmony/harmonydb"
|
||||||
@ -32,13 +34,14 @@ type ParkPieceTask struct {
|
|||||||
max int
|
max int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int) *ParkPieceTask {
|
func NewParkPieceTask(db *harmonydb.DB, sc *ffi.SealCalls, max int, miners []address.Address) *ParkPieceTask {
|
||||||
pt := &ParkPieceTask{
|
pt := &ParkPieceTask{
|
||||||
db: db,
|
db: db,
|
||||||
sc: sc,
|
sc: sc,
|
||||||
|
|
||||||
max: max,
|
max: max,
|
||||||
}
|
}
|
||||||
|
|
||||||
go pt.pollPieceTasks(context.Background())
|
go pt.pollPieceTasks(context.Background())
|
||||||
return pt
|
return pt
|
||||||
}
|
}
|
||||||
|
@ -393,6 +393,8 @@ type machineInfo struct {
|
|||||||
FSAvailable int64
|
FSAvailable int64
|
||||||
Reserved int64
|
Reserved int64
|
||||||
Used int64
|
Used int64
|
||||||
|
AllowMiners string
|
||||||
|
DenyMiners string
|
||||||
LastHeartbeat time.Time
|
LastHeartbeat time.Time
|
||||||
HeartbeatErr *string
|
HeartbeatErr *string
|
||||||
|
|
||||||
@ -458,7 +460,7 @@ func (a *app) clusterNodeInfo(ctx context.Context, id int64) (*machineInfo, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// query storage info
|
// query storage info
|
||||||
rows2, err := a.db.Query(ctx, "SELECT storage_id, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, capacity, available, fs_available, reserved, used, last_heartbeat, heartbeat_err FROM storage_path WHERE urls LIKE '%' || $1 || '%'", summaries[0].Info.Host)
|
rows2, err := a.db.Query(ctx, "SELECT storage_id, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, capacity, available, fs_available, reserved, used, allow_miners, deny_miners, last_heartbeat, heartbeat_err FROM storage_path WHERE urls LIKE '%' || $1 || '%'", summaries[0].Info.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -481,13 +483,15 @@ func (a *app) clusterNodeInfo(ctx context.Context, id int64) (*machineInfo, erro
|
|||||||
FSAvailable int64
|
FSAvailable int64
|
||||||
Reserved int64
|
Reserved int64
|
||||||
Used int64
|
Used int64
|
||||||
|
AllowMiners string
|
||||||
|
DenyMiners string
|
||||||
LastHeartbeat time.Time
|
LastHeartbeat time.Time
|
||||||
HeartbeatErr *string
|
HeartbeatErr *string
|
||||||
|
|
||||||
UsedPercent float64
|
UsedPercent float64
|
||||||
ReservedPercent float64
|
ReservedPercent float64
|
||||||
}
|
}
|
||||||
if err := rows2.Scan(&s.ID, &s.Weight, &s.MaxStorage, &s.CanSeal, &s.CanStore, &s.Groups, &s.AllowTo, &s.AllowTypes, &s.DenyTypes, &s.Capacity, &s.Available, &s.FSAvailable, &s.Reserved, &s.Used, &s.LastHeartbeat, &s.HeartbeatErr); err != nil {
|
if err := rows2.Scan(&s.ID, &s.Weight, &s.MaxStorage, &s.CanSeal, &s.CanStore, &s.Groups, &s.AllowTo, &s.AllowTypes, &s.DenyTypes, &s.Capacity, &s.Available, &s.FSAvailable, &s.Reserved, &s.Used, &s.AllowMiners, &s.DenyMiners, &s.LastHeartbeat, &s.HeartbeatErr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +179,12 @@ Response:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -218,6 +224,12 @@ Response:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -3735,6 +3735,12 @@ Inputs:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -3782,7 +3788,8 @@ Inputs:
|
|||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
34359738368,
|
34359738368,
|
||||||
"sealing"
|
"sealing",
|
||||||
|
1000
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -3809,6 +3816,12 @@ Response:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -3928,6 +3941,12 @@ Response:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -4004,6 +4023,12 @@ Response:
|
|||||||
],
|
],
|
||||||
"DenyTypes": [
|
"DenyTypes": [
|
||||||
"string value"
|
"string value"
|
||||||
|
],
|
||||||
|
"AllowMiners": [
|
||||||
|
"string value"
|
||||||
|
],
|
||||||
|
"DenyMiners": [
|
||||||
|
"string value"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE storage_path ADD COLUMN IF NOT EXISTS allow_miners varchar, ADD COLUMN IF NOT EXISTS deny_miners varchar; -- comma separated list of miner addresses
|
@ -15,6 +15,7 @@ import (
|
|||||||
"go.opencensus.io/tag"
|
"go.opencensus.io/tag"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/journal/alerting"
|
"github.com/filecoin-project/lotus/journal/alerting"
|
||||||
@ -200,7 +201,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo,
|
|||||||
currUrls = union(currUrls, si.URLs)
|
currUrls = union(currUrls, si.URLs)
|
||||||
|
|
||||||
_, err = tx.Exec(
|
_, err = tx.Exec(
|
||||||
"UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, last_heartbeat=NOW() WHERE storage_id=$10",
|
"UPDATE storage_path set urls=$1, weight=$2, max_storage=$3, can_seal=$4, can_store=$5, groups=$6, allow_to=$7, allow_types=$8, deny_types=$9, allow_miners=$10, deny_miners=$11, last_heartbeat=NOW() WHERE storage_id=$12",
|
||||||
strings.Join(currUrls, ","),
|
strings.Join(currUrls, ","),
|
||||||
si.Weight,
|
si.Weight,
|
||||||
si.MaxStorage,
|
si.MaxStorage,
|
||||||
@ -210,6 +211,8 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo,
|
|||||||
strings.Join(si.AllowTo, ","),
|
strings.Join(si.AllowTo, ","),
|
||||||
strings.Join(si.AllowTypes, ","),
|
strings.Join(si.AllowTypes, ","),
|
||||||
strings.Join(si.DenyTypes, ","),
|
strings.Join(si.DenyTypes, ","),
|
||||||
|
strings.Join(si.AllowMiners, ","),
|
||||||
|
strings.Join(si.DenyMiners, ","),
|
||||||
si.ID)
|
si.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, xerrors.Errorf("storage attach UPDATE fails: %v", err)
|
return false, xerrors.Errorf("storage attach UPDATE fails: %v", err)
|
||||||
@ -221,7 +224,7 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo,
|
|||||||
// Insert storage id
|
// Insert storage id
|
||||||
_, err = tx.Exec(
|
_, err = tx.Exec(
|
||||||
"INSERT INTO storage_path "+
|
"INSERT INTO storage_path "+
|
||||||
"Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())",
|
"Values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW(), $16, $17)",
|
||||||
si.ID,
|
si.ID,
|
||||||
strings.Join(si.URLs, ","),
|
strings.Join(si.URLs, ","),
|
||||||
si.Weight,
|
si.Weight,
|
||||||
@ -236,7 +239,9 @@ func (dbi *DBIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo,
|
|||||||
st.Available,
|
st.Available,
|
||||||
st.FSAvailable,
|
st.FSAvailable,
|
||||||
st.Reserved,
|
st.Reserved,
|
||||||
st.Used)
|
st.Used,
|
||||||
|
strings.Join(si.AllowMiners, ","),
|
||||||
|
strings.Join(si.DenyMiners, ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, xerrors.Errorf("StorageAttach insert fails: %v", err)
|
return false, xerrors.Errorf("StorageAttach insert fails: %v", err)
|
||||||
}
|
}
|
||||||
@ -532,14 +537,16 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st
|
|||||||
// 7. Storage path is part of the groups which are allowed from the storage paths which already hold the sector
|
// 7. Storage path is part of the groups which are allowed from the storage paths which already hold the sector
|
||||||
|
|
||||||
var rows []struct {
|
var rows []struct {
|
||||||
StorageId string
|
StorageId string
|
||||||
Urls string
|
Urls string
|
||||||
Weight uint64
|
Weight uint64
|
||||||
CanSeal bool
|
CanSeal bool
|
||||||
CanStore bool
|
CanStore bool
|
||||||
Groups string
|
Groups string
|
||||||
AllowTypes string
|
AllowTypes string
|
||||||
DenyTypes string
|
DenyTypes string
|
||||||
|
AllowMiners string
|
||||||
|
DenyMiners string
|
||||||
}
|
}
|
||||||
err = dbi.harmonyDB.Select(ctx, &rows,
|
err = dbi.harmonyDB.Select(ctx, &rows,
|
||||||
`SELECT storage_id,
|
`SELECT storage_id,
|
||||||
@ -549,7 +556,9 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st
|
|||||||
can_store,
|
can_store,
|
||||||
groups,
|
groups,
|
||||||
allow_types,
|
allow_types,
|
||||||
deny_types
|
deny_types,
|
||||||
|
allow_miners,
|
||||||
|
deny_miners
|
||||||
FROM storage_path
|
FROM storage_path
|
||||||
WHERE can_seal=true
|
WHERE can_seal=true
|
||||||
and available >= $1
|
and available >= $1
|
||||||
@ -570,6 +579,53 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(row.AllowMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
allowMiners := splitString(row.AllowMiners)
|
||||||
|
for _, m := range allowMiners {
|
||||||
|
m := m
|
||||||
|
maddr, err := address.NewFromString(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == s.Miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Debugf("not selecting on %s, not allowed by allow miners filters", row.StorageId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(row.DenyMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
denyMiners := splitString(row.DenyMiners)
|
||||||
|
for _, m := range denyMiners {
|
||||||
|
m := m
|
||||||
|
maddr, err := address.NewFromString(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == s.Miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
log.Debugf("not selecting on %s, not allowed by deny miners filters", row.StorageId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if allowList != nil {
|
if allowList != nil {
|
||||||
groups := splitString(row.Groups)
|
groups := splitString(row.Groups)
|
||||||
allow := false
|
allow := false
|
||||||
@ -618,19 +674,21 @@ func (dbi *DBIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft st
|
|||||||
func (dbi *DBIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) {
|
func (dbi *DBIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) {
|
||||||
|
|
||||||
var qResults []struct {
|
var qResults []struct {
|
||||||
Urls string
|
Urls string
|
||||||
Weight uint64
|
Weight uint64
|
||||||
MaxStorage uint64
|
MaxStorage uint64
|
||||||
CanSeal bool
|
CanSeal bool
|
||||||
CanStore bool
|
CanStore bool
|
||||||
Groups string
|
Groups string
|
||||||
AllowTo string
|
AllowTo string
|
||||||
AllowTypes string
|
AllowTypes string
|
||||||
DenyTypes string
|
DenyTypes string
|
||||||
|
AllowMiners string
|
||||||
|
DenyMiners string
|
||||||
}
|
}
|
||||||
|
|
||||||
err := dbi.harmonyDB.Select(ctx, &qResults,
|
err := dbi.harmonyDB.Select(ctx, &qResults,
|
||||||
"SELECT urls, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types "+
|
"SELECT urls, weight, max_storage, can_seal, can_store, groups, allow_to, allow_types, deny_types, allow_miners, deny_miners "+
|
||||||
"FROM storage_path WHERE storage_id=$1", string(id))
|
"FROM storage_path WHERE storage_id=$1", string(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return storiface.StorageInfo{}, xerrors.Errorf("StorageInfo query fails: %v", err)
|
return storiface.StorageInfo{}, xerrors.Errorf("StorageInfo query fails: %v", err)
|
||||||
@ -647,11 +705,13 @@ func (dbi *DBIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface
|
|||||||
sinfo.AllowTo = splitString(qResults[0].AllowTo)
|
sinfo.AllowTo = splitString(qResults[0].AllowTo)
|
||||||
sinfo.AllowTypes = splitString(qResults[0].AllowTypes)
|
sinfo.AllowTypes = splitString(qResults[0].AllowTypes)
|
||||||
sinfo.DenyTypes = splitString(qResults[0].DenyTypes)
|
sinfo.DenyTypes = splitString(qResults[0].DenyTypes)
|
||||||
|
sinfo.AllowMiners = splitString(qResults[0].AllowMiners)
|
||||||
|
sinfo.DenyMiners = splitString(qResults[0].DenyMiners)
|
||||||
|
|
||||||
return sinfo, nil
|
return sinfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) {
|
func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error) {
|
||||||
var err error
|
var err error
|
||||||
var spaceReq uint64
|
var spaceReq uint64
|
||||||
switch pathType {
|
switch pathType {
|
||||||
@ -667,16 +727,18 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rows []struct {
|
var rows []struct {
|
||||||
StorageId string
|
StorageId string
|
||||||
Urls string
|
Urls string
|
||||||
Weight uint64
|
Weight uint64
|
||||||
MaxStorage uint64
|
MaxStorage uint64
|
||||||
CanSeal bool
|
CanSeal bool
|
||||||
CanStore bool
|
CanStore bool
|
||||||
Groups string
|
Groups string
|
||||||
AllowTo string
|
AllowTo string
|
||||||
AllowTypes string
|
AllowTypes string
|
||||||
DenyTypes string
|
DenyTypes string
|
||||||
|
AllowMiners string
|
||||||
|
DenyMiners string
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dbi.harmonyDB.Select(ctx, &rows,
|
err = dbi.harmonyDB.Select(ctx, &rows,
|
||||||
@ -689,7 +751,9 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec
|
|||||||
groups,
|
groups,
|
||||||
allow_to,
|
allow_to,
|
||||||
allow_types,
|
allow_types,
|
||||||
deny_types
|
deny_types,
|
||||||
|
allow_miners,
|
||||||
|
deny_miners
|
||||||
FROM storage_path
|
FROM storage_path
|
||||||
WHERE available >= $1
|
WHERE available >= $1
|
||||||
and NOW()-($2 * INTERVAL '1 second') < last_heartbeat
|
and NOW()-($2 * INTERVAL '1 second') < last_heartbeat
|
||||||
@ -700,24 +764,77 @@ func (dbi *DBIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sec
|
|||||||
SkippedHeartbeatThresh.Seconds(),
|
SkippedHeartbeatThresh.Seconds(),
|
||||||
pathType == storiface.PathSealing,
|
pathType == storiface.PathSealing,
|
||||||
pathType == storiface.PathStorage,
|
pathType == storiface.PathStorage,
|
||||||
|
miner.String(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("Querying for best storage sectors fails with err %w: ", err)
|
return nil, xerrors.Errorf("Querying for best storage sectors fails with err %w: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []storiface.StorageInfo
|
var result []storiface.StorageInfo
|
||||||
|
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
|
// Matching with 0 as a workaround to avoid having minerID
|
||||||
|
// present when calling TaskStorage.HasCapacity()
|
||||||
|
if !(miner == abi.ActorID(0)) {
|
||||||
|
if len(row.AllowMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
allowMiners := splitString(row.AllowMiners)
|
||||||
|
for _, m := range allowMiners {
|
||||||
|
m := m
|
||||||
|
maddr, err := address.NewFromString(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(row.DenyMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
denyMiners := splitString(row.DenyMiners)
|
||||||
|
for _, m := range denyMiners {
|
||||||
|
m := m
|
||||||
|
maddr, err := address.NewFromString(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = append(result, storiface.StorageInfo{
|
result = append(result, storiface.StorageInfo{
|
||||||
ID: storiface.ID(row.StorageId),
|
ID: storiface.ID(row.StorageId),
|
||||||
URLs: splitString(row.Urls),
|
URLs: splitString(row.Urls),
|
||||||
Weight: row.Weight,
|
Weight: row.Weight,
|
||||||
MaxStorage: row.MaxStorage,
|
MaxStorage: row.MaxStorage,
|
||||||
CanSeal: row.CanSeal,
|
CanSeal: row.CanSeal,
|
||||||
CanStore: row.CanStore,
|
CanStore: row.CanStore,
|
||||||
Groups: splitString(row.Groups),
|
Groups: splitString(row.Groups),
|
||||||
AllowTo: splitString(row.AllowTo),
|
AllowTo: splitString(row.AllowTo),
|
||||||
AllowTypes: splitString(row.AllowTypes),
|
AllowTypes: splitString(row.AllowTypes),
|
||||||
DenyTypes: splitString(row.DenyTypes),
|
DenyTypes: splitString(row.DenyTypes),
|
||||||
|
AllowMiners: splitString(row.AllowMiners),
|
||||||
|
DenyMiners: splitString(row.DenyMiners),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"go.opencensus.io/tag"
|
"go.opencensus.io/tag"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ type SectorIndex interface { // part of storage-miner api
|
|||||||
StorageDropSector(ctx context.Context, storageID storiface.ID, s abi.SectorID, ft storiface.SectorFileType) 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)
|
StorageFindSector(ctx context.Context, sector abi.SectorID, ft storiface.SectorFileType, ssize abi.SectorSize, allowFetch bool) ([]storiface.SectorStorageInfo, error)
|
||||||
|
|
||||||
StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error)
|
StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType, miner abi.ActorID) ([]storiface.StorageInfo, error)
|
||||||
|
|
||||||
// atomically acquire locks on all sector file types. close ctx to unlock
|
// atomically acquire locks on all sector file types. close ctx to unlock
|
||||||
StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error
|
StorageLock(ctx context.Context, sector abi.SectorID, read storiface.SectorFileType, write storiface.SectorFileType) error
|
||||||
@ -61,6 +62,7 @@ type storageEntry struct {
|
|||||||
heartbeatErr error
|
heartbeatErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MemIndex represents an in-memory index of storage sectors and storage entries.
|
||||||
type MemIndex struct {
|
type MemIndex struct {
|
||||||
*indexLocks
|
*indexLocks
|
||||||
lk sync.RWMutex
|
lk sync.RWMutex
|
||||||
@ -206,6 +208,8 @@ func (i *MemIndex) StorageAttach(ctx context.Context, si storiface.StorageInfo,
|
|||||||
i.stores[si.ID].info.AllowTo = si.AllowTo
|
i.stores[si.ID].info.AllowTo = si.AllowTo
|
||||||
i.stores[si.ID].info.AllowTypes = allow
|
i.stores[si.ID].info.AllowTypes = allow
|
||||||
i.stores[si.ID].info.DenyTypes = deny
|
i.stores[si.ID].info.DenyTypes = deny
|
||||||
|
i.stores[si.ID].info.AllowMiners = si.AllowMiners
|
||||||
|
i.stores[si.ID].info.DenyMiners = si.DenyMiners
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -476,8 +480,10 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto
|
|||||||
|
|
||||||
Primary: isprimary[id],
|
Primary: isprimary[id],
|
||||||
|
|
||||||
AllowTypes: st.info.AllowTypes,
|
AllowTypes: st.info.AllowTypes,
|
||||||
DenyTypes: st.info.DenyTypes,
|
DenyTypes: st.info.DenyTypes,
|
||||||
|
AllowMiners: st.info.AllowMiners,
|
||||||
|
DenyMiners: st.info.DenyMiners,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,6 +498,52 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(st.info.AllowMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range st.info.AllowMiners {
|
||||||
|
minerIDStr := m
|
||||||
|
maddr, err := address.NewFromString(minerIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("converting miner address to ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == s.Miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Debugf("not allocating on %s, miner %s not allowed", st.info.ID, s.Miner.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(st.info.DenyMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range st.info.DenyMiners {
|
||||||
|
minerIDStr := m
|
||||||
|
maddr, err := address.NewFromString(minerIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("converting miner address to ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == s.Miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
log.Debugf("not allocating on %s, miner %s denied", st.info.ID, s.Miner.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if spaceReq > uint64(st.fsi.Available) {
|
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)
|
log.Debugf("not selecting on %s, out of space (available: %d, need: %d)", st.info.ID, st.fsi.Available, spaceReq)
|
||||||
continue
|
continue
|
||||||
@ -555,8 +607,10 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto
|
|||||||
|
|
||||||
Primary: false,
|
Primary: false,
|
||||||
|
|
||||||
AllowTypes: st.info.AllowTypes,
|
AllowTypes: st.info.AllowTypes,
|
||||||
DenyTypes: st.info.DenyTypes,
|
DenyTypes: st.info.DenyTypes,
|
||||||
|
AllowMiners: st.info.AllowMiners,
|
||||||
|
DenyMiners: st.info.DenyMiners,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,6 +618,21 @@ func (i *MemIndex) StorageFindSector(ctx context.Context, s abi.SectorID, ft sto
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) {
|
func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.StorageInfo, error) {
|
||||||
i.lk.RLock()
|
i.lk.RLock()
|
||||||
defer i.lk.RUnlock()
|
defer i.lk.RUnlock()
|
||||||
@ -576,7 +645,33 @@ func (i *MemIndex) StorageInfo(ctx context.Context, id storiface.ID) (storiface.
|
|||||||
return *si.info, nil
|
return *si.info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.SectorFileType, ssize abi.SectorSize, pathType storiface.PathType) ([]storiface.StorageInfo, error) {
|
// 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) {
|
||||||
i.lk.RLock()
|
i.lk.RLock()
|
||||||
defer i.lk.RUnlock()
|
defer i.lk.RUnlock()
|
||||||
|
|
||||||
@ -604,6 +699,52 @@ func (i *MemIndex) StorageBestAlloc(ctx context.Context, allocate storiface.Sect
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(p.info.AllowMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range p.info.AllowMiners {
|
||||||
|
minerIDStr := m
|
||||||
|
maddr, err := address.NewFromString(minerIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("converting miner address to ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Debugf("not allocating on %s, miner %s not allowed", p.info.ID, miner.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.info.DenyMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range p.info.DenyMiners {
|
||||||
|
minerIDStr := m
|
||||||
|
maddr, err := address.NewFromString(minerIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("parsing miner address: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := address.IDFromAddress(maddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("converting miner address to ID: %w", err)
|
||||||
|
}
|
||||||
|
if abi.ActorID(mid) == miner {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
log.Debugf("not allocating on %s, miner %s denied", p.info.ID, miner.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if spaceReq > uint64(p.fsi.Available) {
|
if spaceReq > uint64(p.fsi.Available) {
|
||||||
log.Debugf("not allocating on %s, out of space (available: %d, need: %d)", p.info.ID, p.fsi.Available, spaceReq)
|
log.Debugf("not allocating on %s, out of space (available: %d, need: %d)", p.info.ID, p.fsi.Available, spaceReq)
|
||||||
continue
|
continue
|
||||||
|
@ -152,3 +152,61 @@ func TestFindAllow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStorageBestAlloc(t *testing.T) {
|
||||||
|
idx := NewMemIndex(nil)
|
||||||
|
|
||||||
|
dummyStorageInfo := storiface.StorageInfo{
|
||||||
|
ID: storiface.ID("dummy"),
|
||||||
|
CanSeal: true,
|
||||||
|
CanStore: true,
|
||||||
|
URLs: []string{"http://localhost:9999/"},
|
||||||
|
Weight: 10,
|
||||||
|
AllowMiners: []string{
|
||||||
|
"t001",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dummyFsStat := fsutil.FsStat{
|
||||||
|
Capacity: 10000000000,
|
||||||
|
Available: 7000000000,
|
||||||
|
Reserved: 100000000,
|
||||||
|
Used: 3000000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := idx.StorageAttach(context.Background(), dummyStorageInfo, dummyFsStat)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("PathSealing", func(t *testing.T) {
|
||||||
|
result, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 1)
|
||||||
|
require.Equal(t, err, nil)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, len(result), 1)
|
||||||
|
require.Equal(t, result[0].ID, dummyStorageInfo.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PathStorage", func(t *testing.T) {
|
||||||
|
result, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathStorage, 1)
|
||||||
|
require.Equal(t, err, nil)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Equal(t, len(result), 1)
|
||||||
|
require.Equal(t, result[0].ID, dummyStorageInfo.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("NotAllowedMiner", func(t *testing.T) {
|
||||||
|
_, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 2)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("NoAvailableSpace", func(t *testing.T) {
|
||||||
|
bigSectorSize := abi.SectorSize(10000000000)
|
||||||
|
_, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, bigSectorSize, storiface.PathSealing, 1)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AllowedMiner", func(t *testing.T) {
|
||||||
|
_, err := idx.StorageBestAlloc(context.Background(), storiface.FTUnsealed, 123, storiface.PathSealing, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/proof"
|
"github.com/filecoin-project/go-state-types/proof"
|
||||||
|
|
||||||
@ -204,16 +205,18 @@ func (st *Local) OpenPath(ctx context.Context, p string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = st.index.StorageAttach(ctx, storiface.StorageInfo{
|
err = st.index.StorageAttach(ctx, storiface.StorageInfo{
|
||||||
ID: meta.ID,
|
ID: meta.ID,
|
||||||
URLs: st.urls,
|
URLs: st.urls,
|
||||||
Weight: meta.Weight,
|
Weight: meta.Weight,
|
||||||
MaxStorage: meta.MaxStorage,
|
MaxStorage: meta.MaxStorage,
|
||||||
CanSeal: meta.CanSeal,
|
CanSeal: meta.CanSeal,
|
||||||
CanStore: meta.CanStore,
|
CanStore: meta.CanStore,
|
||||||
Groups: meta.Groups,
|
Groups: meta.Groups,
|
||||||
AllowTo: meta.AllowTo,
|
AllowTo: meta.AllowTo,
|
||||||
AllowTypes: meta.AllowTypes,
|
AllowTypes: meta.AllowTypes,
|
||||||
DenyTypes: meta.DenyTypes,
|
DenyTypes: meta.DenyTypes,
|
||||||
|
AllowMiners: meta.AllowMiners,
|
||||||
|
DenyMiners: meta.DenyMiners,
|
||||||
}, 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)
|
||||||
@ -294,16 +297,18 @@ func (st *Local) Redeclare(ctx context.Context, filterId *storiface.ID, dropMiss
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = st.index.StorageAttach(ctx, storiface.StorageInfo{
|
err = st.index.StorageAttach(ctx, storiface.StorageInfo{
|
||||||
ID: id,
|
ID: id,
|
||||||
URLs: st.urls,
|
URLs: st.urls,
|
||||||
Weight: meta.Weight,
|
Weight: meta.Weight,
|
||||||
MaxStorage: meta.MaxStorage,
|
MaxStorage: meta.MaxStorage,
|
||||||
CanSeal: meta.CanSeal,
|
CanSeal: meta.CanSeal,
|
||||||
CanStore: meta.CanStore,
|
CanStore: meta.CanStore,
|
||||||
Groups: meta.Groups,
|
Groups: meta.Groups,
|
||||||
AllowTo: meta.AllowTo,
|
AllowTo: meta.AllowTo,
|
||||||
AllowTypes: meta.AllowTypes,
|
AllowTypes: meta.AllowTypes,
|
||||||
DenyTypes: meta.DenyTypes,
|
DenyTypes: meta.DenyTypes,
|
||||||
|
AllowMiners: meta.AllowMiners,
|
||||||
|
DenyMiners: meta.DenyMiners,
|
||||||
}, 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)
|
||||||
@ -495,20 +500,64 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi
|
|||||||
var out storiface.SectorPaths
|
var out storiface.SectorPaths
|
||||||
var storageIDs storiface.SectorPaths
|
var storageIDs storiface.SectorPaths
|
||||||
|
|
||||||
allocPathOk := func(canSeal, canStore bool, allowTypes, denyTypes []string, fileType storiface.SectorFileType) bool {
|
allocPathOk := func(canSeal, canStore bool, allowTypes, denyTypes, allowMiners, denyMiners []string, fileType storiface.SectorFileType, miner abi.ActorID) (bool, error) {
|
||||||
if (pathType == storiface.PathSealing) && !canSeal {
|
if (pathType == storiface.PathSealing) && !canSeal {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pathType == storiface.PathStorage) && !canStore {
|
if (pathType == storiface.PathStorage) && !canStore {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fileType.Allowed(allowTypes, denyTypes) {
|
if !fileType.Allowed(allowTypes, denyTypes) {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
if len(allowMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range allowMiners {
|
||||||
|
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 {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(denyMiners) > 0 {
|
||||||
|
found := false
|
||||||
|
for _, m := range denyMiners {
|
||||||
|
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 {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// First find existing files
|
// First find existing files
|
||||||
@ -536,8 +585,15 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if allocate.Has(fileType) && !allocPathOk(info.CanSeal, info.CanStore, info.AllowTypes, info.DenyTypes, fileType) {
|
if allocate.Has(fileType) {
|
||||||
continue // allocate request for a path of different type
|
ok, err := allocPathOk(info.CanSeal, info.CanStore, info.AllowTypes, info.DenyTypes, info.AllowMiners, info.DenyMiners, fileType, sid.ID.Miner)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue // allocate request for a path of different type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spath := p.sectorPath(sid.ID, fileType)
|
spath := p.sectorPath(sid.ID, fileType)
|
||||||
@ -556,7 +612,7 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sis, err := st.index.StorageBestAlloc(ctx, fileType, ssize, pathType)
|
sis, err := st.index.StorageBestAlloc(ctx, fileType, ssize, pathType, sid.ID.Miner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("finding best storage for allocating : %w", err)
|
return storiface.SectorPaths{}, storiface.SectorPaths{}, xerrors.Errorf("finding best storage for allocating : %w", err)
|
||||||
}
|
}
|
||||||
@ -574,7 +630,14 @@ func (st *Local) AcquireSector(ctx context.Context, sid storiface.SectorRef, exi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !allocPathOk(si.CanSeal, si.CanStore, si.AllowTypes, si.DenyTypes, fileType) {
|
alloc, err := allocPathOk(si.CanSeal, si.CanStore, si.AllowTypes, si.DenyTypes, si.AllowMiners, si.DenyMiners, fileType, sid.ID.Miner)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !alloc {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,18 +54,18 @@ func (mr *MockSectorIndexMockRecorder) StorageAttach(arg0, arg1, arg2 interface{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StorageBestAlloc mocks base method.
|
// StorageBestAlloc mocks base method.
|
||||||
func (m *MockSectorIndex) StorageBestAlloc(arg0 context.Context, arg1 storiface.SectorFileType, arg2 abi.SectorSize, arg3 storiface.PathType) ([]storiface.StorageInfo, error) {
|
func (m *MockSectorIndex) StorageBestAlloc(arg0 context.Context, arg1 storiface.SectorFileType, arg2 abi.SectorSize, arg3 storiface.PathType, arg4 abi.ActorID) ([]storiface.StorageInfo, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "StorageBestAlloc", arg0, arg1, arg2, arg3)
|
ret := m.ctrl.Call(m, "StorageBestAlloc", arg0, arg1, arg2, arg3, arg4)
|
||||||
ret0, _ := ret[0].([]storiface.StorageInfo)
|
ret0, _ := ret[0].([]storiface.StorageInfo)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageBestAlloc indicates an expected call of StorageBestAlloc.
|
// StorageBestAlloc indicates an expected call of StorageBestAlloc.
|
||||||
func (mr *MockSectorIndexMockRecorder) StorageBestAlloc(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
func (mr *MockSectorIndexMockRecorder) StorageBestAlloc(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageBestAlloc", reflect.TypeOf((*MockSectorIndex)(nil).StorageBestAlloc), arg0, arg1, arg2, arg3)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageBestAlloc", reflect.TypeOf((*MockSectorIndex)(nil).StorageBestAlloc), arg0, arg1, arg2, arg3, arg4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageDeclareSector mocks base method.
|
// StorageDeclareSector mocks base method.
|
||||||
|
@ -356,7 +356,7 @@ func (m *Manager) SectorsUnsealPiece(ctx context.Context, sector storiface.Secto
|
|||||||
|
|
||||||
// selector will schedule the Unseal task on a worker that either already has the sealed sector files or has space in
|
// selector will schedule the Unseal task on a worker that either already has the sealed sector files or has space in
|
||||||
// one of it's sealing scratch spaces to store them after fetching them from another worker.
|
// one of it's sealing scratch spaces to store them after fetching them from another worker.
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTSealed|storiface.FTCache, true)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTSealed|storiface.FTCache, sector.ID.Miner, true)
|
||||||
|
|
||||||
log.Debugf("will schedule unseal for sector %d", sector.ID)
|
log.Debugf("will schedule unseal for sector %d", sector.ID)
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTUnseal, selector, unsealFetch, func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTUnseal, selector, unsealFetch, func(ctx context.Context, w Worker) error {
|
||||||
@ -377,7 +377,7 @@ func (m *Manager) SectorsUnsealPiece(ctx context.Context, sector storiface.Secto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get a selector for moving unsealed sector into long-term storage
|
// get a selector for moving unsealed sector into long-term storage
|
||||||
fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTUnsealed, storiface.PathStorage, !m.disallowRemoteFinalize)
|
fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTUnsealed, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize)
|
||||||
|
|
||||||
// move unsealed sector to long-term storage
|
// move unsealed sector to long-term storage
|
||||||
// Possible TODO: Add an option to not keep the unsealed sector in long term storage?
|
// Possible TODO: Add an option to not keep the unsealed sector in long term storage?
|
||||||
@ -431,9 +431,9 @@ func (m *Manager) AddPiece(ctx context.Context, sector storiface.SectorRef, exis
|
|||||||
var selector WorkerSelector
|
var selector WorkerSelector
|
||||||
var err error
|
var err error
|
||||||
if len(existingPieces) == 0 { // new
|
if len(existingPieces) == 0 { // new
|
||||||
selector = newAllocSelector(m.index, storiface.FTUnsealed, storiface.PathSealing)
|
selector = newAllocSelector(m.index, storiface.FTUnsealed, storiface.PathSealing, sector.ID.Miner)
|
||||||
} else { // use existing
|
} else { // use existing
|
||||||
selector = newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, false)
|
selector = newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, sector.ID.Miner, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var out abi.PieceInfo
|
var out abi.PieceInfo
|
||||||
@ -484,7 +484,7 @@ func (m *Manager) SealPreCommit1(ctx context.Context, sector storiface.SectorRef
|
|||||||
|
|
||||||
// TODO: also consider where the unsealed data sits
|
// TODO: also consider where the unsealed data sits
|
||||||
|
|
||||||
selector := newAllocSelector(m.index, storiface.FTCache|storiface.FTSealed, storiface.PathSealing)
|
selector := newAllocSelector(m.index, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, sector.ID.Miner)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit1, selector, m.schedFetch(sector, storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit1, selector, m.schedFetch(sector, storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.SealPreCommit1(ctx, sector, ticket, pieces))
|
err := m.startWork(ctx, w, wk)(w.SealPreCommit1(ctx, sector, ticket, pieces))
|
||||||
@ -533,7 +533,7 @@ func (m *Manager) SealPreCommit2(ctx context.Context, sector storiface.SectorRef
|
|||||||
return storiface.SectorCids{}, xerrors.Errorf("acquiring sector lock: %w", err)
|
return storiface.SectorCids{}, xerrors.Errorf("acquiring sector lock: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, true)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, sector.ID.Miner, true)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit2, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTPreCommit2, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.SealPreCommit2(ctx, sector, phase1Out))
|
err := m.startWork(ctx, w, wk)(w.SealPreCommit2(ctx, sector, phase1Out))
|
||||||
@ -585,7 +585,7 @@ func (m *Manager) SealCommit1(ctx context.Context, sector storiface.SectorRef, t
|
|||||||
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
||||||
// with direct access to the data. We want to do that because this step is
|
// with direct access to the data. We want to do that because this step is
|
||||||
// generally very cheap / fast, and transferring data is not worth the effort
|
// generally very cheap / fast, and transferring data is not worth the effort
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, false)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, sector.ID.Miner, false)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTCommit1, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTCommit1, selector, m.schedFetch(sector, storiface.FTCache|storiface.FTSealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.SealCommit1(ctx, sector, ticket, seed, pieces, cids))
|
err := m.startWork(ctx, w, wk)(w.SealCommit1(ctx, sector, ticket, seed, pieces, cids))
|
||||||
@ -700,7 +700,7 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector storiface.SectorRef
|
|||||||
|
|
||||||
// do the cache trimming wherever the likely still very large cache lives.
|
// do the cache trimming wherever the likely still very large cache lives.
|
||||||
// we really don't want to move it.
|
// we really don't want to move it.
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache, false)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTCache, sector.ID.Miner, false)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTFinalize, selector,
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTFinalize, selector,
|
||||||
m.schedFetch(sector, storiface.FTCache, cachePathType, storiface.AcquireMove),
|
m.schedFetch(sector, storiface.FTCache, cachePathType, storiface.AcquireMove),
|
||||||
@ -713,7 +713,7 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector storiface.SectorRef
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get a selector for moving stuff into long-term storage
|
// get a selector for moving stuff into long-term storage
|
||||||
fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, storiface.PathStorage, !m.disallowRemoteFinalize)
|
fetchSel := newMoveSelector(m.index, sector.ID, storiface.FTCache|storiface.FTSealed, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize)
|
||||||
|
|
||||||
// only move the unsealed file if it still exists and needs moving
|
// only move the unsealed file if it still exists and needs moving
|
||||||
moveUnsealed := storiface.FTUnsealed
|
moveUnsealed := storiface.FTUnsealed
|
||||||
@ -769,7 +769,7 @@ func (m *Manager) FinalizeReplicaUpdate(ctx context.Context, sector storiface.Se
|
|||||||
|
|
||||||
// do the cache trimming wherever the likely still large cache lives.
|
// do the cache trimming wherever the likely still large cache lives.
|
||||||
// we really don't want to move it.
|
// we really don't want to move it.
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdateCache, false)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdateCache, sector.ID.Miner, false)
|
||||||
|
|
||||||
err := m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeReplicaUpdate, selector,
|
err := m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeReplicaUpdate, selector,
|
||||||
m.schedFetch(sector, storiface.FTCache|storiface.FTUpdateCache, pathType, storiface.AcquireMove),
|
m.schedFetch(sector, storiface.FTCache|storiface.FTUpdateCache, pathType, storiface.AcquireMove),
|
||||||
@ -783,7 +783,7 @@ func (m *Manager) FinalizeReplicaUpdate(ctx context.Context, sector storiface.Se
|
|||||||
|
|
||||||
move := func(types storiface.SectorFileType) error {
|
move := func(types storiface.SectorFileType) error {
|
||||||
// get a selector for moving stuff into long-term storage
|
// get a selector for moving stuff into long-term storage
|
||||||
fetchSel := newMoveSelector(m.index, sector.ID, types, storiface.PathStorage, !m.disallowRemoteFinalize)
|
fetchSel := newMoveSelector(m.index, sector.ID, types, storiface.PathStorage, sector.ID.Miner, !m.disallowRemoteFinalize)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTFetch, fetchSel,
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTFetch, fetchSel,
|
||||||
m.schedFetch(sector, types, storiface.PathStorage, storiface.AcquireMove),
|
m.schedFetch(sector, types, storiface.PathStorage, storiface.AcquireMove),
|
||||||
@ -834,7 +834,7 @@ func (m *Manager) ReleaseUnsealed(ctx context.Context, sector storiface.SectorRe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, false)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed, sector.ID.Miner, false)
|
||||||
|
|
||||||
return m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeUnsealed, selector, m.schedFetch(sector, storiface.FTUnsealed, pathType, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
return m.sched.Schedule(ctx, sector, sealtasks.TTFinalizeUnsealed, selector, m.schedFetch(sector, storiface.FTUnsealed, pathType, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
||||||
_, err := m.waitSimpleCall(ctx)(w.ReleaseUnsealed(ctx, sector, keepUnsealed))
|
_, err := m.waitSimpleCall(ctx)(w.ReleaseUnsealed(ctx, sector, keepUnsealed))
|
||||||
@ -906,7 +906,7 @@ func (m *Manager) GenerateSectorKeyFromData(ctx context.Context, sector storifac
|
|||||||
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
||||||
// with direct access to the data. We want to do that because this step is
|
// with direct access to the data. We want to do that because this step is
|
||||||
// generally very cheap / fast, and transferring data is not worth the effort
|
// generally very cheap / fast, and transferring data is not worth the effort
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed|storiface.FTUpdate|storiface.FTUpdateCache|storiface.FTCache, true)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTUnsealed|storiface.FTUpdate|storiface.FTUpdateCache|storiface.FTCache, sector.ID.Miner, true)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTRegenSectorKey, selector, m.schedFetch(sector, storiface.FTUpdate|storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTRegenSectorKey, selector, m.schedFetch(sector, storiface.FTUpdate|storiface.FTUnsealed, storiface.PathSealing, storiface.AcquireMove), func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.GenerateSectorKeyFromData(ctx, sector, commD))
|
err := m.startWork(ctx, w, wk)(w.GenerateSectorKeyFromData(ctx, sector, commD))
|
||||||
@ -984,7 +984,7 @@ func (m *Manager) ReplicaUpdate(ctx context.Context, sector storiface.SectorRef,
|
|||||||
return storiface.ReplicaUpdateOut{}, xerrors.Errorf("acquiring sector lock: %w", err)
|
return storiface.ReplicaUpdateOut{}, xerrors.Errorf("acquiring sector lock: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := newAllocSelector(m.index, storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing)
|
selector := newAllocSelector(m.index, storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing, sector.ID.Miner)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTReplicaUpdate, selector, m.schedFetch(sector, storiface.FTUnsealed|storiface.FTSealed|storiface.FTCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTReplicaUpdate, selector, m.schedFetch(sector, storiface.FTUnsealed|storiface.FTSealed|storiface.FTCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.ReplicaUpdate(ctx, sector, pieces))
|
err := m.startWork(ctx, w, wk)(w.ReplicaUpdate(ctx, sector, pieces))
|
||||||
@ -1035,7 +1035,7 @@ func (m *Manager) ProveReplicaUpdate1(ctx context.Context, sector storiface.Sect
|
|||||||
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
// NOTE: We set allowFetch to false in so that we always execute on a worker
|
||||||
// with direct access to the data. We want to do that because this step is
|
// with direct access to the data. We want to do that because this step is
|
||||||
// generally very cheap / fast, and transferring data is not worth the effort
|
// generally very cheap / fast, and transferring data is not worth the effort
|
||||||
selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, false)
|
selector := newExistingSelector(m.index, sector.ID, storiface.FTUpdate|storiface.FTUpdateCache, sector.ID.Miner, false)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTProveReplicaUpdate1, selector, m.schedFetch(sector, storiface.FTSealed|storiface.FTCache|storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTProveReplicaUpdate1, selector, m.schedFetch(sector, storiface.FTSealed|storiface.FTCache|storiface.FTUpdate|storiface.FTUpdateCache, storiface.PathSealing, storiface.AcquireCopy), func(ctx context.Context, w Worker) error {
|
||||||
|
|
||||||
@ -1153,7 +1153,7 @@ func (m *Manager) DownloadSectorData(ctx context.Context, sector storiface.Secto
|
|||||||
ptype = storiface.PathStorage
|
ptype = storiface.PathStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
selector := newAllocSelector(m.index, toFetch, ptype)
|
selector := newAllocSelector(m.index, toFetch, ptype, sector.ID.Miner)
|
||||||
|
|
||||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTDownloadSector, selector, schedNop, func(ctx context.Context, w Worker) error {
|
err = m.sched.Schedule(ctx, sector, sealtasks.TTDownloadSector, selector, schedNop, func(ctx context.Context, w Worker) error {
|
||||||
err := m.startWork(ctx, w, wk)(w.DownloadSectorData(ctx, sector, finalized, src))
|
err := m.startWork(ctx, w, wk)(w.DownloadSectorData(ctx, sector, finalized, src))
|
||||||
|
@ -274,7 +274,7 @@ func TestSched(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
rm.done[taskName] = done
|
rm.done[taskName] = done
|
||||||
|
|
||||||
sel := newAllocSelector(index, storiface.FTCache, storiface.PathSealing)
|
sel := newAllocSelector(index, storiface.FTCache, storiface.PathSealing, abi.ActorID(1000))
|
||||||
|
|
||||||
rm.wg.Add(1)
|
rm.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -16,13 +16,15 @@ type allocSelector struct {
|
|||||||
index paths.SectorIndex
|
index paths.SectorIndex
|
||||||
alloc storiface.SectorFileType
|
alloc storiface.SectorFileType
|
||||||
ptype storiface.PathType
|
ptype storiface.PathType
|
||||||
|
miner abi.ActorID
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAllocSelector(index paths.SectorIndex, alloc storiface.SectorFileType, ptype storiface.PathType) *allocSelector {
|
func newAllocSelector(index paths.SectorIndex, alloc storiface.SectorFileType, ptype storiface.PathType, miner abi.ActorID) *allocSelector {
|
||||||
return &allocSelector{
|
return &allocSelector{
|
||||||
index: index,
|
index: index,
|
||||||
alloc: alloc,
|
alloc: alloc,
|
||||||
ptype: ptype,
|
ptype: ptype,
|
||||||
|
miner: miner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ func (s *allocSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt abi
|
|||||||
return false, false, xerrors.Errorf("getting sector size: %w", err)
|
return false, false, xerrors.Errorf("getting sector size: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.ptype)
|
best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.ptype, s.miner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, false, xerrors.Errorf("finding best alloc storage: %w", err)
|
return false, false, xerrors.Errorf("finding best alloc storage: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,16 @@ type existingSelector struct {
|
|||||||
index paths.SectorIndex
|
index paths.SectorIndex
|
||||||
sector abi.SectorID
|
sector abi.SectorID
|
||||||
fileType storiface.SectorFileType
|
fileType storiface.SectorFileType
|
||||||
|
miner abi.ActorID
|
||||||
allowFetch bool
|
allowFetch bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExistingSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, allowFetch bool) *existingSelector {
|
func newExistingSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, miner abi.ActorID, allowFetch bool) *existingSelector {
|
||||||
return &existingSelector{
|
return &existingSelector{
|
||||||
index: index,
|
index: index,
|
||||||
sector: sector,
|
sector: sector,
|
||||||
fileType: alloc,
|
fileType: alloc,
|
||||||
|
miner: miner,
|
||||||
allowFetch: allowFetch,
|
allowFetch: allowFetch,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,11 @@ type moveSelector struct {
|
|||||||
sector abi.SectorID
|
sector abi.SectorID
|
||||||
alloc storiface.SectorFileType
|
alloc storiface.SectorFileType
|
||||||
destPtype storiface.PathType
|
destPtype storiface.PathType
|
||||||
|
miner abi.ActorID
|
||||||
allowRemote bool
|
allowRemote bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMoveSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, destPtype storiface.PathType, allowRemote bool) *moveSelector {
|
func newMoveSelector(index paths.SectorIndex, sector abi.SectorID, alloc storiface.SectorFileType, destPtype storiface.PathType, miner abi.ActorID, allowRemote bool) *moveSelector {
|
||||||
return &moveSelector{
|
return &moveSelector{
|
||||||
index: index,
|
index: index,
|
||||||
sector: sector,
|
sector: sector,
|
||||||
@ -67,7 +68,7 @@ func (s *moveSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt abi.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.destPtype)
|
best, err := s.index.StorageBestAlloc(ctx, s.alloc, ssize, s.destPtype, s.miner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, false, xerrors.Errorf("finding best dest storage: %w", err)
|
return false, false, xerrors.Errorf("finding best dest storage: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,28 @@ import (
|
|||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FTUnsealed represents an unsealed sector file type.
|
||||||
|
// FTSealed represents a sealed sector file type.
|
||||||
|
// FTCache represents a cache sector file type.
|
||||||
|
// FTUpdate represents an update sector file type.
|
||||||
|
// FTUpdateCache represents an update cache sector file type.
|
||||||
|
// FTPiece represents a Piece Park sector file type.
|
||||||
|
// FileTypes represents the total number of file types.
|
||||||
|
//
|
||||||
|
// The SectorFileType type is an integer type that represents different sector file types.
|
||||||
|
// It has several methods to manipulate and query the file type.
|
||||||
|
// The String method returns a string representation of the file type.
|
||||||
|
// The Strings method returns a slice of string representations of all the file types that are set in the receiver object.
|
||||||
|
// The AllSet method returns a slice of all the file types that are set in the receiver object.
|
||||||
|
// The Has method checks whether a specific file type is set in the receiver object.
|
||||||
|
// The SealSpaceUse method calculates the space used by the receiver object in sealing a sector of a given size.
|
||||||
|
// The SubAllowed method removes selected file types from the receiver object based on a list of allowed and denied file types.
|
||||||
|
// The Unset method removes selected file types from the receiver object.
|
||||||
|
// The AnyAllowed method checks whether any file types in the receiver object are allowed based on a list of allowed and denied file types.
|
||||||
|
// The Allowed method checks whether all file types in the receiver object are allowed based on a list of allowed and denied file types.
|
||||||
|
// The StoreSpaceUse method calculates the space used by the receiver object in storing a sector of a given size.
|
||||||
|
// The All method returns an array that represents which file types are set in the receiver object.
|
||||||
|
// The IsNone method checks whether the receiver object represents no file types.
|
||||||
const (
|
const (
|
||||||
// "regular" sectors
|
// "regular" sectors
|
||||||
FTUnsealed SectorFileType = 1 << iota
|
FTUnsealed SectorFileType = 1 << iota
|
||||||
@ -24,12 +46,28 @@ const (
|
|||||||
FileTypes = iota
|
FileTypes = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PathTypes is a slice of SectorFileType that represents different types of sector file paths.
|
||||||
|
// It contains the following types of sector file paths:
|
||||||
|
// - FTUnsealed: represents regular unsealed sectors
|
||||||
|
// - FTSealed: represents sealed sectors
|
||||||
|
// - FTCache: represents cache sectors
|
||||||
|
// - FTUpdate: represents snap sectors
|
||||||
|
// - FTUpdateCache: represents snap cache sectors
|
||||||
|
// - FTPiece: represents Piece Park sectors
|
||||||
var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache, FTPiece}
|
var PathTypes = []SectorFileType{FTUnsealed, FTSealed, FTCache, FTUpdate, FTUpdateCache, FTPiece}
|
||||||
|
|
||||||
|
// FTNone represents a sector file type of none. This constant is used in the StorageLock method to specify that a sector should not have any file types locked.
|
||||||
|
// Example usage:
|
||||||
|
// err := m.index.StorageLock(ctx, sector.ID, storiface.FTNone, storiface.FTSealed|storiface.FTUnsealed|storiface.FTCache)
|
||||||
const (
|
const (
|
||||||
FTNone SectorFileType = 0
|
FTNone SectorFileType = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FTAll represents the combination of all available sector file types.
|
||||||
|
// It is a variable of type SectorFileType.
|
||||||
|
// The value of FTAll is calculated by iterating over the PathTypes slice and using the |= operator to perform a bitwise OR operation on each path type.
|
||||||
|
// The result is assigned to the variable out and returned.
|
||||||
|
// FTAll is immediately invoked as a function using the anonymous function syntax, so the result is returned as soon as it is calculated.
|
||||||
var FTAll = func() (out SectorFileType) {
|
var FTAll = func() (out SectorFileType) {
|
||||||
for _, pathType := range PathTypes {
|
for _, pathType := range PathTypes {
|
||||||
out |= pathType
|
out |= pathType
|
||||||
@ -37,8 +75,10 @@ var FTAll = func() (out SectorFileType) {
|
|||||||
return out
|
return out
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// FSOverheadDen represents the constant value 10, which is used to calculate the overhead in various storage space utilization calculations.
|
||||||
const FSOverheadDen = 10
|
const FSOverheadDen = 10
|
||||||
|
|
||||||
|
// FSOverheadSeal is a map that represents the overheads for different SectorFileType in sealed sectors.
|
||||||
var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads
|
var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads
|
||||||
FTUnsealed: FSOverheadDen,
|
FTUnsealed: FSOverheadDen,
|
||||||
FTSealed: FSOverheadDen,
|
FTSealed: FSOverheadDen,
|
||||||
@ -50,6 +90,30 @@ var FSOverheadSeal = map[SectorFileType]int{ // 10x overheads
|
|||||||
|
|
||||||
// sector size * disk / fs overhead. FSOverheadDen is like the unit of sector size
|
// sector size * disk / fs overhead. FSOverheadDen is like the unit of sector size
|
||||||
|
|
||||||
|
// FsOverheadFinalized is a map that represents the finalized overhead for different types of SectorFileType.
|
||||||
|
// The keys in the map are the SectorFileType values, and the values are integers representing the overhead.
|
||||||
|
// It is used to calculate the storage space usage for different types of sectors, as shown in the example below:
|
||||||
|
//
|
||||||
|
// func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
||||||
|
// var need uint64
|
||||||
|
// for _, pathType := range PathTypes {
|
||||||
|
// if !t.Has(pathType) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// oh, ok := FsOverheadFinalized[pathType]
|
||||||
|
// if !ok {
|
||||||
|
// return 0, xerrors.Errorf("no finalized overhead info for %s", pathType)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// need += uint64(oh) * uint64(ssize) / FSOverheadDen
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return need, nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The overhead value is retrieved from FsOverheadFinalized by using the SectorFileType value as the key.
|
||||||
|
// If the overhead value is not found in the map, an error is returned indicating that there is no finalized overhead information for the given sector type.
|
||||||
var FsOverheadFinalized = map[SectorFileType]int{
|
var FsOverheadFinalized = map[SectorFileType]int{
|
||||||
FTUnsealed: FSOverheadDen,
|
FTUnsealed: FSOverheadDen,
|
||||||
FTSealed: FSOverheadDen,
|
FTSealed: FSOverheadDen,
|
||||||
@ -59,8 +123,13 @@ var FsOverheadFinalized = map[SectorFileType]int{
|
|||||||
FTPiece: FSOverheadDen,
|
FTPiece: FSOverheadDen,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SectorFileType represents the type of a sector file
|
||||||
|
// TypeFromString converts a string to a SectorFileType
|
||||||
type SectorFileType int
|
type SectorFileType int
|
||||||
|
|
||||||
|
// TypeFromString converts a string representation of a SectorFileType to its corresponding value.
|
||||||
|
// It returns the SectorFileType and nil error if the string matches one of the existing types.
|
||||||
|
// If the string does not match any type, it returns 0 and an error.
|
||||||
func TypeFromString(s string) (SectorFileType, error) {
|
func TypeFromString(s string) (SectorFileType, error) {
|
||||||
switch s {
|
switch s {
|
||||||
case "unsealed":
|
case "unsealed":
|
||||||
@ -80,6 +149,7 @@ func TypeFromString(s string) (SectorFileType, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the SectorFileType.
|
||||||
func (t SectorFileType) String() string {
|
func (t SectorFileType) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
case FTUnsealed:
|
case FTUnsealed:
|
||||||
@ -99,6 +169,12 @@ func (t SectorFileType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strings returns a slice of strings representing the names of the SectorFileType values that are set in the receiver value.
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// fileType := SectorFileType(FTSealed | FTCache)
|
||||||
|
// names := fileType.Strings() // names = ["sealed", "cache"]
|
||||||
|
// fmt.Println(names)
|
||||||
func (t SectorFileType) Strings() []string {
|
func (t SectorFileType) Strings() []string {
|
||||||
var out []string
|
var out []string
|
||||||
for _, fileType := range PathTypes {
|
for _, fileType := range PathTypes {
|
||||||
@ -111,6 +187,7 @@ func (t SectorFileType) Strings() []string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllSet returns a slice of SectorFileType values that are set in the SectorFileType receiver value
|
||||||
func (t SectorFileType) AllSet() []SectorFileType {
|
func (t SectorFileType) AllSet() []SectorFileType {
|
||||||
var out []SectorFileType
|
var out []SectorFileType
|
||||||
for _, fileType := range PathTypes {
|
for _, fileType := range PathTypes {
|
||||||
@ -123,10 +200,33 @@ func (t SectorFileType) AllSet() []SectorFileType {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has checks if the SectorFileType has a specific singleType.
|
||||||
func (t SectorFileType) Has(singleType SectorFileType) bool {
|
func (t SectorFileType) Has(singleType SectorFileType) bool {
|
||||||
return t&singleType == singleType
|
return t&singleType == singleType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SealSpaceUse calculates the amount of space needed for sealing the sector
|
||||||
|
// based on the given sector size. It iterates over the different path types
|
||||||
|
// and calculates the space needed for each path type using the FSOverheadSeal
|
||||||
|
// map. The overhead value is multiplied by the sector size and divided by the
|
||||||
|
// FSOverheadDen constant. The total space needed is accumulated and returned.
|
||||||
|
// If there is no seal overhead information for a particular path type, an error
|
||||||
|
// is returned.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// fileType := FTSealed | FTCache
|
||||||
|
// sectorSize := abi.SectorSize(32 << 20) // 32 MiB
|
||||||
|
// spaceNeeded, err := fileType.SealSpaceUse(sectorSize)
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
//
|
||||||
|
// ssize: The size of the sector
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// uint64: The amount of space needed for sealing the sector
|
||||||
|
// error: If there is no seal overhead information for a path type
|
||||||
func (t SectorFileType) SealSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
func (t SectorFileType) SealSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
||||||
var need uint64
|
var need uint64
|
||||||
for _, pathType := range PathTypes {
|
for _, pathType := range PathTypes {
|
||||||
@ -145,6 +245,15 @@ func (t SectorFileType) SealSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
|||||||
return need, nil
|
return need, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The method takes in two parameters: allowTypes and denyTypes, both of which are slices of strings.
|
||||||
|
// If allowTypes is not empty, the method sets a denyMask with all bits set to 1, and then iterates over each allowType,
|
||||||
|
// converting it to a SectorFileType using the TypeFromString function and unsetting the corresponding bit in the denyMask.
|
||||||
|
// If a string in allowTypes cannot be converted to a valid SectorFileType, it is ignored.
|
||||||
|
// After processing allowTypes, the method iterates over each denyType, converting it to a SectorFileType using the TypeFromString function
|
||||||
|
// and setting the corresponding bit in the denyMask.
|
||||||
|
// If a string in denyTypes cannot be converted to a valid SectorFileType, it is ignored.
|
||||||
|
// Finally, the method returns the bitwise AND of the original SectorFileType and the denyMask.
|
||||||
|
// The returned SectorFileType will only allow the types specified in allowTypes and exclude the types specified in denyTypes.`
|
||||||
func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) SectorFileType {
|
func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) SectorFileType {
|
||||||
var denyMask SectorFileType // 1s deny
|
var denyMask SectorFileType // 1s deny
|
||||||
|
|
||||||
@ -174,18 +283,30 @@ func (t SectorFileType) SubAllowed(allowTypes []string, denyTypes []string) Sect
|
|||||||
return t & denyMask
|
return t & denyMask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unset removes the specified sector file type(s) from the current SectorFileType value.
|
||||||
|
// It performs a bitwise AND operation between the current value and the bitwise complement of the toUnset value.
|
||||||
|
// The result is returned as a new SectorFileType value.
|
||||||
|
// Any bits that are set in toUnset will be cleared in the result.
|
||||||
|
// Usage: result = value.Unset(typesToUnset)
|
||||||
func (t SectorFileType) Unset(toUnset SectorFileType) SectorFileType {
|
func (t SectorFileType) Unset(toUnset SectorFileType) SectorFileType {
|
||||||
return t &^ toUnset
|
return t &^ toUnset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AnyAllowed checks if the SectorFileType has any allowed types and no denied types.
|
||||||
func (t SectorFileType) AnyAllowed(allowTypes []string, denyTypes []string) bool {
|
func (t SectorFileType) AnyAllowed(allowTypes []string, denyTypes []string) bool {
|
||||||
return t.SubAllowed(allowTypes, denyTypes) != t
|
return t.SubAllowed(allowTypes, denyTypes) != t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allowed checks if the SectorFileType is allowed based on the given allowTypes and denyTypes.
|
||||||
|
// Returns true if the SectorFileType is allowed, otherwise false.
|
||||||
func (t SectorFileType) Allowed(allowTypes []string, denyTypes []string) bool {
|
func (t SectorFileType) Allowed(allowTypes []string, denyTypes []string) bool {
|
||||||
return t.SubAllowed(allowTypes, denyTypes) == 0
|
return t.SubAllowed(allowTypes, denyTypes) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StoreSpaceUse calculates the space used for storing sectors of a specific file type.
|
||||||
|
// It takes the sector size as input and returns the total space needed in bytes and an error, if any.
|
||||||
|
// The calculation is based on the finalized overhead information for the file type.
|
||||||
|
// If the overhead information is not available for a particular file type, an error will be returned.
|
||||||
func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
||||||
var need uint64
|
var need uint64
|
||||||
for _, pathType := range PathTypes {
|
for _, pathType := range PathTypes {
|
||||||
@ -204,6 +325,7 @@ func (t SectorFileType) StoreSpaceUse(ssize abi.SectorSize) (uint64, error) {
|
|||||||
return need, nil
|
return need, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All returns an array indicating whether each FileTypes flag is set in the SectorFileType.
|
||||||
func (t SectorFileType) All() [FileTypes]bool {
|
func (t SectorFileType) All() [FileTypes]bool {
|
||||||
var out [FileTypes]bool
|
var out [FileTypes]bool
|
||||||
|
|
||||||
@ -214,10 +336,13 @@ func (t SectorFileType) All() [FileTypes]bool {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNone checks if the SectorFileType value is equal to zero.
|
||||||
|
// It returns true if the value is zero, indicating that the type is none.
|
||||||
func (t SectorFileType) IsNone() bool {
|
func (t SectorFileType) IsNone() bool {
|
||||||
return t == 0
|
return t == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SectorPaths represents the paths for different sector files.
|
||||||
type SectorPaths struct {
|
type SectorPaths struct {
|
||||||
ID abi.SectorID
|
ID abi.SectorID
|
||||||
|
|
||||||
@ -229,6 +354,7 @@ type SectorPaths struct {
|
|||||||
Piece string
|
Piece string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasAllSet checks if all paths of a SectorPaths struct are set for a given SectorFileType.
|
||||||
func (sp SectorPaths) HasAllSet(ft SectorFileType) bool {
|
func (sp SectorPaths) HasAllSet(ft SectorFileType) bool {
|
||||||
for _, fileType := range ft.AllSet() {
|
for _, fileType := range ft.AllSet() {
|
||||||
if PathByType(sp, fileType) == "" {
|
if PathByType(sp, fileType) == "" {
|
||||||
@ -239,6 +365,9 @@ func (sp SectorPaths) HasAllSet(ft SectorFileType) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subset returns a new instance of SectorPaths that contains only the paths specified by the filter SectorFileType.
|
||||||
|
// It iterates over each fileType in the filter, retrieves the corresponding path from the original SectorPaths instance, and sets it in the new instance.
|
||||||
|
// Finally, it sets the ID field of the new instance to be the same as the original instance.
|
||||||
func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths {
|
func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths {
|
||||||
var out SectorPaths
|
var out SectorPaths
|
||||||
|
|
||||||
@ -251,6 +380,24 @@ func (sp SectorPaths) Subset(filter SectorFileType) SectorPaths {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseSectorID parses a sector ID from a given base name.
|
||||||
|
// It expects the format "s-t0%d-%d", where the first %d represents the miner ID
|
||||||
|
// and the second %d represents the sector number.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - baseName: The base name from which to parse the sector ID.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - abi.SectorID: The parsed sector ID.
|
||||||
|
// - error: An error if parsing fails.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// id, err := ParseSectorID(baseName)
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
// // use id
|
||||||
func ParseSectorID(baseName string) (abi.SectorID, error) {
|
func ParseSectorID(baseName string) (abi.SectorID, error) {
|
||||||
var n abi.SectorNumber
|
var n abi.SectorNumber
|
||||||
var mid abi.ActorID
|
var mid abi.ActorID
|
||||||
@ -269,10 +416,19 @@ func ParseSectorID(baseName string) (abi.SectorID, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SectorName returns the name of a sector in the format "s-t0<Miner>-<Number>"
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - sid: The sector ID
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - The name of the sector as a string
|
||||||
func SectorName(sid abi.SectorID) string {
|
func SectorName(sid abi.SectorID) string {
|
||||||
return fmt.Sprintf("s-t0%d-%d", sid.Miner, sid.Number)
|
return fmt.Sprintf("s-t0%d-%d", sid.Miner, sid.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathByType returns the path associated with the specified fileType in the given SectorPaths.
|
||||||
|
// It panics if the requested path type is unknown.
|
||||||
func PathByType(sps SectorPaths, fileType SectorFileType) string {
|
func PathByType(sps SectorPaths, fileType SectorFileType) string {
|
||||||
switch fileType {
|
switch fileType {
|
||||||
case FTUnsealed:
|
case FTUnsealed:
|
||||||
@ -309,11 +465,14 @@ func SetPathByType(sps *SectorPaths, fileType SectorFileType, p string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathsWithIDs represents paths and IDs for sector files.
|
||||||
type PathsWithIDs struct {
|
type PathsWithIDs struct {
|
||||||
Paths SectorPaths
|
Paths SectorPaths
|
||||||
IDs SectorPaths
|
IDs SectorPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasAllSet checks if all paths and IDs in PathsWithIDs have a corresponding path set for the specified SectorFileType.
|
||||||
|
// It returns true if all paths and IDs are set, and false otherwise.
|
||||||
func (p PathsWithIDs) HasAllSet(ft SectorFileType) bool {
|
func (p PathsWithIDs) HasAllSet(ft SectorFileType) bool {
|
||||||
return p.Paths.HasAllSet(ft) && p.IDs.HasAllSet(ft)
|
return p.Paths.HasAllSet(ft) && p.IDs.HasAllSet(ft)
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,14 @@ type StorageInfo struct {
|
|||||||
// - "update-cache"
|
// - "update-cache"
|
||||||
// Any other value will generate a warning and be ignored.
|
// Any other value will generate a warning and be ignored.
|
||||||
DenyTypes []string
|
DenyTypes []string
|
||||||
|
|
||||||
|
// AllowMiners lists miner IDs which are allowed to store their sector data into
|
||||||
|
// this path. If empty, all miner IDs are allowed
|
||||||
|
AllowMiners []string
|
||||||
|
|
||||||
|
// DenyMiners lists miner IDs which are denied to store their sector data into
|
||||||
|
// this path
|
||||||
|
DenyMiners []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthReport struct {
|
type HealthReport struct {
|
||||||
@ -104,8 +112,10 @@ type SectorStorageInfo struct {
|
|||||||
|
|
||||||
Primary bool
|
Primary bool
|
||||||
|
|
||||||
AllowTypes []string
|
AllowTypes []string
|
||||||
DenyTypes []string
|
DenyTypes []string
|
||||||
|
AllowMiners []string
|
||||||
|
DenyMiners []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Decl struct {
|
type Decl struct {
|
||||||
|
@ -225,4 +225,12 @@ type LocalStorageMeta struct {
|
|||||||
// - "update-cache"
|
// - "update-cache"
|
||||||
// Any other value will generate a warning and be ignored.
|
// Any other value will generate a warning and be ignored.
|
||||||
DenyTypes []string
|
DenyTypes []string
|
||||||
|
|
||||||
|
// AllowMiners lists miner IDs which are allowed to store their sector data into
|
||||||
|
// this path. If empty, all miner IDs are allowed
|
||||||
|
AllowMiners []string
|
||||||
|
|
||||||
|
// DenyMiners lists miner IDs which are denied to store their sector data into
|
||||||
|
// this path
|
||||||
|
DenyMiners []string
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user