Skip to content

Commit 6644e35

Browse files
mmsqevladjdkaljo242
authored
feat: support build tag with rocksdb (#200)
* feat: support build tag with rocksdb to avoid undefined: config.OpenDB when build with rocksdb * Apply suggestions from code review --------- Co-authored-by: Vlad J <vladjdk@gmail.com> Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
1 parent bb811f4 commit 6644e35

File tree

3 files changed

+169
-14
lines changed

3 files changed

+169
-14
lines changed

server/config/opendb.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//go:build !rocksdb
2+
// +build !rocksdb
3+
4+
package config
5+
6+
import (
7+
"path/filepath"
8+
9+
dbm "github.com/cosmos/cosmos-db"
10+
11+
"github.com/cosmos/cosmos-sdk/server/types"
12+
)
13+
14+
// OpenDB opens a database based on the specified backend type.
15+
// It takes the home directory where the database data will be stored, along with the backend type.
16+
// It opens a database named "application" using the specified backend type and the data directory.
17+
// It returns the opened database and an error (if any). If the database opens successfully, the error will be nil.
18+
//
19+
// NOTE: this is included in builds without rocksdb.
20+
// When building the binary with rocksdb, the code in 'rocksdb.go' will be included
21+
// instead of this
22+
func OpenDB(_ types.AppOptions, home string, backendType dbm.BackendType) (dbm.DB, error) {
23+
dataDir := filepath.Join(home, "data")
24+
return dbm.NewDB("application", backendType, dataDir)
25+
}
26+
27+
// OpenReadOnlyDB opens rocksdb backend in read-only mode.
28+
func OpenReadOnlyDB(home string, backendType dbm.BackendType) (dbm.DB, error) {
29+
return OpenDB(nil, home, backendType)
30+
}

server/config/opendb_rocksdb.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//go:build rocksdb
2+
// +build rocksdb
3+
4+
package config
5+
6+
import (
7+
"path/filepath"
8+
"runtime"
9+
"strings"
10+
11+
dbm "github.com/cosmos/cosmos-db"
12+
"github.com/cosmos/cosmos-sdk/server/types"
13+
"github.com/linxGnu/grocksdb"
14+
)
15+
16+
// 3G block cache
17+
const BlockCacheSize = 3 << 30
18+
19+
func OpenDB(_ types.AppOptions, home string, backendType dbm.BackendType) (dbm.DB, error) {
20+
dataDir := filepath.Join(home, "data")
21+
if backendType == dbm.RocksDBBackend {
22+
return openRocksdb(filepath.Join(dataDir, "application.db"), false)
23+
}
24+
25+
return dbm.NewDB("application", backendType, dataDir)
26+
}
27+
28+
// OpenReadOnlyDB opens rocksdb backend in read-only mode.
29+
func OpenReadOnlyDB(home string, backendType dbm.BackendType) (dbm.DB, error) {
30+
dataDir := filepath.Join(home, "data")
31+
if backendType == dbm.RocksDBBackend {
32+
return openRocksdb(filepath.Join(dataDir, "application.db"), true)
33+
}
34+
35+
return dbm.NewDB("application", backendType, dataDir)
36+
}
37+
38+
func openRocksdb(dir string, readonly bool) (dbm.DB, error) {
39+
opts, err := loadLatestOptions(dir)
40+
if err != nil {
41+
return nil, err
42+
}
43+
// customize rocksdb options
44+
opts = NewRocksdbOptions(opts, false)
45+
46+
var db *grocksdb.DB
47+
if readonly {
48+
db, err = grocksdb.OpenDbForReadOnly(opts, dir, false)
49+
} else {
50+
db, err = grocksdb.OpenDb(opts, dir)
51+
}
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
ro := grocksdb.NewDefaultReadOptions()
57+
wo := grocksdb.NewDefaultWriteOptions()
58+
woSync := grocksdb.NewDefaultWriteOptions()
59+
woSync.SetSync(true)
60+
return dbm.NewRocksDBWithRawDB(db, ro, wo, woSync), nil
61+
}
62+
63+
// 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))
66+
if err != nil {
67+
// not found is not an error
68+
if strings.HasPrefix(err.Error(), "NotFound: ") {
69+
return nil, nil
70+
}
71+
return nil, err
72+
}
73+
74+
cfNames := opts.ColumnFamilyNames()
75+
cfOpts := opts.ColumnFamilyOpts()
76+
77+
for i := 0; i < len(cfNames); i++ {
78+
if cfNames[i] == "default" {
79+
return &cfOpts[i], nil
80+
}
81+
}
82+
83+
return opts.Options(), nil
84+
}
85+
86+
// NewRocksdbOptions build options for `application.db`,
87+
// 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 {
89+
if opts == nil {
90+
opts = grocksdb.NewDefaultOptions()
91+
// only enable dynamic-level-bytes on new db, don't override for existing db
92+
opts.SetLevelCompactionDynamicLevelBytes(true)
93+
}
94+
opts.SetCreateIfMissing(true)
95+
opts.IncreaseParallelism(runtime.NumCPU())
96+
opts.OptimizeLevelStyleCompaction(512 * 1024 * 1024)
97+
opts.SetTargetFileSizeMultiplier(2)
98+
99+
// block based table options
100+
bbto := grocksdb.NewDefaultBlockBasedTableOptions()
101+
102+
bbto.SetBlockCache(grocksdb.NewLRUCache(BlockCacheSize))
103+
104+
// http://rocksdb.org/blog/2021/12/29/ribbon-filter.html
105+
bbto.SetFilterPolicy(grocksdb.NewRibbonHybridFilterPolicy(9.9, 1))
106+
107+
// partition index
108+
// http://rocksdb.org/blog/2017/05/12/partitioned-index-filter.html
109+
bbto.SetIndexType(grocksdb.KTwoLevelIndexSearchIndexType)
110+
bbto.SetPartitionFilters(true)
111+
bbto.SetOptimizeFiltersForMemory(true)
112+
113+
// reduce memory usage
114+
bbto.SetCacheIndexAndFilterBlocks(true)
115+
bbto.SetPinTopLevelIndexAndFilter(true)
116+
bbto.SetPinL0FilterAndIndexBlocksInCache(true)
117+
118+
// hash index is better for iavl tree which mostly do point lookup.
119+
bbto.SetDataBlockIndexType(grocksdb.KDataBlockIndexTypeBinarySearchAndHash)
120+
121+
opts.SetBlockBasedTableFactory(bbto)
122+
123+
// in iavl tree, we almost always query existing keys
124+
opts.SetOptimizeFiltersForHits(true)
125+
126+
// heavier compression option at bottommost level,
127+
// 110k dict bytes is default in zstd library,
128+
// train bytes is recommended to be set at 100x dict bytes.
129+
opts.SetBottommostCompression(grocksdb.ZSTDCompression)
130+
compressOpts := grocksdb.NewDefaultCompressionOptions()
131+
compressOpts.Level = 12
132+
if !sstFileWriter {
133+
compressOpts.MaxDictBytes = 110 * 1024
134+
opts.SetBottommostCompressionOptionsZstdMaxTrainBytes(compressOpts.MaxDictBytes*100, true)
135+
}
136+
opts.SetBottommostCompressionOptions(compressOpts, true)
137+
return opts
138+
}

