-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from sei-protocol/yzang/add-memiavl
[MemIAVL] Initial commit for memIAVL db and store
- Loading branch information
Showing
43 changed files
with
9,157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,12 @@ | |
|
||
# Go workspace file | ||
go.work | ||
.DS_Store | ||
*.swp | ||
*.swo | ||
*.swl | ||
*.swm | ||
*.swn | ||
*.pyc | ||
.dccache | ||
.idea |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
syntax = "proto3"; | ||
|
||
option go_package = "github.com/sei-protocol/sei-db/memiavl"; | ||
|
||
import "gogoproto/gogo.proto"; | ||
|
||
// CommitInfo defines commit information used by the multi-store when committing | ||
// a version/height. | ||
message CommitInfo { | ||
int64 version = 1; | ||
repeated StoreInfo store_infos = 2 [(gogoproto.nullable) = false]; | ||
} | ||
|
||
// StoreInfo defines store-specific commit information. It contains a reference | ||
// between a store name and the commit ID. | ||
message StoreInfo { | ||
string name = 1; | ||
CommitID commit_id = 2 [(gogoproto.nullable) = false]; | ||
} | ||
|
||
// CommitID defines the committment information when a specific store is | ||
// committed. | ||
message CommitID { | ||
option (gogoproto.goproto_stringer) = false; | ||
|
||
int64 version = 1; | ||
bytes hash = 2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
syntax = "proto3"; | ||
package memiavl; | ||
|
||
option go_package = "github.com/sei-protocol/sei-db/memiavl"; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "iavl/changeset.proto"; | ||
import "memiavl/commit_info.proto"; | ||
|
||
// NamedChangeSet combine a tree name with the changeset | ||
message NamedChangeSet { | ||
iavl.ChangeSet changeset = 1 [(gogoproto.nullable) = false]; | ||
string name = 2; | ||
} | ||
|
||
// TreeNameUpgrade defines upgrade of tree names: | ||
// - New tree: { name: "tree" } | ||
// - Delete tree: { name: "tree", delete: true } | ||
// - Rename tree: { name: "new-tree", rename_from: "old-tree" } | ||
message TreeNameUpgrade { | ||
string name = 1; | ||
string rename_from = 2; | ||
bool delete = 3; | ||
} | ||
|
||
// WALEntry is a single Write-Ahead-Log entry | ||
message WALEntry { | ||
repeated NamedChangeSet changesets = 1; | ||
repeated TreeNameUpgrade upgrades = 2; | ||
} | ||
|
||
// MultiTreeMetadata stores the metadata for MultiTree | ||
message MultiTreeMetadata { | ||
CommitInfo commit_info = 1; | ||
int64 initial_version = 2; | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package config | ||
|
||
const ( | ||
DefaultCacheSize = 100000 | ||
DefaultSnapshotInterval = 10000 | ||
) | ||
|
||
type MemIAVLConfig struct { | ||
// Enable defines if the memiavl should be enabled. | ||
Enable bool `mapstructure:"enable"` | ||
// ZeroCopy defines if the memiavl should return slices pointing to mmap-ed buffers directly (zero-copy), | ||
// the zero-copied slices must not be retained beyond current block's execution. | ||
// the sdk address cache will be disabled if zero-copy is enabled. | ||
ZeroCopy bool `mapstructure:"zero-copy"` | ||
// AsyncCommitBuffer defines the size of asynchronous commit queue, this greatly improve block catching-up | ||
// performance, -1 means synchronous commit. | ||
AsyncCommitBuffer int `mapstructure:"async-commit-buffer"` | ||
// SnapshotKeepRecent defines what many old snapshots (excluding the latest one) to keep after new snapshots are | ||
// taken, defaults to 1 to make sure ibc relayers work. | ||
SnapshotKeepRecent uint32 `mapstructure:"snapshot-keep-recent"` | ||
// SnapshotInterval defines the block interval the memiavl snapshot is taken, default to 1000. | ||
SnapshotInterval uint32 `mapstructure:"snapshot-interval"` | ||
// CacheSize defines the size of the cache for each memiavl store. | ||
CacheSize int `mapstructure:"cache-size"` | ||
} | ||
|
||
func DefaultMemIAVLConfig() MemIAVLConfig { | ||
return MemIAVLConfig{ | ||
CacheSize: DefaultCacheSize, | ||
SnapshotInterval: DefaultSnapshotInterval, | ||
SnapshotKeepRecent: 1, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package config | ||
|
||
// DefaultConfigTemplate defines the configuration template for the memiavl configuration | ||
const DefaultConfigTemplate = ` | ||
############################################################################### | ||
### MemIAVL Configuration ### | ||
############################################################################### | ||
[memiavl] | ||
# Enable defines if the memiavl should be enabled. | ||
enable = {{ .MemIAVL.Enable }} | ||
# ZeroCopy defines if the memiavl should return slices pointing to mmap-ed buffers directly (zero-copy), | ||
# the zero-copied slices must not be retained beyond current block's execution. | ||
# the sdk address cache will be disabled if zero-copy is enabled. | ||
zero-copy = {{ .MemIAVL.ZeroCopy }} | ||
# AsyncCommitBuffer defines the size of asynchronous commit queue, this greatly improve block catching-up | ||
# performance, -1 means synchronous commit. | ||
async-commit-buffer = {{ .MemIAVL.AsyncCommitBuffer }} | ||
# SnapshotKeepRecent defines what many old snapshots (excluding the latest one) to keep after new snapshots are | ||
# taken, defaults to 1 to make sure ibc relayers work. | ||
snapshot-keep-recent = {{ .MemIAVL.SnapshotKeepRecent }} | ||
# SnapshotInterval defines the block interval the memiavl snapshot is taken, default to 1000. | ||
snapshot-interval = {{ .MemIAVL.SnapshotInterval }} | ||
# CacheSize defines the size of the cache for each memiavl store, default to 1000. | ||
cache-size = {{ .MemIAVL.CacheSize }} | ||
` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
package memiavl | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"math/rand" | ||
"sort" | ||
"testing" | ||
|
||
iavlcache "github.com/cosmos/iavl/cache" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tidwall/btree" | ||
) | ||
|
||
func BenchmarkByteCompare(b *testing.B) { | ||
var x, y [32]byte | ||
for i := 0; i < b.N; i++ { | ||
_ = bytes.Compare(x[:], y[:]) | ||
} | ||
} | ||
|
||
func BenchmarkRandomGet(b *testing.B) { | ||
amount := 1000000 | ||
items := genRandItems(amount) | ||
targetKey := items[500].key | ||
targetValue := items[500].value | ||
targetItem := itemT{key: targetKey} | ||
|
||
tree := New(0) | ||
for _, item := range items { | ||
tree.set(item.key, item.value) | ||
} | ||
|
||
snapshotDir := b.TempDir() | ||
err := tree.WriteSnapshot(snapshotDir) | ||
require.NoError(b, err) | ||
snapshot, err := OpenSnapshot(snapshotDir) | ||
require.NoError(b, err) | ||
defer snapshot.Close() | ||
|
||
b.Run("memiavl", func(b *testing.B) { | ||
require.Equal(b, targetValue, tree.Get(targetKey)) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = tree.Get(targetKey) | ||
} | ||
}) | ||
b.Run("memiavl-disk", func(b *testing.B) { | ||
diskTree := NewFromSnapshot(snapshot, true, 0) | ||
require.Equal(b, targetValue, diskTree.Get(targetKey)) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = diskTree.Get(targetKey) | ||
} | ||
}) | ||
b.Run("memiavl-disk-cache-hit", func(b *testing.B) { | ||
diskTree := NewFromSnapshot(snapshot, true, 1) | ||
require.Equal(b, targetValue, diskTree.Get(targetKey)) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = diskTree.Get(targetKey) | ||
} | ||
}) | ||
b.Run("memiavl-disk-cache-miss", func(b *testing.B) { | ||
diskTree := NewFromSnapshot(snapshot, true, 0) | ||
// enforce an empty cache to emulate cache miss | ||
diskTree.cache = iavlcache.New(0) | ||
require.Equal(b, targetValue, diskTree.Get(targetKey)) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = diskTree.Get(targetKey) | ||
} | ||
}) | ||
b.Run("btree-degree-2", func(b *testing.B) { | ||
bt2 := btree.NewBTreeGOptions(lessG, btree.Options{ | ||
NoLocks: true, | ||
Degree: 2, | ||
}) | ||
for _, item := range items { | ||
bt2.Set(item) | ||
} | ||
v, _ := bt2.Get(targetItem) | ||
require.Equal(b, targetValue, v.value) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_, _ = bt2.Get(targetItem) | ||
} | ||
}) | ||
b.Run("btree-degree-32", func(b *testing.B) { | ||
bt32 := btree.NewBTreeGOptions(lessG, btree.Options{ | ||
NoLocks: true, | ||
Degree: 32, | ||
}) | ||
for _, item := range items { | ||
bt32.Set(item) | ||
} | ||
v, _ := bt32.Get(targetItem) | ||
require.Equal(b, targetValue, v.value) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_, _ = bt32.Get(targetItem) | ||
} | ||
}) | ||
b.Run("iavl-lru", func(b *testing.B) { | ||
cache := iavlcache.New(amount) | ||
for _, item := range items { | ||
cache.Add(NewIavlCacheNode(item.key, item.value)) | ||
} | ||
v := cache.Get(targetItem.key).(iavlCacheNode).value | ||
require.Equal(b, targetValue, v) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = cache.Get(targetKey).(iavlCacheNode).value | ||
} | ||
}) | ||
b.Run("go-map", func(b *testing.B) { | ||
m := make(map[string][]byte, amount) | ||
for _, item := range items { | ||
m[string(item.key)] = item.value | ||
} | ||
v := m[string(targetItem.key)] | ||
require.Equal(b, targetValue, v) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = m[string(targetKey)] | ||
} | ||
}) | ||
|
||
b.Run("binary-search", func(b *testing.B) { | ||
// the last benchmark sort the items in place | ||
sort.Slice(items, func(i, j int) bool { | ||
return bytes.Compare(items[i].key, items[j].key) < 0 | ||
}) | ||
cmp := func(i int) bool { return bytes.Compare(items[i].key, targetKey) != -1 } | ||
i := sort.Search(len(items), cmp) | ||
require.Equal(b, targetValue, items[i].value) | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
n := sort.Search(len(items), cmp) | ||
_ = items[n].value | ||
} | ||
}) | ||
} | ||
|
||
func BenchmarkRandomSet(b *testing.B) { | ||
items := genRandItems(1000000) | ||
b.ResetTimer() | ||
b.Run("memiavl", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
tree := New(0) | ||
for _, item := range items { | ||
tree.set(item.key, item.value) | ||
} | ||
} | ||
}) | ||
b.Run("tree2", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
bt := btree.NewBTreeGOptions(lessG, btree.Options{ | ||
NoLocks: true, | ||
Degree: 2, | ||
}) | ||
for _, item := range items { | ||
bt.Set(item) | ||
} | ||
} | ||
}) | ||
b.Run("tree32", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
bt := btree.NewBTreeGOptions(lessG, btree.Options{ | ||
NoLocks: true, | ||
Degree: 32, | ||
}) | ||
for _, item := range items { | ||
bt.Set(item) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
type itemT struct { | ||
key, value []byte | ||
} | ||
|
||
func lessG(a, b itemT) bool { | ||
return bytes.Compare(a.key, b.key) == -1 | ||
} | ||
|
||
func int64ToItemT(n uint64) itemT { | ||
var key, value [8]byte | ||
binary.BigEndian.PutUint64(key[:], n) | ||
binary.LittleEndian.PutUint64(value[:], n) | ||
return itemT{ | ||
key: key[:], | ||
value: value[:], | ||
} | ||
} | ||
|
||
func genRandItems(n int) []itemT { | ||
r := rand.New(rand.NewSource(0)) | ||
items := make([]itemT, n) | ||
itemsM := make(map[uint64]bool) | ||
for i := 0; i < n; i++ { | ||
for { | ||
key := uint64(r.Int63n(10000000000000000)) | ||
if !itemsM[key] { | ||
itemsM[key] = true | ||
items[i] = int64ToItemT(key) | ||
break | ||
} | ||
} | ||
} | ||
return items | ||
} | ||
|
||
type iavlCacheNode struct { | ||
key []byte | ||
value []byte | ||
} | ||
|
||
func NewIavlCacheNode(key, value []byte) iavlCacheNode { | ||
return iavlCacheNode{key, value} | ||
} | ||
|
||
func (n iavlCacheNode) GetKey() []byte { | ||
return n.key | ||
} | ||
|
||
func (n iavlCacheNode) GetCacheKey() []byte { | ||
return n.key | ||
} |
Oops, something went wrong.