Skip to content

Commit c07a240

Browse files
kocubinskialjo242
andauthored
refactor(v2): reduce leaf I/O (#1044)
Co-authored-by: Alex | Interchain Labs <[email protected]>
1 parent 00941d2 commit c07a240

15 files changed

+851
-828
lines changed

v2/cmd/bench/bench.go

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package bench
2+
3+
import (
4+
"log/slog"
5+
"net/http"
6+
"os"
7+
"runtime/pprof"
8+
"time"
9+
10+
"github.com/cosmos/iavl/v2"
11+
"github.com/cosmos/iavl/v2/metrics"
12+
"github.com/cosmos/iavl/v2/testutil"
13+
"github.com/prometheus/client_golang/prometheus"
14+
"github.com/prometheus/client_golang/prometheus/promauto"
15+
"github.com/prometheus/client_golang/prometheus/promhttp"
16+
"github.com/spf13/cobra"
17+
)
18+
19+
func Command() *cobra.Command {
20+
cmd := &cobra.Command{
21+
Use: "bench",
22+
Short: "run benchmarks",
23+
}
24+
cmd.AddCommand(benchCommand())
25+
return cmd
26+
}
27+
28+
func benchCommand() *cobra.Command {
29+
var (
30+
dbPath string
31+
changelogPath string
32+
loadSnapshot bool
33+
usePrometheus bool
34+
cpuProfile string
35+
)
36+
cmd := &cobra.Command{
37+
Use: "std",
38+
Short: "run the std development benchmark",
39+
Long: `Runs a longer benchmark for the IAVL tree. This is useful for development and testing.
40+
Pre-requisites this command:
41+
$ go run ./cmd gen tree --db /tmp/iavl-v2 --limit 1 --type osmo-like-many
42+
mkdir -p /tmp/osmo-like-many/v2 && go run ./cmd gen emit --start 2 --limit 1000 --type osmo-like-many --out /tmp/osmo-like-many/v2
43+
44+
Optional for --snapshot arg:
45+
$ go run ./cmd snapshot --db /tmp/iavl-v2 --version 1
46+
`,
47+
48+
RunE: func(_ *cobra.Command, _ []string) error {
49+
if cpuProfile != "" {
50+
f, err := os.Create(cpuProfile)
51+
if err != nil {
52+
return err
53+
}
54+
if err := pprof.StartCPUProfile(f); err != nil {
55+
return err
56+
}
57+
defer func() {
58+
pprof.StopCPUProfile()
59+
f.Close()
60+
}()
61+
}
62+
treeOpts := iavl.DefaultTreeOptions()
63+
treeOpts.CheckpointInterval = 80
64+
treeOpts.StateStorage = true
65+
treeOpts.HeightFilter = 1
66+
treeOpts.EvictionDepth = 22
67+
treeOpts.MetricsProxy = metrics.NewStructMetrics()
68+
if usePrometheus {
69+
treeOpts.MetricsProxy = newPrometheusMetricsProxy()
70+
}
71+
72+
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
73+
var multiTree *iavl.MultiTree
74+
if loadSnapshot {
75+
var err error
76+
multiTree, err = iavl.ImportMultiTree(logger, 1, dbPath, treeOpts)
77+
if err != nil {
78+
return err
79+
}
80+
} else {
81+
multiTree = iavl.NewMultiTree(logger, dbPath, treeOpts)
82+
if err := multiTree.MountTrees(); err != nil {
83+
return err
84+
}
85+
if err := multiTree.LoadVersion(1); err != nil {
86+
return err
87+
}
88+
if err := multiTree.WarmLeaves(); err != nil {
89+
return err
90+
}
91+
}
92+
93+
opts := testutil.CompactedChangelogs(changelogPath)
94+
opts.SampleRate = 250_000
95+
96+
// opts.Until = 1_000
97+
// opts.UntilHash = "557663181d9ab97882ecfc6538e3b4cfe31cd805222fae905c4b4f4403ca5cda"
98+
opts.Until = 500
99+
opts.UntilHash = "2670bd5767e70f2bf9e4f723b5f205759e39afdb5d8cfb6b54a4a3ecc27a1377"
100+
101+
_, err := multiTree.TestBuild(opts)
102+
return err
103+
},
104+
}
105+
cmd.Flags().StringVar(&dbPath, "db", "/tmp/iavl-v2", "the path to the database at version 1")
106+
cmd.Flags().StringVar(&changelogPath, "changelog", "/tmp/osmo-like-many/v2", "the path to the changelog")
107+
cmd.Flags().BoolVar(&loadSnapshot, "snapshot", false, "load the snapshot at version 1 before running the benchmarks (loads full tree into memory)")
108+
cmd.Flags().BoolVar(&usePrometheus, "prometheus", false, "enable prometheus metrics")
109+
cmd.Flags().StringVar(&cpuProfile, "cpu-profile", "", "write cpu profile to file")
110+
111+
if err := cmd.MarkFlagRequired("changelog"); err != nil {
112+
panic(err)
113+
}
114+
if err := cmd.MarkFlagRequired("db"); err != nil {
115+
panic(err)
116+
}
117+
return cmd
118+
}
119+
120+
var _ metrics.Proxy = &prometheusMetricsProxy{}
121+
122+
type prometheusMetricsProxy struct {
123+
workingSize prometheus.Gauge
124+
workingBytes prometheus.Gauge
125+
}
126+
127+
func newPrometheusMetricsProxy() *prometheusMetricsProxy {
128+
p := &prometheusMetricsProxy{}
129+
p.workingSize = promauto.NewGauge(prometheus.GaugeOpts{
130+
Name: "iavl_working_size",
131+
Help: "working size",
132+
})
133+
p.workingBytes = promauto.NewGauge(prometheus.GaugeOpts{
134+
Name: "iavl_working_bytes",
135+
Help: "working bytes",
136+
})
137+
http.Handle("/metrics", promhttp.Handler())
138+
go func() {
139+
err := http.ListenAndServe(":2112", nil)
140+
if err != nil {
141+
panic(err)
142+
}
143+
}()
144+
return p
145+
}
146+
147+
func (p *prometheusMetricsProxy) IncrCounter(_ float32, _ ...string) {
148+
}
149+
150+
func (p *prometheusMetricsProxy) SetGauge(val float32, keys ...string) {
151+
k := keys[1]
152+
switch k {
153+
case "working_size":
154+
p.workingSize.Set(float64(val))
155+
case "working_bytes":
156+
p.workingBytes.Set(float64(val))
157+
}
158+
}
159+
160+
func (p *prometheusMetricsProxy) MeasureSince(_ time.Time, _ ...string) {}

v2/cmd/gen/gen.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ func treeCommand() *cobra.Command {
144144
cmd := &cobra.Command{
145145
Use: "tree",
146146
Short: "build and save a Tree to disk, taking generated changesets as input",
147-
RunE: func(cmd *cobra.Command, args []string) error {
148-
multiTree := iavl.NewMultiTree(iavl.NewTestLogger(), dbPath, iavl.TreeOptions{StateStorage: true})
147+
RunE: func(_ *cobra.Command, _ []string) error {
148+
multiTree := iavl.NewMultiTree(iavl.NewDebugLogger(), dbPath, iavl.DefaultTreeOptions())
149149
defer func(mt *iavl.MultiTree) {
150150
err := mt.Close()
151151
if err != nil {

v2/cmd/root.go

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"github.com/cosmos/iavl/v2/cmd/bench"
45
"github.com/cosmos/iavl/v2/cmd/gen"
56
"github.com/cosmos/iavl/v2/cmd/rollback"
67
"github.com/cosmos/iavl/v2/cmd/scan"
@@ -19,6 +20,7 @@ func RootCommand() (*cobra.Command, error) {
1920
rollback.Command(),
2021
scan.Command(),
2122
latestCommand(),
23+
bench.Command(),
2224
)
2325
return cmd, nil
2426
}

v2/iterator_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ func Test_Iterator(t *testing.T) {
1313
sql, err := iavl.NewInMemorySqliteDb(pool)
1414
require.NoError(t, err)
1515

16-
tree := iavl.NewTree(sql, pool, iavl.TreeOptions{StateStorage: false})
16+
opts := iavl.DefaultTreeOptions()
17+
tree := iavl.NewTree(sql, pool, opts)
1718
set := func(key string, value string) {
1819
_, err := tree.Set([]byte(key), []byte(value))
1920
require.NoError(t, err)
@@ -226,7 +227,8 @@ func Test_IteratorTree(t *testing.T) {
226227
sql, err := iavl.NewSqliteDb(pool, iavl.SqliteDbOptions{Path: tmpDir})
227228
require.NoError(t, err)
228229

229-
tree := iavl.NewTree(sql, pool, iavl.TreeOptions{StateStorage: true})
230+
opts := iavl.DefaultTreeOptions()
231+
tree := iavl.NewTree(sql, pool, opts)
230232
set := func(key string, value string) {
231233
_, err := tree.Set([]byte(key), []byte(value))
232234
require.NoError(t, err)
@@ -241,7 +243,7 @@ func Test_IteratorTree(t *testing.T) {
241243

242244
_, version, err := tree.SaveVersion()
243245
require.NoError(t, err)
244-
tree = iavl.NewTree(sql, pool, iavl.TreeOptions{StateStorage: true})
246+
tree = iavl.NewTree(sql, pool, opts)
245247
require.NoError(t, tree.LoadVersion(version))
246248
cases := []struct {
247249
name string
@@ -304,5 +306,4 @@ func Test_IteratorTree(t *testing.T) {
304306
require.NoError(t, itr.Close())
305307
})
306308
}
307-
308309
}

v2/logger.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package iavl
22

3-
import "log/slog"
3+
import (
4+
"log/slog"
5+
"os"
6+
)
47

58
// Logger defines basic logger that IAVL expects.
69
// It is a subset of the cosmossdk.io/core/log.Logger interface.
@@ -39,6 +42,10 @@ func NewTestLogger() Logger {
3942
return &testLogger{}
4043
}
4144

45+
func NewDebugLogger() Logger {
46+
return slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
47+
}
48+
4249
type testLogger struct{}
4350

4451
func (l *testLogger) Info(msg string, keys ...any) {

0 commit comments

Comments
 (0)