github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/benchmarks/cosmos-exim/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/fibonacci-chain/fbc/libs/iavl"
    10  	tmdb "github.com/fibonacci-chain/fbc/libs/tm-db"
    11  )
    12  
    13  // stores is the list of stores in the CosmosHub database
    14  // FIXME would be nice to autodetect this
    15  var stores = []string{
    16  	"acc",
    17  	"distribution",
    18  	"evidence",
    19  	"god",
    20  	"main",
    21  	"mint",
    22  	"params",
    23  	"slashing",
    24  	"staking",
    25  	"supply",
    26  	"upgrade",
    27  }
    28  
    29  // Stats track import/export statistics
    30  type Stats struct {
    31  	nodes     uint64
    32  	leafNodes uint64
    33  	size      uint64
    34  	duration  time.Duration
    35  }
    36  
    37  func (s *Stats) Add(o Stats) {
    38  	s.nodes += o.nodes
    39  	s.leafNodes += o.leafNodes
    40  	s.size += o.size
    41  	s.duration += o.duration
    42  }
    43  
    44  func (s *Stats) AddDurationSince(started time.Time) {
    45  	s.duration += time.Since(started)
    46  }
    47  
    48  func (s *Stats) AddNode(node *iavl.ExportNode) {
    49  	s.nodes++
    50  	if node.Height == 0 {
    51  		s.leafNodes++
    52  	}
    53  	s.size += uint64(len(node.Key) + len(node.Value) + 8 + 1)
    54  }
    55  
    56  func (s *Stats) String() string {
    57  	return fmt.Sprintf("%v nodes (%v leaves) in %v with size %v MB",
    58  		s.nodes, s.leafNodes, s.duration.Round(time.Millisecond), s.size/1024/1024)
    59  }
    60  
    61  // main runs the main program
    62  func main() {
    63  	if len(os.Args) != 2 {
    64  		fmt.Fprintf(os.Stderr, "Usage: %v <dbpath>\n", os.Args[0])
    65  		os.Exit(1)
    66  	}
    67  	err := run(os.Args[1])
    68  	if err != nil {
    69  		fmt.Fprintf(os.Stderr, "Error: %v\n", err.Error())
    70  		os.Exit(1)
    71  	}
    72  }
    73  
    74  // run runs the command with normal error handling
    75  func run(dbPath string) error {
    76  	version, exports, err := runExport(dbPath)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	err = runImport(version, exports)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	return nil
    86  }
    87  
    88  // runExport runs an export benchmark and returns a map of store names/export nodes
    89  func runExport(dbPath string) (int64, map[string][]*iavl.ExportNode, error) {
    90  	ldb := tmdb.NewDB("application", tmdb.GoLevelDBBackend, dbPath)
    91  	tree, err := iavl.NewMutableTree(tmdb.NewPrefixDB(ldb, []byte("s/k:main/")), 0)
    92  	if err != nil {
    93  		return 0, nil, err
    94  	}
    95  	version, err := tree.LoadVersion(0)
    96  	if err != nil {
    97  		return 0, nil, err
    98  	}
    99  	fmt.Printf("Exporting cosmoshub database at version %v\n\n", version)
   100  
   101  	exports := make(map[string][]*iavl.ExportNode, len(stores))
   102  
   103  	totalStats := Stats{}
   104  	for _, name := range stores {
   105  		db := tmdb.NewPrefixDB(ldb, []byte("s/k:"+name+"/"))
   106  		tree, err := iavl.NewMutableTree(db, 0)
   107  		if err != nil {
   108  			return 0, nil, err
   109  		}
   110  
   111  		stats := Stats{}
   112  		export := make([]*iavl.ExportNode, 0, 100000)
   113  
   114  		storeVersion, err := tree.LoadVersion(0)
   115  		if err != nil {
   116  			return 0, nil, err
   117  		}
   118  		if storeVersion == 0 {
   119  			fmt.Printf("%-13v: %v\n", name, stats.String())
   120  			continue
   121  		}
   122  
   123  		itree, err := tree.GetImmutable(version)
   124  		if err != nil {
   125  			return 0, nil, err
   126  		}
   127  		start := time.Now().UTC()
   128  		exporter := itree.Export()
   129  		defer exporter.Close()
   130  		for {
   131  			node, err := exporter.Next()
   132  			if err == iavl.ExportDone {
   133  				break
   134  			} else if err != nil {
   135  				return 0, nil, err
   136  			}
   137  			export = append(export, node)
   138  			stats.AddNode(node)
   139  		}
   140  		stats.AddDurationSince(start)
   141  		fmt.Printf("%-13v: %v\n", name, stats.String())
   142  		totalStats.Add(stats)
   143  		exports[name] = export
   144  	}
   145  
   146  	fmt.Printf("\nExported %v stores with %v\n\n", len(stores), totalStats.String())
   147  
   148  	return version, exports, nil
   149  }
   150  
   151  // runImport runs an import benchmark with nodes exported from runExport()
   152  func runImport(version int64, exports map[string][]*iavl.ExportNode) error {
   153  	fmt.Print("Importing into new LevelDB stores\n\n")
   154  
   155  	totalStats := Stats{}
   156  
   157  	for _, name := range stores {
   158  		tempdir, err := ioutil.TempDir("", name)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		defer os.RemoveAll(tempdir)
   163  
   164  		start := time.Now()
   165  		stats := Stats{}
   166  
   167  		newDB := tmdb.NewDB(name, tmdb.GoLevelDBBackend, tempdir)
   168  		newTree, err := iavl.NewMutableTree(newDB, 0)
   169  		if err != nil {
   170  			return err
   171  		}
   172  		importer, err := newTree.Import(version)
   173  		if err != nil {
   174  			return err
   175  		}
   176  		defer importer.Close()
   177  		for _, node := range exports[name] {
   178  			err = importer.Add(node)
   179  			if err != nil {
   180  				return err
   181  			}
   182  			stats.AddNode(node)
   183  		}
   184  		err = importer.Commit()
   185  		if err != nil {
   186  			return err
   187  		}
   188  		stats.AddDurationSince(start)
   189  		fmt.Printf("%-12v: %v\n", name, stats.String())
   190  		totalStats.Add(stats)
   191  	}
   192  
   193  	fmt.Printf("\nImported %v stores with %v\n", len(stores), totalStats.String())
   194  
   195  	return nil
   196  }