lotus/extern/storage-sealing/fsm.go

419 lines
11 KiB
Go
Raw Normal View History

//go:generate go run ./gen
package sealing
import (
"bytes"
"context"
2020-03-22 20:44:27 +00:00
"encoding/json"
2020-01-22 20:29:19 +00:00
"fmt"
"reflect"
2020-01-22 20:29:19 +00:00
"time"
"golang.org/x/xerrors"
statemachine "github.com/filecoin-project/go-statemachine"
"github.com/filecoin-project/specs-actors/actors/abi"
)
2020-03-06 18:59:08 +00:00
func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface{}, uint64, error) {
next, err := m.plan(events, user.(*SectorInfo))
if err != nil || next == nil {
2020-03-06 23:03:57 +00:00
return nil, uint64(len(events)), err
}
return func(ctx statemachine.Context, si SectorInfo) error {
err := next(ctx, si)
if err != nil {
2020-04-06 22:31:33 +00:00
log.Errorf("unhandled sector error (%d): %+v", si.SectorNumber, err)
return nil
}
return nil
2020-03-06 18:59:08 +00:00
}, uint64(len(events)), nil // TODO: This processed event count is not very correct
}
2020-04-06 20:23:37 +00:00
var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *SectorInfo) error{
2020-06-22 16:42:38 +00:00
// Sealing
2020-06-23 19:32:22 +00:00
UndefinedSectorState: planOne(
on(SectorStart{}, Empty),
2020-06-23 19:32:22 +00:00
on(SectorStartCC{}, Packing),
),
Empty: planOne(on(SectorAddPiece{}, WaitDeals)),
WaitDeals: planOne(
on(SectorAddPiece{}, WaitDeals),
on(SectorStartPacking{}, Packing),
),
2020-06-23 19:32:22 +00:00
Packing: planOne(on(SectorPacked{}, PreCommit1)),
2020-04-06 20:23:37 +00:00
PreCommit1: planOne(
on(SectorPreCommit1{}, PreCommit2),
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
2020-04-06 20:23:37 +00:00
on(SectorPackingFailed{}, PackingFailed),
2020-04-03 16:54:01 +00:00
),
2020-04-06 20:23:37 +00:00
PreCommit2: planOne(
on(SectorPreCommit2{}, PreCommitting),
on(SectorSealPreCommit2Failed{}, SealPreCommit2Failed),
2020-04-06 20:23:37 +00:00
on(SectorPackingFailed{}, PackingFailed),
),
2020-04-06 20:23:37 +00:00
PreCommitting: planOne(
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
2020-05-18 22:49:21 +00:00
on(SectorPreCommitted{}, PreCommitWait),
2020-04-06 20:23:37 +00:00
on(SectorChainPreCommitFailed{}, PreCommitFailed),
2020-06-02 21:45:28 +00:00
on(SectorPreCommitLanded{}, WaitSeed),
),
2020-05-18 22:49:21 +00:00
PreCommitWait: planOne(
on(SectorChainPreCommitFailed{}, PreCommitFailed),
on(SectorPreCommitLanded{}, WaitSeed),
),
2020-04-06 20:23:37 +00:00
WaitSeed: planOne(
on(SectorSeedReady{}, Committing),
on(SectorChainPreCommitFailed{}, PreCommitFailed),
),
Committing: planCommitting,
CommitWait: planOne(
on(SectorProving{}, FinalizeSector),
on(SectorCommitFailed{}, CommitFailed),
2020-01-20 08:23:56 +00:00
),
FinalizeSector: planOne(
on(SectorFinalized{}, Proving),
2020-06-03 21:42:13 +00:00
on(SectorFinalizeFailed{}, FinalizeFailed),
2020-01-29 21:25:06 +00:00
),
2020-06-22 16:42:38 +00:00
// Sealing errors
SealPreCommit1Failed: planOne(
on(SectorRetrySealPreCommit1{}, PreCommit1),
),
SealPreCommit2Failed: planOne(
on(SectorRetrySealPreCommit1{}, PreCommit1),
on(SectorRetrySealPreCommit2{}, PreCommit2),
),
2020-04-06 20:23:37 +00:00
PreCommitFailed: planOne(
on(SectorRetryPreCommit{}, PreCommitting),
on(SectorRetryWaitSeed{}, WaitSeed),
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
2020-06-02 21:45:28 +00:00
on(SectorPreCommitLanded{}, WaitSeed),
2020-01-23 17:34:04 +00:00
),
2020-04-06 20:23:37 +00:00
ComputeProofFailed: planOne(
on(SectorRetryComputeProof{}, Committing),
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
),
2020-04-06 20:23:37 +00:00
CommitFailed: planOne(
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
2020-04-06 20:23:37 +00:00
on(SectorRetryWaitSeed{}, WaitSeed),
on(SectorRetryComputeProof{}, Committing),
on(SectorRetryInvalidProof{}, Committing),
2020-06-17 15:19:36 +00:00
on(SectorRetryPreCommitWait{}, PreCommitWait),
2020-08-05 01:30:58 +00:00
on(SectorChainPreCommitFailed{}, PreCommitFailed),
on(SectorRetryPreCommit{}, PreCommitting),
on(SectorRetryCommitWait{}, CommitWait),
),
2020-06-03 21:42:13 +00:00
FinalizeFailed: planOne(
on(SectorRetryFinalize{}, FinalizeSector),
),
2020-06-22 16:42:38 +00:00
// Post-seal
Proving: planOne(
on(SectorFaultReported{}, FaultReported),
on(SectorFaulty{}, Faulty),
on(SectorRemove{}, Removing),
),
Removing: planOne(
on(SectorRemoved{}, Removed),
on(SectorRemoveFailed{}, RemoveFailed),
),
Faulty: planOne(
on(SectorFaultReported{}, FaultReported),
),
2020-06-22 16:42:38 +00:00
FaultedFinal: final,
2020-06-22 16:42:38 +00:00
Removed: final,
}
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, error) {
/////
// First process all events
2020-01-22 20:29:19 +00:00
for _, event := range events {
2020-03-22 20:44:27 +00:00
e, err := json.Marshal(event)
if err != nil {
log.Errorf("marshaling event for logging: %+v", err)
continue
}
2020-01-22 20:29:19 +00:00
l := Log{
Timestamp: uint64(time.Now().Unix()),
2020-03-22 20:44:27 +00:00
Message: string(e),
2020-01-22 20:29:19 +00:00
Kind: fmt.Sprintf("event;%T", event.User),
}
if err, iserr := event.User.(xerrors.Formatter); iserr {
l.Trace = fmt.Sprintf("%+v", err)
}
2020-08-05 01:30:58 +00:00
if len(state.Log) > 8000 {
log.Warnw("truncating sector log", "sector", state.SectorNumber)
state.Log[2000] = Log{
Timestamp: uint64(time.Now().Unix()),
Message: "truncating log (above 8000 entries)",
Kind: fmt.Sprintf("truncate"),
}
state.Log = append(state.Log[:2000], state.Log[:6000]...)
}
2020-01-22 20:29:19 +00:00
state.Log = append(state.Log, l)
}
p := fsmPlanners[state.State]
if p == nil {
2020-04-03 16:24:45 +00:00
return nil, xerrors.Errorf("planner for state %s not found", state.State)
}
if err := p(events, state); err != nil {
2020-04-03 16:24:45 +00:00
return nil, xerrors.Errorf("running planner for state %s failed: %w", state.State, err)
}
/////
// Now decide what to do next
/*
2020-06-26 15:29:08 +00:00
* Empty <- incoming deals
| |
| v
*<- WaitDeals <- incoming deals
| |
| v
*<- Packing <- incoming committed capacity
| |
| v
*<- PreCommit1 <--> SealPreCommit1Failed
| | ^ ^^
| | *----------++----\
| v v || |
*<- PreCommit2 --------++--> SealPreCommit2Failed
| | ||
| v /-------/|
* PreCommitting <-----+---> PreCommitFailed
| | | ^
| v | |
*<- WaitSeed -----------+-----/
| ||| ^ |
| ||| \--------*-----/
| ||| |
| vvv v----+----> ComputeProofFailed
*<- Committing |
| | ^--> CommitFailed
| v ^
*<- CommitWait ---/
| |
| v
| FinalizeSector <--> FinalizeFailed
| |
| v
*<- Proving
|
v
FailedUnrecoverable
UndefinedSectorState <- ¯\_()_/¯
| ^
*---------------------/
*/
m.stats.updateSector(m.minerSector(state.SectorNumber), state.State)
switch state.State {
// Happy path
2020-06-26 15:29:08 +00:00
case Empty:
fallthrough
case WaitDeals:
log.Infof("Waiting for deals %d", state.SectorNumber)
case Packing:
return m.handlePacking, nil
2020-04-06 20:23:37 +00:00
case PreCommit1:
2020-04-03 16:54:01 +00:00
return m.handlePreCommit1, nil
2020-04-06 20:23:37 +00:00
case PreCommit2:
2020-04-03 16:54:01 +00:00
return m.handlePreCommit2, nil
2020-04-06 20:23:37 +00:00
case PreCommitting:
return m.handlePreCommitting, nil
2020-05-18 22:49:21 +00:00
case PreCommitWait:
return m.handlePreCommitWait, nil
case WaitSeed:
2020-01-20 22:04:46 +00:00
return m.handleWaitSeed, nil
case Committing:
return m.handleCommitting, nil
case CommitWait:
return m.handleCommitWait, nil
case FinalizeSector:
2020-01-29 22:37:31 +00:00
return m.handleFinalizeSector, nil
// Handled failure modes
case SealPreCommit1Failed:
return m.handleSealPrecommit1Failed, nil
case SealPreCommit2Failed:
return m.handleSealPrecommit2Failed, nil
case PreCommitFailed:
2020-01-23 17:34:04 +00:00
return m.handlePreCommitFailed, nil
2020-04-06 20:23:37 +00:00
case ComputeProofFailed:
return m.handleComputeProofFailed, nil
2020-04-06 20:23:37 +00:00
case CommitFailed:
return m.handleCommitFailed, nil
2020-06-03 21:42:13 +00:00
case FinalizeFailed:
return m.handleFinalizeFailed, nil
2020-06-22 16:42:38 +00:00
// Post-seal
case Proving:
2020-07-02 20:09:59 +00:00
return m.handleProvingSector, nil
2020-06-22 16:42:38 +00:00
case Removing:
return m.handleRemoving, nil
2020-07-14 08:41:19 +00:00
case Removed:
return nil, nil
2020-06-22 16:42:38 +00:00
// Faults
case Faulty:
return m.handleFaulty, nil
case FaultReported:
return m.handleFaultReported, nil
// Fatal errors
case UndefinedSectorState:
log.Error("sector update with undefined state!")
case FailedUnrecoverable:
2020-04-06 22:31:33 +00:00
log.Errorf("sector %d failed unrecoverably", state.SectorNumber)
default:
2020-05-18 22:49:21 +00:00
log.Errorf("unexpected sector update state: %s", state.State)
}
return nil, nil
}
func planCommitting(events []statemachine.Event, state *SectorInfo) error {
for _, event := range events {
switch e := event.User.(type) {
case globalMutator:
if e.applyGlobal(state) {
return nil
}
case SectorCommitted: // the normal case
e.apply(state)
state.State = CommitWait
case SectorSeedReady: // seed changed :/
if e.SeedEpoch == state.SeedEpoch && bytes.Equal(e.SeedValue, state.SeedValue) {
log.Warnf("planCommitting: got SectorSeedReady, but the seed didn't change")
continue // or it didn't!
}
log.Warnf("planCommitting: commit Seed changed")
e.apply(state)
state.State = Committing
return nil
2020-01-20 22:04:46 +00:00
case SectorComputeProofFailed:
2020-04-06 20:23:37 +00:00
state.State = ComputeProofFailed
case SectorSealPreCommit1Failed:
2020-07-15 01:41:35 +00:00
state.State = SealPreCommit1Failed
case SectorCommitFailed:
state.State = CommitFailed
case SectorRetryCommitWait:
state.State = CommitWait
default:
return xerrors.Errorf("planCommitting got event of unknown type %T, events: %+v", event.User, events)
}
}
return nil
}
func (m *Sealing) restartSectors(ctx context.Context) error {
trackedSectors, err := m.ListSectors()
if err != nil {
log.Errorf("loading sector list: %+v", err)
}
2020-08-18 14:20:31 +00:00
cfg, err := m.getConfig()
if err != nil {
return xerrors.Errorf("getting the sealing delay: %w", err)
}
for _, sector := range trackedSectors {
2020-04-06 22:31:33 +00:00
if err := m.sectors.Send(uint64(sector.SectorNumber), SectorRestart{}); err != nil {
log.Errorf("restarting sector %d: %+v", sector.SectorNumber, err)
}
if sector.State == WaitDeals {
2020-08-18 14:20:31 +00:00
if cfg.WaitDealsDelay > 0 {
timer := time.NewTimer(cfg.WaitDealsDelay)
go func() {
<-timer.C
if err := m.StartPacking(sector.SectorNumber); err != nil {
log.Errorf("starting sector %d: %+v", sector.SectorNumber, err)
}
}()
}
}
}
// TODO: Grab on-chain sector set and diff with trackedSectors
return nil
}
func (m *Sealing) ForceSectorState(ctx context.Context, id abi.SectorNumber, state SectorState) error {
return m.sectors.Send(id, SectorForceState{state})
}
func final(events []statemachine.Event, state *SectorInfo) error {
2020-04-03 16:24:45 +00:00
return xerrors.Errorf("didn't expect any events in state %s, got %+v", state.State, events)
}
func on(mut mutator, next SectorState) func() (mutator, SectorState) {
return func() (mutator, SectorState) {
return mut, next
}
}
func planOne(ts ...func() (mut mutator, next SectorState)) func(events []statemachine.Event, state *SectorInfo) error {
return func(events []statemachine.Event, state *SectorInfo) error {
if len(events) != 1 {
for _, event := range events {
if gm, ok := event.User.(globalMutator); ok {
gm.applyGlobal(state)
return nil
}
}
2020-04-03 16:24:45 +00:00
return xerrors.Errorf("planner for state %s only has a plan for a single event only, got %+v", state.State, events)
}
if gm, ok := events[0].User.(globalMutator); ok {
2020-01-22 18:30:56 +00:00
gm.applyGlobal(state)
return nil
}
for _, t := range ts {
mut, next := t()
if reflect.TypeOf(events[0].User) != reflect.TypeOf(mut) {
continue
}
2020-01-21 16:05:10 +00:00
if err, iserr := events[0].User.(error); iserr {
2020-04-06 22:31:33 +00:00
log.Warnf("sector %d got error event %T: %+v", state.SectorNumber, events[0].User, err)
2020-01-21 16:05:10 +00:00
}
events[0].User.(mutator).apply(state)
state.State = next
return nil
}
_, ok := events[0].User.(Ignorable)
if ok {
return nil
}
2020-04-03 16:24:45 +00:00
return xerrors.Errorf("planner for state %s received unexpected event %T (%+v)", state.State, events[0].User, events[0])
}
}