2018-03-17 11:53:21 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2020-08-30 08:42:16 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
2022-02-11 17:35:45 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
2018-03-17 11:53:21 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
|
|
)
|
|
|
|
|
2019-09-10 14:22:21 +00:00
|
|
|
// LevelDBOptions configuration for LevelDB.
|
|
|
|
type LevelDBOptions struct {
|
|
|
|
DataDirectoryPath string `yaml:"DataDirectoryPath"`
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// LevelDBStore is the official storage implementation for storing and retrieving
|
2018-03-17 11:53:21 +00:00
|
|
|
// blockchain data.
|
|
|
|
type LevelDBStore struct {
|
|
|
|
db *leveldb.DB
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// NewLevelDBStore returns a new LevelDBStore object that will
|
2018-03-17 11:53:21 +00:00
|
|
|
// initialize the database found at the given path.
|
2019-09-16 15:52:47 +00:00
|
|
|
func NewLevelDBStore(cfg LevelDBOptions) (*LevelDBStore, error) {
|
2020-08-30 08:42:16 +00:00
|
|
|
var opts = new(opt.Options) // should be exposed via LevelDBOptions if anything needed
|
2019-09-10 14:22:21 +00:00
|
|
|
|
2020-08-30 08:42:16 +00:00
|
|
|
opts.Filter = filter.NewBloomFilter(10)
|
2019-09-10 14:22:21 +00:00
|
|
|
db, err := leveldb.OpenFile(cfg.DataDirectoryPath, opts)
|
2018-03-17 11:53:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-02-19 11:48:48 +00:00
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
return &LevelDBStore{
|
2019-09-10 14:22:21 +00:00
|
|
|
path: cfg.DataDirectoryPath,
|
2018-03-17 11:53:21 +00:00
|
|
|
db: db,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Get(key []byte) ([]byte, error) {
|
2019-09-27 12:40:44 +00:00
|
|
|
value, err := s.db.Get(key, nil)
|
|
|
|
if err == leveldb.ErrNotFound {
|
|
|
|
err = ErrKeyNotFound
|
|
|
|
}
|
|
|
|
return value, err
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-12 10:35:09 +00:00
|
|
|
// PutChangeSet implements the Store interface.
|
storage: use two maps for MemoryStore
Simple and dumb as it is, this allows to separate contract storage from other
things and dramatically improve Seek() time over storage (even though it's
still unordered!) which in turn improves block processing speed.
LevelDB LevelDB (KeepOnlyLatest) BoltDB BoltDB (KeepOnlyLatest)
Master real 16m27,936s real 10m9,440s real 16m39,369s real 8m1,227s
user 20m12,619s user 26m13,925s user 18m9,162s user 18m5,846s
sys 2m56,377s sys 1m32,051s sys 9m52,576s sys 2m9,455s
2 maps real 10m49,495s real 8m53,342s real 11m46,204s real 5m56,043s
user 14m19,922s user 24m6,225s user 13m25,691s user 15m4,694s
sys 1m53,021s sys 1m23,006s sys 4m31,735s sys 2m8,714s
neo-bench performance is mostly unaffected, ~0.5% for 1-1 test and 4% for
10K-10K test both fall within regular test error range.
2022-02-15 16:07:59 +00:00
|
|
|
func (s *LevelDBStore) PutChangeSet(puts map[string][]byte, stores map[string][]byte) error {
|
2021-08-12 10:35:09 +00:00
|
|
|
tx, err := s.db.OpenTransaction()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
storage: use two maps for MemoryStore
Simple and dumb as it is, this allows to separate contract storage from other
things and dramatically improve Seek() time over storage (even though it's
still unordered!) which in turn improves block processing speed.
LevelDB LevelDB (KeepOnlyLatest) BoltDB BoltDB (KeepOnlyLatest)
Master real 16m27,936s real 10m9,440s real 16m39,369s real 8m1,227s
user 20m12,619s user 26m13,925s user 18m9,162s user 18m5,846s
sys 2m56,377s sys 1m32,051s sys 9m52,576s sys 2m9,455s
2 maps real 10m49,495s real 8m53,342s real 11m46,204s real 5m56,043s
user 14m19,922s user 24m6,225s user 13m25,691s user 15m4,694s
sys 1m53,021s sys 1m23,006s sys 4m31,735s sys 2m8,714s
neo-bench performance is mostly unaffected, ~0.5% for 1-1 test and 4% for
10K-10K test both fall within regular test error range.
2022-02-15 16:07:59 +00:00
|
|
|
for _, m := range []map[string][]byte{puts, stores} {
|
|
|
|
for k := range m {
|
|
|
|
if m[k] != nil {
|
|
|
|
err = tx.Put([]byte(k), m[k], nil)
|
|
|
|
} else {
|
|
|
|
err = tx.Delete([]byte(k), nil)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
tx.Discard()
|
|
|
|
return err
|
|
|
|
}
|
2021-08-12 10:35:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return tx.Commit()
|
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// Seek implements the Store interface.
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
2022-02-11 17:35:45 +00:00
|
|
|
iter := s.db.NewIterator(seekRangeToPrefixes(rng), nil)
|
|
|
|
s.seek(iter, rng.Backwards, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SeekGC implements the Store interface.
|
|
|
|
func (s *LevelDBStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
|
|
|
tx, err := s.db.OpenTransaction()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
iter := tx.NewIterator(seekRangeToPrefixes(rng), nil)
|
|
|
|
s.seek(iter, rng.Backwards, func(k, v []byte) bool {
|
|
|
|
if !keep(k, v) {
|
|
|
|
err = tx.Delete(k, nil)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return tx.Commit()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LevelDBStore) seek(iter iterator.Iterator, backwards bool, f func(k, v []byte) bool) {
|
2022-02-11 15:00:45 +00:00
|
|
|
var (
|
|
|
|
next func() bool
|
|
|
|
ok bool
|
|
|
|
)
|
|
|
|
|
2022-02-11 17:35:45 +00:00
|
|
|
if !backwards {
|
2022-02-11 15:00:45 +00:00
|
|
|
ok = iter.Next()
|
|
|
|
next = iter.Next
|
2021-12-28 13:01:44 +00:00
|
|
|
} else {
|
2022-02-11 15:00:45 +00:00
|
|
|
ok = iter.Last()
|
|
|
|
next = iter.Prev
|
2021-12-28 13:01:44 +00:00
|
|
|
}
|
|
|
|
|
2022-02-11 15:00:45 +00:00
|
|
|
for ; ok; ok = next() {
|
2022-01-17 17:41:51 +00:00
|
|
|
if !f(iter.Key(), iter.Value()) {
|
|
|
|
break
|
|
|
|
}
|
2021-12-28 13:01:44 +00:00
|
|
|
}
|
|
|
|
iter.Release()
|
|
|
|
}
|
|
|
|
|
2019-09-16 15:52:47 +00:00
|
|
|
// Close implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Close() error {
|
|
|
|
return s.db.Close()
|
|
|
|
}
|