diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 46b21a91c..ae235e01e 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -442,6 +442,30 @@ # env var: LOTUS_SEALING_MAXUPGRADINGSECTORS #MaxUpgradingSectors = 0 + # When set to a non-zero value, minimum number of epochs until sector expiration required for sectors to be considered + # for upgrades (0 = DealMinDuration = 180 days = 518400 epochs) + # + # Note that if all deals waiting in the input queue have lifetimes longer than this value, upgrade sectors will be + # required to have expiration of at least the soonest-ending deal + # + # type: uint64 + # env var: LOTUS_SEALING_MINUPGRADESECTOREXPIRATION + #MinUpgradeSectorExpiration = 0 + + # When set to a non-zero value, minimum number of epochs until sector expiration above which upgrade candidates will + # be selected based on lowest initial pledge. + # + # Target sector expiration is calculated by looking at the input deal queue, sorting it by deal expiration, and + # selecting N deals from the queue up to sector size. The target expiration will be Nth deal end epoch, or in case + # where there weren't enough deals to fill a sector, DealMaxDuration (540 days = 1555200 epochs) + # + # Setting this to a high value (for example to maximum deal duration - 1555200) will disable selection based on + # initial pledge - upgrade sectors will always be chosen based on longest expiration + # + # type: uint64 + # env var: LOTUS_SEALING_MINTARGETUPGRADESECTOREXPIRATION + #MinTargetUpgradeSectorExpiration = 0 + # CommittedCapacitySectorLifetime is the duration a Committed Capacity (CC) sector will # live before it must be extended or converted into sector containing deals before it is # terminated. Value must be between 180-540 days inclusive diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index f317e0606..6b5927448 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -922,6 +922,30 @@ flow when the volume of storage deals is lower.`, Comment: `Upper bound on how many sectors can be sealing+upgrading at the same time when upgrading CC sectors with deals (0 = MaxSealingSectorsForDeals)`, }, + { + Name: "MinUpgradeSectorExpiration", + Type: "uint64", + + Comment: `When set to a non-zero value, minimum number of epochs until sector expiration required for sectors to be considered +for upgrades (0 = DealMinDuration = 180 days = 518400 epochs) + +Note that if all deals waiting in the input queue have lifetimes longer than this value, upgrade sectors will be +required to have expiration of at least the soonest-ending deal`, + }, + { + Name: "MinTargetUpgradeSectorExpiration", + Type: "uint64", + + Comment: `When set to a non-zero value, minimum number of epochs until sector expiration above which upgrade candidates will +be selected based on lowest initial pledge. + +Target sector expiration is calculated by looking at the input deal queue, sorting it by deal expiration, and +selecting N deals from the queue up to sector size. The target expiration will be Nth deal end epoch, or in case +where there weren't enough deals to fill a sector, DealMaxDuration (540 days = 1555200 epochs) + +Setting this to a high value (for example to maximum deal duration - 1555200) will disable selection based on +initial pledge - upgrade sectors will always be chosen based on longest expiration`, + }, { Name: "CommittedCapacitySectorLifetime", Type: "Duration", diff --git a/node/config/types.go b/node/config/types.go index dbea0ddb6..3c85587c3 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -319,6 +319,24 @@ type SealingConfig struct { // Upper bound on how many sectors can be sealing+upgrading at the same time when upgrading CC sectors with deals (0 = MaxSealingSectorsForDeals) MaxUpgradingSectors uint64 + // When set to a non-zero value, minimum number of epochs until sector expiration required for sectors to be considered + // for upgrades (0 = DealMinDuration = 180 days = 518400 epochs) + // + // Note that if all deals waiting in the input queue have lifetimes longer than this value, upgrade sectors will be + // required to have expiration of at least the soonest-ending deal + MinUpgradeSectorExpiration uint64 + + // When set to a non-zero value, minimum number of epochs until sector expiration above which upgrade candidates will + // be selected based on lowest initial pledge. + // + // Target sector expiration is calculated by looking at the input deal queue, sorting it by deal expiration, and + // selecting N deals from the queue up to sector size. The target expiration will be Nth deal end epoch, or in case + // where there weren't enough deals to fill a sector, DealMaxDuration (540 days = 1555200 epochs) + // + // Setting this to a high value (for example to maximum deal duration - 1555200) will disable selection based on + // initial pledge - upgrade sectors will always be chosen based on longest expiration + MinTargetUpgradeSectorExpiration uint64 + // CommittedCapacitySectorLifetime is the duration a Committed Capacity (CC) sector will // live before it must be extended or converted into sector containing deals before it is // terminated. Value must be between 180-540 days inclusive diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 2ea733605..0d85cd168 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -983,17 +983,19 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error return func(cfg sealiface.Config) (err error) { err = mutateSealingCfg(r, func(c config.SealingConfiger) { newCfg := config.SealingConfig{ - MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, - MaxSealingSectors: cfg.MaxSealingSectors, - MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, - PreferNewSectorsForDeals: cfg.PreferNewSectorsForDeals, - MaxUpgradingSectors: cfg.MaxUpgradingSectors, - CommittedCapacitySectorLifetime: config.Duration(cfg.CommittedCapacitySectorLifetime), - WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), - MakeNewSectorForDeals: cfg.MakeNewSectorForDeals, - MakeCCSectorsAvailable: cfg.MakeCCSectorsAvailable, - AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, - FinalizeEarly: cfg.FinalizeEarly, + MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, + MaxSealingSectors: cfg.MaxSealingSectors, + MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, + PreferNewSectorsForDeals: cfg.PreferNewSectorsForDeals, + MaxUpgradingSectors: cfg.MaxUpgradingSectors, + CommittedCapacitySectorLifetime: config.Duration(cfg.CommittedCapacitySectorLifetime), + WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), + MakeNewSectorForDeals: cfg.MakeNewSectorForDeals, + MinUpgradeSectorExpiration: cfg.MinUpgradeSectorExpiration, + MinTargetUpgradeSectorExpiration: cfg.MinTargetUpgradeSectorExpiration, + MakeCCSectorsAvailable: cfg.MakeCCSectorsAvailable, + AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.FinalizeEarly, CollateralFromMinerBalance: cfg.CollateralFromMinerBalance, AvailableBalanceBuffer: types.FIL(cfg.AvailableBalanceBuffer), @@ -1024,11 +1026,14 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error func ToSealingConfig(dealmakingCfg config.DealmakingConfig, sealingCfg config.SealingConfig) sealiface.Config { return sealiface.Config{ - MaxWaitDealsSectors: sealingCfg.MaxWaitDealsSectors, - MaxSealingSectors: sealingCfg.MaxSealingSectors, - MaxSealingSectorsForDeals: sealingCfg.MaxSealingSectorsForDeals, - PreferNewSectorsForDeals: sealingCfg.PreferNewSectorsForDeals, - MaxUpgradingSectors: sealingCfg.MaxUpgradingSectors, + MaxWaitDealsSectors: sealingCfg.MaxWaitDealsSectors, + MaxSealingSectors: sealingCfg.MaxSealingSectors, + MaxSealingSectorsForDeals: sealingCfg.MaxSealingSectorsForDeals, + PreferNewSectorsForDeals: sealingCfg.PreferNewSectorsForDeals, + MinUpgradeSectorExpiration: sealingCfg.MinUpgradeSectorExpiration, + MinTargetUpgradeSectorExpiration: sealingCfg.MinTargetUpgradeSectorExpiration, + MaxUpgradingSectors: sealingCfg.MaxUpgradingSectors, + StartEpochSealingBuffer: abi.ChainEpoch(dealmakingCfg.StartEpochSealingBuffer), MakeNewSectorForDeals: sealingCfg.MakeNewSectorForDeals, CommittedCapacitySectorLifetime: time.Duration(sealingCfg.CommittedCapacitySectorLifetime), diff --git a/storage/pipeline/input.go b/storage/pipeline/input.go index 2f203a17b..e86f7b9a9 100644 --- a/storage/pipeline/input.go +++ b/storage/pipeline/input.go @@ -521,7 +521,7 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e return nil } -func (m *Sealing) calcTargetExpiration(ctx context.Context, ssize abi.SectorSize) (minTarget, target abi.ChainEpoch, err error) { +func (m *Sealing) calcTargetExpiration(ctx context.Context, ssize abi.SectorSize, cfg sealiface.Config) (minExp, target abi.ChainEpoch, err error) { var candidates []*pendingPiece for _, piece := range m.pendingPieces { @@ -537,30 +537,49 @@ func (m *Sealing) calcTargetExpiration(ctx context.Context, ssize abi.SectorSize }) var totalBytes uint64 + var full bool + + // Find the expiration of the last deal which can fit into the sector, use that as the initial target for _, candidate := range candidates { totalBytes += uint64(candidate.size) + target = candidate.deal.DealProposal.EndEpoch if totalBytes >= uint64(abi.PaddedPieceSize(ssize).Unpadded()) { - return candidates[0].deal.DealProposal.EndEpoch, candidate.deal.DealProposal.EndEpoch, nil + full = true + break } } - ts, err := m.Api.ChainHead(ctx) - if err != nil { - return 0, 0, xerrors.Errorf("getting current epoch: %w", err) + // if the sector isn't full, use max deal duration as the target + if !full { + ts, err := m.Api.ChainHead(ctx) + if err != nil { + return 0, 0, xerrors.Errorf("getting current epoch: %w", err) + } + + minDur, maxDur := policy.DealDurationBounds(0) + minExp = ts.Height() + minDur + + target = maxDur } - minDur, maxDur := policy.DealDurationBounds(0) - minTarget = ts.Height() + minDur - - if len(candidates) > 0 && candidates[0].deal.DealProposal.EndEpoch > minTarget { - minTarget = candidates[0].deal.DealProposal.EndEpoch + // make sure that at least one deal in the queue is within the expiration + if len(candidates) > 0 && candidates[0].deal.DealProposal.EndEpoch > minExp { + minExp = candidates[0].deal.DealProposal.EndEpoch } - return minTarget, ts.Height() + maxDur, nil + // apply user minimums + if abi.ChainEpoch(cfg.MinUpgradeSectorExpiration) > minExp { + minExp = abi.ChainEpoch(cfg.MinUpgradeSectorExpiration) + } + if abi.ChainEpoch(cfg.MinTargetUpgradeSectorExpiration) > target { + target = abi.ChainEpoch(cfg.MinTargetUpgradeSectorExpiration) + } + + return minExp, target, nil } -func (m *Sealing) maybeUpgradeSector(ctx context.Context, sp abi.RegisteredSealProof, ef expFn) (bool, error) { +func (m *Sealing) maybeUpgradeSector(ctx context.Context, sp abi.RegisteredSealProof, cfg sealiface.Config, ef expFn) (bool, error) { if len(m.available) == 0 { return false, nil } @@ -569,7 +588,7 @@ func (m *Sealing) maybeUpgradeSector(ctx context.Context, sp abi.RegisteredSealP if err != nil { return false, xerrors.Errorf("getting sector size: %w", err) } - minExpiration, targetExpiration, err := m.calcTargetExpiration(ctx, ssize) + minExpiration, targetExpiration, err := m.calcTargetExpiration(ctx, ssize, cfg) if err != nil { return false, xerrors.Errorf("calculating min target expiration: %w", err) } @@ -687,7 +706,7 @@ func (m *Sealing) tryGetDealSector(ctx context.Context, sp abi.RegisteredSealPro "shouldUpgrade", shouldUpgrade) if shouldUpgrade { - got, err := m.maybeUpgradeSector(ctx, sp, ef) + got, err := m.maybeUpgradeSector(ctx, sp, cfg, ef) if err != nil { return err } diff --git a/storage/pipeline/sealiface/config.go b/storage/pipeline/sealiface/config.go index 0470db38e..2db155d5c 100644 --- a/storage/pipeline/sealiface/config.go +++ b/storage/pipeline/sealiface/config.go @@ -20,6 +20,10 @@ type Config struct { PreferNewSectorsForDeals bool + MinUpgradeSectorExpiration uint64 + + MinTargetUpgradeSectorExpiration uint64 + MaxUpgradingSectors uint64 MakeNewSectorForDeals bool