server/start.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func NewDefaultStartOptions(appCreator types.AppCreator, defaultNodeHome string)
6767
return StartOptions{
6868
AppCreator: appCreator,
6969
DefaultNodeHome: defaultNodeHome,
70-
DBOpener: openDB,
70+
DBOpener: cosmosevmserverconfig.OpenDB,
7171
}
7272
}
7373

@@ -673,16 +673,3 @@ func GenDocProvider(cfg *cmtcfg.Config) func() (*cmttypes.GenesisDoc, error) {
673673
return appGenesis.ToGenesisDoc()
674674
}
675675
}
676-
677-
// openDB opens a database based on the specified backend type.
678-
// It takes the home directory where the database data will be stored, along with the backend type.
679-
// It opens a database named "application" using the specified backend type and the data directory.
680-
// It returns the opened database and an error (if any). If the database opens successfully, the error will be nil.
681-
//
682-
// NOTE: this is included in builds without rocksdb.
683-
// When building the binary with rocksdb, the code in 'rocksdb.go' will be included
684-
// instead of this
685-
func openDB(_ types.AppOptions, home string, backendType dbm.BackendType) (dbm.DB, error) {
686-
dataDir := filepath.Join(home, "data")
687-
return dbm.NewDB("application", backendType, dataDir)
688-
}

0 commit comments

Comments
 (0)