Merge pull request #248 from filecoin-project/feat/sync-state-cmd
Add a command to inspect sync state progress
This commit is contained in:
commit
bc7faec5d2
21
api/api.go
21
api/api.go
@ -57,6 +57,9 @@ type FullNode interface {
|
|||||||
ChainGetBlockReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
|
ChainGetBlockReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
|
||||||
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||||
|
|
||||||
|
// syncer
|
||||||
|
SyncState(context.Context) (*SyncState, error)
|
||||||
|
|
||||||
// messages
|
// messages
|
||||||
|
|
||||||
MpoolPending(context.Context, *types.TipSet) ([]*types.SignedMessage, error)
|
MpoolPending(context.Context, *types.TipSet) ([]*types.SignedMessage, error)
|
||||||
@ -292,3 +295,21 @@ type ReplayResults struct {
|
|||||||
Receipt *types.MessageReceipt
|
Receipt *types.MessageReceipt
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SyncState struct {
|
||||||
|
Base *types.TipSet
|
||||||
|
Target *types.TipSet
|
||||||
|
|
||||||
|
Stage SyncStateStage
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncStateStage int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StageIdle = SyncStateStage(iota)
|
||||||
|
StageHeaders
|
||||||
|
StagePersistHeaders
|
||||||
|
StageMessages
|
||||||
|
StageSyncComplete
|
||||||
|
)
|
||||||
|
@ -47,6 +47,8 @@ type FullNodeStruct struct {
|
|||||||
ChainGetBlockReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
ChainGetBlockReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
||||||
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
|
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
|
||||||
|
|
||||||
|
SyncState func(context.Context) (*SyncState, error) `perm:"read"`
|
||||||
|
|
||||||
MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
|
MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
|
||||||
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
|
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
|
||||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||||
@ -284,6 +286,10 @@ func (c *FullNodeStruct) ChainNotify(ctx context.Context) (<-chan []*store.HeadC
|
|||||||
return c.Internal.ChainNotify(ctx)
|
return c.Internal.ChainNotify(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *FullNodeStruct) SyncState(ctx context.Context) (*SyncState, error) {
|
||||||
|
return c.Internal.SyncState(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address) ([]*SectorInfo, error) {
|
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address) ([]*SectorInfo, error) {
|
||||||
return c.Internal.StateMinerSectors(ctx, addr)
|
return c.Internal.StateMinerSectors(ctx, addr)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
"github.com/filecoin-project/go-lotus/build"
|
"github.com/filecoin-project/go-lotus/build"
|
||||||
"github.com/filecoin-project/go-lotus/chain/actors"
|
"github.com/filecoin-project/go-lotus/chain/actors"
|
||||||
"github.com/filecoin-project/go-lotus/chain/address"
|
"github.com/filecoin-project/go-lotus/chain/address"
|
||||||
@ -50,6 +51,8 @@ type Syncer struct {
|
|||||||
|
|
||||||
self peer.ID
|
self peer.ID
|
||||||
|
|
||||||
|
syncState SyncerState
|
||||||
|
|
||||||
// peer heads
|
// peer heads
|
||||||
// Note: clear cache on disconnects
|
// Note: clear cache on disconnects
|
||||||
peerHeads map[peer.ID]*types.TipSet
|
peerHeads map[peer.ID]*types.TipSet
|
||||||
@ -516,6 +519,8 @@ func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*t
|
|||||||
at = ts.Parents()
|
at = ts.Parents()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetHeight(blockSet[len(blockSet)-1].Height())
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for blockSet[len(blockSet)-1].Height() > untilHeight {
|
for blockSet[len(blockSet)-1].Height() > untilHeight {
|
||||||
// NB: GetBlocks validates that the blocks are in-fact the ones we
|
// NB: GetBlocks validates that the blocks are in-fact the ones we
|
||||||
@ -544,6 +549,7 @@ loop:
|
|||||||
blockSet = append(blockSet, b)
|
blockSet = append(blockSet, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetHeight(blks[len(blockSet)-1].Height())
|
||||||
at = blks[len(blks)-1].Parents()
|
at = blks[len(blks)-1].Parents()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +571,7 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) syncMessagesAndCheckState(headers []*types.TipSet) error {
|
func (syncer *Syncer) syncMessagesAndCheckState(headers []*types.TipSet) error {
|
||||||
|
syncer.syncState.SetHeight(0)
|
||||||
return syncer.iterFullTipsets(headers, func(fts *store.FullTipSet) error {
|
return syncer.iterFullTipsets(headers, func(fts *store.FullTipSet) error {
|
||||||
log.Debugf("validating tipset (heigt=%d, size=%d)", fts.TipSet().Height(), len(fts.TipSet().Cids()))
|
log.Debugf("validating tipset (heigt=%d, size=%d)", fts.TipSet().Height(), len(fts.TipSet().Cids()))
|
||||||
if err := syncer.ValidateTipSet(context.TODO(), fts); err != nil {
|
if err := syncer.ValidateTipSet(context.TODO(), fts); err != nil {
|
||||||
@ -572,6 +579,8 @@ func (syncer *Syncer) syncMessagesAndCheckState(headers []*types.TipSet) error {
|
|||||||
return xerrors.Errorf("message processing failed: %w", err)
|
return xerrors.Errorf("message processing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetHeight(fts.TipSet().Height())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -668,11 +677,15 @@ func persistMessages(bs bstore.Blockstore, bst *BSTipSet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
|
func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
|
||||||
|
syncer.syncState.Init(syncer.store.GetHeaviestTipSet(), fts.TipSet())
|
||||||
|
|
||||||
headers, err := syncer.collectHeaders(fts.TipSet(), syncer.store.GetHeaviestTipSet())
|
headers, err := syncer.collectHeaders(fts.TipSet(), syncer.store.GetHeaviestTipSet())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetStage(api.StagePersistHeaders)
|
||||||
|
|
||||||
for _, ts := range headers {
|
for _, ts := range headers {
|
||||||
for _, b := range ts.Blocks() {
|
for _, b := range ts.Blocks() {
|
||||||
if err := syncer.store.PersistBlockHeader(b); err != nil {
|
if err := syncer.store.PersistBlockHeader(b); err != nil {
|
||||||
@ -681,10 +694,14 @@ func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetStage(api.StageMessages)
|
||||||
|
|
||||||
if err := syncer.syncMessagesAndCheckState(headers); err != nil {
|
if err := syncer.syncMessagesAndCheckState(headers); err != nil {
|
||||||
return xerrors.Errorf("collectChain syncMessages: %w", err)
|
return xerrors.Errorf("collectChain syncMessages: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncer.syncState.SetStage(api.StageSyncComplete)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,3 +717,7 @@ func VerifyElectionProof(ctx context.Context, eproof []byte, rand []byte, worker
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) State() SyncerState {
|
||||||
|
return syncer.syncState.Snapshot()
|
||||||
|
}
|
||||||
|
64
chain/syncstate.go
Normal file
64
chain/syncstate.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package chain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
|
"github.com/filecoin-project/go-lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SyncStageString(v api.SyncStateStage) string {
|
||||||
|
switch v {
|
||||||
|
case api.StageHeaders:
|
||||||
|
return "header sync"
|
||||||
|
case api.StagePersistHeaders:
|
||||||
|
return "persisting headers"
|
||||||
|
case api.StageMessages:
|
||||||
|
return "message sync"
|
||||||
|
case api.StageSyncComplete:
|
||||||
|
return "complete"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("<unknown: %d>", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncerState struct {
|
||||||
|
lk sync.Mutex
|
||||||
|
Target *types.TipSet
|
||||||
|
Base *types.TipSet
|
||||||
|
Stage api.SyncStateStage
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SyncerState) SetStage(v api.SyncStateStage) {
|
||||||
|
ss.lk.Lock()
|
||||||
|
defer ss.lk.Unlock()
|
||||||
|
ss.Stage = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SyncerState) Init(base, target *types.TipSet) {
|
||||||
|
ss.lk.Lock()
|
||||||
|
defer ss.lk.Unlock()
|
||||||
|
ss.Target = target
|
||||||
|
ss.Base = base
|
||||||
|
ss.Stage = api.StageHeaders
|
||||||
|
ss.Height = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SyncerState) SetHeight(h uint64) {
|
||||||
|
ss.lk.Lock()
|
||||||
|
defer ss.lk.Unlock()
|
||||||
|
ss.Height = h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *SyncerState) Snapshot() SyncerState {
|
||||||
|
ss.lk.Lock()
|
||||||
|
defer ss.lk.Unlock()
|
||||||
|
return SyncerState{
|
||||||
|
Base: ss.Base,
|
||||||
|
Target: ss.Target,
|
||||||
|
Stage: ss.Stage,
|
||||||
|
Height: ss.Height,
|
||||||
|
}
|
||||||
|
}
|
@ -119,6 +119,7 @@ var Commands = []*cli.Command{
|
|||||||
paychCmd,
|
paychCmd,
|
||||||
sendCmd,
|
sendCmd,
|
||||||
stateCmd,
|
stateCmd,
|
||||||
|
syncCmd,
|
||||||
versionCmd,
|
versionCmd,
|
||||||
walletCmd,
|
walletCmd,
|
||||||
}
|
}
|
||||||
|
50
cli/sync.go
Normal file
50
cli/sync.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gopkg.in/urfave/cli.v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/chain"
|
||||||
|
cid "github.com/ipfs/go-cid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var syncCmd = &cli.Command{
|
||||||
|
Name: "sync",
|
||||||
|
Usage: "Inspect or interact with the chain syncer",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
syncStatusCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var syncStatusCmd = &cli.Command{
|
||||||
|
Name: "status",
|
||||||
|
Usage: "check sync status",
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
api, err := GetFullNodeAPI(cctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx := ReqContext(cctx)
|
||||||
|
|
||||||
|
ss, err := api.SyncState(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var base, target []cid.Cid
|
||||||
|
if ss.Base != nil {
|
||||||
|
base = ss.Base.Cids()
|
||||||
|
}
|
||||||
|
if ss.Target != nil {
|
||||||
|
target = ss.Target.Cids()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("sync status:")
|
||||||
|
fmt.Printf("Base:\t%s\n", base)
|
||||||
|
fmt.Printf("Target:\t%s\n", target)
|
||||||
|
fmt.Printf("Stage: %s\n", chain.SyncStageString(ss.Stage))
|
||||||
|
fmt.Printf("Height: %d\n", ss.Height)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -2,6 +2,7 @@ package impl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-lotus/node/impl/client"
|
"github.com/filecoin-project/go-lotus/node/impl/client"
|
||||||
"github.com/filecoin-project/go-lotus/node/impl/paych"
|
"github.com/filecoin-project/go-lotus/node/impl/paych"
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ type FullNodeAPI struct {
|
|||||||
paych.PaychAPI
|
paych.PaychAPI
|
||||||
full.StateAPI
|
full.StateAPI
|
||||||
full.WalletAPI
|
full.WalletAPI
|
||||||
|
full.SyncAPI
|
||||||
|
|
||||||
Miner *miner.Miner
|
Miner *miner.Miner
|
||||||
}
|
}
|
||||||
|
25
node/impl/full/sync.go
Normal file
25
node/impl/full/sync.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package full
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
|
"github.com/filecoin-project/go-lotus/chain"
|
||||||
|
"go.uber.org/fx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncAPI struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
Syncer *chain.Syncer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *SyncAPI) SyncState(ctx context.Context) (*api.SyncState, error) {
|
||||||
|
ss := a.Syncer.State()
|
||||||
|
return &api.SyncState{
|
||||||
|
Base: ss.Base,
|
||||||
|
Target: ss.Target,
|
||||||
|
Stage: ss.Stage,
|
||||||
|
Height: ss.Height,
|
||||||
|
}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user