Skip to content

Commit 71fc739

Browse files
authored
feat: Upgrade rocksdb to v10.9.1 and add tuning profiles. (#1977)
* use rocksdb 10.9.1 * tune-up rocksdb * provides rocksdb tune-up options for the different node types * update changelog * nit * fix rocksdb build issue in nix * update changelog * lint * deploy cache when loadLatestOptions failed * rollback rocksdb to v10.9.1 * fix tune-up issues * update CHANGELOG * fix mem leak * update cronos-store revision * revert nix/rocksdb postPatch change * add DirectIOForCompaction flag * update toml comment * add proper destroys * add comment --------- Signed-off-by: JayT106 <JayT106@users.noreply.github.com>
1 parent 645f0bd commit 71fc739

13 files changed

Lines changed: 344 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Improvements
66

7+
* [#1977](https://github.com/crypto-org-chain/cronos/pull/1977) Upgrade rocksdb to v10.9.1 and add tuning profiles.
78
* [#1970](https://github.com/crypto-org-chain/cronos/pull/1970) feat(testground): refine testground test process in local
89
* [#1971](https://github.com/crypto-org-chain/cronos/pull/1971) Upgrade cosmos-sdk to v0.53.4.
910
* [#1976](https://github.com/crypto-org-chain/cronos/pull/1976) Minor improvements for cosmos-sdk v0.53.4 upgrade.

cmd/cronosd/cmd/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ func initAppConfig() (string, interface{}) {
276276
MemIAVL memiavlcfg.MemIAVLConfig `mapstructure:"memiavl"`
277277
VersionDB VersionDBConfig `mapstructure:"versiondb"`
278278
Cronos config2.CronosConfig `mapstructure:"cronos"`
279+
RocksDB config2.RocksDBConfig `mapstructure:"rocksdb"`
279280
}
280281

281282
tpl, cfg := servercfg.AppConfig("")
@@ -285,9 +286,10 @@ func initAppConfig() (string, interface{}) {
285286
MemIAVL: memiavlcfg.DefaultMemIAVLConfig(),
286287
VersionDB: DefaultVersionDBConfig(),
287288
Cronos: config2.DefaultCronosConfig(),
289+
RocksDB: config2.DefaultRocksDBConfig(),
288290
}
289291

290-
return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate + config2.DefaultCronosConfigTemplate, customAppConfig
292+
return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate + config2.DefaultCronosConfigTemplate + config2.DefaultRocksDBConfigTemplate, customAppConfig
291293
}
292294

293295
// newApp creates the application

cmd/cronosd/cmd/versiondb.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func ChangeSetCmd() *cobra.Command {
2424
DefaultStores: storeNames,
2525
OpenReadOnlyDB: opendb.OpenReadOnlyDB,
2626
AppRocksDBOptions: func(sstFileWriter bool) *grocksdb.Options {
27-
return opendb.NewRocksdbOptions(nil, sstFileWriter)
27+
return opendb.NewRocksdbOptions(nil, sstFileWriter, opendb.RocksDBTuneUpOptions{})
2828
},
2929
})
3030
}

cmd/cronosd/config/config.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package config
22

3-
import sdk "github.com/cosmos/cosmos-sdk/types"
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
sdk "github.com/cosmos/cosmos-sdk/types"
8+
)
49

510
// SetBech32Prefixes sets the global prefixes to be used when serializing addresses and public keys to Bech32 strings.
611
func SetBech32Prefixes(config *sdk.Config) {
@@ -16,9 +21,40 @@ type CronosConfig struct {
1621
DisableOptimisticExecution bool `mapstructure:"disable-optimistic-execution"`
1722
}
1823

24+
const (
25+
NodeTypeDefault = ""
26+
NodeTypeValidator = "validator"
27+
NodeTypeRPC = "rpc"
28+
NodeTypeArchive = "archive"
29+
)
30+
31+
type RocksDBConfig struct {
32+
// Defines the tuning profile for RocksDB based on the node's primary workload.
33+
// Valid values: "", "validator", "rpc", "archive"
34+
NodeType string `mapstructure:"node_type"`
35+
}
36+
37+
func (c *RocksDBConfig) Validate() error {
38+
normalized := strings.ToLower(strings.TrimSpace(c.NodeType))
39+
switch normalized {
40+
case NodeTypeDefault, NodeTypeValidator, NodeTypeRPC, NodeTypeArchive:
41+
c.NodeType = normalized
42+
return nil
43+
default:
44+
return fmt.Errorf("invalid rocksdb.node_type %q: allowed values are %q, %q, %q, or %q (empty)",
45+
c.NodeType, NodeTypeValidator, NodeTypeRPC, NodeTypeArchive, NodeTypeDefault)
46+
}
47+
}
48+
1949
func DefaultCronosConfig() CronosConfig {
2050
return CronosConfig{
2151
DisableTxReplacement: false,
2252
DisableOptimisticExecution: false,
2353
}
2454
}
55+
56+
func DefaultRocksDBConfig() RocksDBConfig {
57+
return RocksDBConfig{
58+
NodeType: "",
59+
}
60+
}

cmd/cronosd/config/toml.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,21 @@ disable-tx-replacement = {{ .Cronos.DisableTxReplacement }}
1414
# Set to true to disable optimistic execution (not recommended on validator nodes).
1515
disable-optimistic-execution = {{ .Cronos.DisableOptimisticExecution }}
1616
`
17+
18+
// DefaultRocksDBConfigTemplate defines the configuration template for rocksdb configuration
19+
const DefaultRocksDBConfigTemplate = `
20+
###############################################################################
21+
### RocksDB Configuration ###
22+
###############################################################################
23+
24+
[rocksdb]
25+
26+
# Defines the tuning profile for RocksDB based on the node's primary workload.
27+
# This is an experimental feature for performance optimization.
28+
# Valid values:
29+
# - "" (default): standard configuration, safe for all nodes.
30+
# - "validator" : optimizes for lowest latency point-lookups (state reads) during block execution.
31+
# - "rpc" : optimizes for highly concurrent read workloads (eth_calls, state queries) with lock-free caches.
32+
# - "archive" : optimizes for massive historical data scanning and sequential reads (eth_getLogs).
33+
node_type = "{{ .RocksDB.NodeType }}"
34+
`

cmd/cronosd/dbmigrate/migrate_rocksdb.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// PrepareRocksDBOptions returns RocksDB options for migration
1313
func PrepareRocksDBOptions() interface{} {
14-
return opendb.NewRocksdbOptions(nil, false)
14+
return opendb.NewRocksdbOptions(nil, false, opendb.RocksDBTuneUpOptions{})
1515
}
1616

1717
// openRocksDBForMigration opens a RocksDB database for migration (write mode)

cmd/cronosd/opendb/opendb_rocksdb.go

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,59 @@
33
package opendb
44

55
import (
6+
"fmt"
67
"path/filepath"
78
"runtime"
89
"strings"
910

1011
dbm "github.com/cosmos/cosmos-db"
12+
cronosconfig "github.com/crypto-org-chain/cronos/cmd/cronosd/config"
1113
"github.com/linxGnu/grocksdb"
14+
"github.com/spf13/cast"
1215

1316
"github.com/cosmos/cosmos-sdk/server/types"
1417
)
1518

1619
// BlockCacheSize 3G block cache
1720
const BlockCacheSize = 3 << 30
1821

19-
func OpenDB(_ types.AppOptions, home string, backendType dbm.BackendType) (dbm.DB, error) {
22+
type RocksDBTuneUpOptions struct {
23+
EnableAsyncIo bool
24+
EnableAutoReadaheadSize bool
25+
EnableOptimizeForPointLookup bool
26+
EnableHyperClockCache bool
27+
EnableDirectIOForCompaction bool
28+
}
29+
30+
func OpenDB(appOpts types.AppOptions, home string, backendType dbm.BackendType) (dbm.DB, error) {
2031
dataDir := filepath.Join(home, "data")
32+
2133
if backendType == dbm.RocksDBBackend {
22-
return openRocksdb(filepath.Join(dataDir, "application.db"), false)
34+
tuneUpOpts := RocksDBTuneUpOptions{}
35+
if appOpts != nil {
36+
if v := appOpts.Get("rocksdb.node_type"); v != nil {
37+
cfg := cronosconfig.RocksDBConfig{NodeType: cast.ToString(v)}
38+
if err := cfg.Validate(); err != nil {
39+
return nil, fmt.Errorf("invalid rocksdb configuration: %w", err)
40+
}
41+
switch cfg.NodeType {
42+
case cronosconfig.NodeTypeValidator:
43+
tuneUpOpts.EnableOptimizeForPointLookup = true
44+
case cronosconfig.NodeTypeRPC:
45+
tuneUpOpts.EnableAutoReadaheadSize = true
46+
tuneUpOpts.EnableOptimizeForPointLookup = true
47+
tuneUpOpts.EnableHyperClockCache = true
48+
tuneUpOpts.EnableDirectIOForCompaction = true
49+
case cronosconfig.NodeTypeArchive:
50+
tuneUpOpts.EnableAsyncIo = true
51+
tuneUpOpts.EnableAutoReadaheadSize = true
52+
tuneUpOpts.EnableHyperClockCache = true
53+
tuneUpOpts.EnableOptimizeForPointLookup = true
54+
tuneUpOpts.EnableDirectIOForCompaction = true
55+
}
56+
}
57+
}
58+
return openRocksdb(filepath.Join(dataDir, "application.db"), false, tuneUpOpts)
2359
}
2460

2561
return dbm.NewDB("application", backendType, dataDir)
@@ -29,19 +65,31 @@ func OpenDB(_ types.AppOptions, home string, backendType dbm.BackendType) (dbm.D
2965
func OpenReadOnlyDB(home string, backendType dbm.BackendType) (dbm.DB, error) {
3066
dataDir := filepath.Join(home, "data")
3167
if backendType == dbm.RocksDBBackend {
32-
return openRocksdb(filepath.Join(dataDir, "application.db"), true)
68+
return openRocksdb(filepath.Join(dataDir, "application.db"), true, RocksDBTuneUpOptions{})
3369
}
3470

3571
return dbm.NewDB("application", backendType, dataDir)
3672
}
3773

38-
func openRocksdb(dir string, readonly bool) (dbm.DB, error) {
39-
opts, err := loadLatestOptions(dir)
74+
func openRocksdb(dir string, readonly bool, tuneUpOpts RocksDBTuneUpOptions) (dbm.DB, error) {
75+
var cache *grocksdb.Cache
76+
if tuneUpOpts.EnableHyperClockCache {
77+
cache = grocksdb.NewHyperClockCache(BlockCacheSize, 0)
78+
} else {
79+
cache = grocksdb.NewLRUCache(BlockCacheSize)
80+
}
81+
82+
// explicitly destroy cache to avoid memory leak. RocksDB holds a shared_ptr to the cache
83+
// after table factory setup. Destroy() just decrements the refcount.
84+
defer cache.Destroy()
85+
86+
opts, err := loadLatestOptions(dir, cache)
4087
if err != nil {
4188
return nil, err
4289
}
4390
// customize rocksdb options
44-
opts = NewRocksdbOptions(opts, false)
91+
opts = newRocksdbOptions(opts, false, tuneUpOpts, cache)
92+
defer opts.Destroy()
4593

4694
var db *grocksdb.DB
4795
if readonly {
@@ -54,38 +102,52 @@ func openRocksdb(dir string, readonly bool) (dbm.DB, error) {
54102
}
55103

56104
ro := grocksdb.NewDefaultReadOptions()
105+
if tuneUpOpts.EnableAsyncIo {
106+
ro.SetAsyncIO(true)
107+
}
108+
if tuneUpOpts.EnableAutoReadaheadSize {
109+
ro.SetAutoReadaheadSize(true)
110+
}
57111
wo := grocksdb.NewDefaultWriteOptions()
58112
woSync := grocksdb.NewDefaultWriteOptions()
59113
woSync.SetSync(true)
60114
return dbm.NewRocksDBWithRawDB(db, ro, wo, woSync), nil
61115
}
62116

63117
// loadLatestOptions try to load options from existing db, returns nil if not exists.
64-
func loadLatestOptions(dir string) (*grocksdb.Options, error) {
65-
opts, err := grocksdb.LoadLatestOptions(dir, grocksdb.NewDefaultEnv(), true, grocksdb.NewLRUCache(BlockCacheSize))
118+
func loadLatestOptions(dir string, cache *grocksdb.Cache) (*grocksdb.Options, error) {
119+
env := grocksdb.NewDefaultEnv()
120+
defer env.Destroy()
121+
122+
opts, err := grocksdb.LoadLatestOptions(dir, env, true, cache)
66123
if err != nil {
67124
// not found is not an error
68125
if strings.HasPrefix(err.Error(), "NotFound: ") {
69126
return nil, nil
70127
}
71128
return nil, err
72129
}
130+
defer opts.Destroy()
73131

74132
cfNames := opts.ColumnFamilyNames()
75133
cfOpts := opts.ColumnFamilyOpts()
76134

77135
for i := 0; i < len(cfNames); i++ {
78136
if cfNames[i] == "default" {
79-
return &cfOpts[i], nil
137+
return cfOpts[i].Clone(), nil
80138
}
81139
}
82140

83-
return opts.Options(), nil
141+
return opts.Options().Clone(), nil
84142
}
85143

86144
// NewRocksdbOptions build options for `application.db`,
87145
// it overrides existing options if provided, otherwise create new one assuming it's a new database.
88-
func NewRocksdbOptions(opts *grocksdb.Options, sstFileWriter bool) *grocksdb.Options {
146+
func NewRocksdbOptions(opts *grocksdb.Options, sstFileWriter bool, tuneUpOpts RocksDBTuneUpOptions) *grocksdb.Options {
147+
return newRocksdbOptions(opts, sstFileWriter, tuneUpOpts, nil)
148+
}
149+
150+
func newRocksdbOptions(opts *grocksdb.Options, sstFileWriter bool, tuneUpOpts RocksDBTuneUpOptions, cache *grocksdb.Cache) *grocksdb.Options {
89151
if opts == nil {
90152
opts = grocksdb.NewDefaultOptions()
91153
// only enable dynamic-level-bytes on new db, don't override for existing db
@@ -95,11 +157,29 @@ func NewRocksdbOptions(opts *grocksdb.Options, sstFileWriter bool) *grocksdb.Opt
95157
opts.IncreaseParallelism(runtime.NumCPU())
96158
opts.OptimizeLevelStyleCompaction(512 * 1024 * 1024)
97159
opts.SetTargetFileSizeMultiplier(2)
160+
if tuneUpOpts.EnableOptimizeForPointLookup {
161+
opts.SetMemTablePrefixBloomSizeRatio(0.02)
162+
opts.SetMemtableWholeKeyFiltering(true)
163+
}
164+
if tuneUpOpts.EnableDirectIOForCompaction {
165+
opts.SetUseDirectIOForFlushAndCompaction(true)
166+
}
98167

99168
// block based table options
100169
bbto := grocksdb.NewDefaultBlockBasedTableOptions()
101-
102-
bbto.SetBlockCache(grocksdb.NewLRUCache(BlockCacheSize))
170+
defer bbto.Destroy()
171+
172+
if cache != nil {
173+
bbto.SetBlockCache(cache)
174+
} else if tuneUpOpts.EnableHyperClockCache {
175+
c := grocksdb.NewHyperClockCache(BlockCacheSize, 0)
176+
defer c.Destroy()
177+
bbto.SetBlockCache(c)
178+
} else {
179+
c := grocksdb.NewLRUCache(BlockCacheSize)
180+
defer c.Destroy()
181+
bbto.SetBlockCache(c)
182+
}
103183

104184
// http://rocksdb.org/blog/2021/12/29/ribbon-filter.html
105185
bbto.SetFilterPolicy(grocksdb.NewRibbonHybridFilterPolicy(9.9, 1))
@@ -117,6 +197,9 @@ func NewRocksdbOptions(opts *grocksdb.Options, sstFileWriter bool) *grocksdb.Opt
117197

118198
// hash index is better for iavl tree which mostly do point lookup.
119199
bbto.SetDataBlockIndexType(grocksdb.KDataBlockIndexTypeBinarySearchAndHash)
200+
if tuneUpOpts.EnableOptimizeForPointLookup {
201+
bbto.SetDataBlockHashRatio(0.75)
202+
}
120203

121204
opts.SetBlockBasedTableFactory(bbto)
122205

0 commit comments

Comments
 (0)