lotus/cmd/lotus-sim/info_state.go

142 lines
3.0 KiB
Go

package main
import (
"bytes"
"context"
"fmt"
"math"
"sync"
"sync/atomic"
"github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types"
)
var infoStateGrowthSimCommand = &cli.Command{
Name: "state-size",
Description: "List daily state size over the course of the simulation starting at the end.",
Action: func(cctx *cli.Context) (err error) {
node, err := open(cctx)
if err != nil {
return err
}
defer func() {
if cerr := node.Close(); err == nil {
err = cerr
}
}()
sim, err := node.LoadSim(cctx.Context, cctx.String("simulation"))
if err != nil {
return err
}
// NOTE: This code is entirely read-bound.
store := node.Chainstore.StateBlockstore()
stateSize := func(ctx context.Context, c cid.Cid) (uint64, error) {
seen := cid.NewSet()
sema := make(chan struct{}, 40)
var lock sync.Mutex
var recSize func(cid.Cid) (uint64, error)
recSize = func(c cid.Cid) (uint64, error) {
// Not a part of the chain state.
if err := ctx.Err(); err != nil {
return 0, err
}
lock.Lock()
visit := seen.Visit(c)
lock.Unlock()
// Already seen?
if !visit {
return 0, nil
}
var links []cid.Cid
var totalSize uint64
if err := store.View(cctx.Context, c, func(data []byte) error {
totalSize += uint64(len(data))
return cbg.ScanForLinks(bytes.NewReader(data), func(c cid.Cid) {
if c.Prefix().Codec != cid.DagCBOR {
return
}
links = append(links, c)
})
}); err != nil {
return 0, err
}
var wg sync.WaitGroup
errCh := make(chan error, 1)
cb := func(c cid.Cid) {
size, err := recSize(c)
if err != nil {
select {
case errCh <- err:
default:
}
return
}
atomic.AddUint64(&totalSize, size)
}
asyncCb := func(c cid.Cid) {
wg.Add(1)
go func() {
defer wg.Done()
defer func() { <-sema }()
cb(c)
}()
}
for _, link := range links {
select {
case sema <- struct{}{}:
asyncCb(link)
default:
cb(link)
}
}
wg.Wait()
select {
case err := <-errCh:
return 0, err
default:
}
return totalSize, nil
}
return recSize(c)
}
firstEpoch := sim.GetStart().Height()
ts := sim.GetHead()
lastHeight := abi.ChainEpoch(math.MaxInt64)
for ts.Height() > firstEpoch && cctx.Err() == nil {
if ts.Height()+builtin.EpochsInDay <= lastHeight {
lastHeight = ts.Height()
parentStateSize, err := stateSize(cctx.Context, ts.ParentState())
if err != nil {
return err
}
_, _ = fmt.Fprintf(cctx.App.Writer, "%d: %s\n", ts.Height(), types.SizeStr(types.NewInt(parentStateSize)))
}
ts, err = sim.Node.Chainstore.LoadTipSet(cctx.Context, ts.Parents())
if err != nil {
return err
}
}
return cctx.Err()
},
}