33package opendb
44
55import (
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
1720const 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
2965func 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