2018-03-17 11:53:21 +00:00
|
|
|
package storage
|
|
|
|
|
2018-03-21 16:11:04 +00:00
|
|
|
import (
|
2021-09-24 13:00:13 +00:00
|
|
|
"bytes"
|
|
|
|
"sort"
|
2019-09-18 15:20:41 +00:00
|
|
|
"strings"
|
2019-08-27 13:32:52 +00:00
|
|
|
"sync"
|
2018-03-21 16:11:04 +00:00
|
|
|
)
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// MemoryStore is an in-memory implementation of a Store, mainly
|
|
|
|
// used for testing. Do not use MemoryStore in production.
|
|
|
|
type MemoryStore struct {
|
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
|
|
|
mut sync.RWMutex
|
|
|
|
mem map[string][]byte
|
|
|
|
stor map[string][]byte
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMemoryStore creates a new MemoryStore object.
|
|
|
|
func NewMemoryStore() *MemoryStore {
|
|
|
|
return &MemoryStore{
|
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
|
|
|
mem: make(map[string][]byte),
|
|
|
|
stor: make(map[string][]byte),
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get implements the Store interface.
|
|
|
|
func (s *MemoryStore) Get(key []byte) ([]byte, error) {
|
2019-09-26 17:49:02 +00:00
|
|
|
s.mut.RLock()
|
|
|
|
defer s.mut.RUnlock()
|
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
|
|
|
m := s.chooseMap(key)
|
|
|
|
if val, ok := m[string(key)]; ok && val != nil {
|
2018-03-17 11:53:21 +00:00
|
|
|
return val, nil
|
|
|
|
}
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
|
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 *MemoryStore) chooseMap(key []byte) map[string][]byte {
|
|
|
|
switch KeyPrefix(key[0]) {
|
|
|
|
case STStorage, STTempStorage:
|
|
|
|
return s.stor
|
|
|
|
default:
|
|
|
|
return s.mem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-07 17:13:59 +00:00
|
|
|
// put puts a key-value pair into the store, it's supposed to be called
|
|
|
|
// with mutex locked.
|
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 put(m map[string][]byte, key string, value []byte) {
|
|
|
|
m[key] = value
|
2019-10-07 17:13:59 +00:00
|
|
|
}
|
|
|
|
|
2021-08-12 10:35:09 +00:00
|
|
|
// PutChangeSet implements the Store interface. Never returns an error.
|
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 *MemoryStore) PutChangeSet(puts map[string][]byte, stores map[string][]byte) error {
|
2019-10-07 17:13:59 +00:00
|
|
|
s.mut.Lock()
|
2022-02-16 16:13:06 +00:00
|
|
|
s.putChangeSet(puts, stores)
|
|
|
|
s.mut.Unlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MemoryStore) putChangeSet(puts map[string][]byte, stores map[string][]byte) {
|
2021-08-12 10:35:09 +00:00
|
|
|
for k := range puts {
|
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
|
|
|
put(s.mem, k, puts[k])
|
|
|
|
}
|
|
|
|
for k := range stores {
|
|
|
|
put(s.stor, k, stores[k])
|
2019-10-07 14:08:09 +00:00
|
|
|
}
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
2019-02-09 15:53:58 +00:00
|
|
|
// Seek implements the Store interface.
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *MemoryStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
2020-02-24 14:51:50 +00:00
|
|
|
s.mut.RLock()
|
2021-12-16 13:55:50 +00:00
|
|
|
s.seek(rng, f)
|
2020-02-24 14:51:50 +00:00
|
|
|
s.mut.RUnlock()
|
|
|
|
}
|
|
|
|
|
2022-02-11 17:35:45 +00:00
|
|
|
// SeekGC implements the Store interface.
|
|
|
|
func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
|
|
|
|
s.mut.Lock()
|
|
|
|
// We still need to perform normal seek, some GC operations can be
|
|
|
|
// sensitive to the order of KV pairs.
|
|
|
|
s.seek(rng, func(k, v []byte) bool {
|
|
|
|
if !keep(k, v) {
|
2022-02-16 13:48:47 +00:00
|
|
|
delete(s.chooseMap(k), string(k))
|
2022-02-11 17:35:45 +00:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
s.mut.Unlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-16 13:55:50 +00:00
|
|
|
// seek is an internal unlocked implementation of Seek. `start` denotes whether
|
2021-12-28 13:01:44 +00:00
|
|
|
// seeking starting from the provided prefix should be performed. Backwards
|
|
|
|
// seeking from some point is supported with corresponding SeekRange field set.
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool) {
|
2021-12-16 13:55:50 +00:00
|
|
|
sPrefix := string(rng.Prefix)
|
|
|
|
lPrefix := len(sPrefix)
|
|
|
|
sStart := string(rng.Start)
|
|
|
|
lStart := len(sStart)
|
2021-09-24 13:00:13 +00:00
|
|
|
var memList []KeyValue
|
2021-12-16 13:55:50 +00:00
|
|
|
|
|
|
|
isKeyOK := func(key string) bool {
|
|
|
|
return strings.HasPrefix(key, sPrefix) && (lStart == 0 || strings.Compare(key[lPrefix:], sStart) >= 0)
|
|
|
|
}
|
2021-12-28 13:01:44 +00:00
|
|
|
if rng.Backwards {
|
|
|
|
isKeyOK = func(key string) bool {
|
|
|
|
return strings.HasPrefix(key, sPrefix) && (lStart == 0 || strings.Compare(key[lPrefix:], sStart) <= 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
less := func(k1, k2 []byte) bool {
|
|
|
|
res := bytes.Compare(k1, k2)
|
|
|
|
return res != 0 && rng.Backwards == (res > 0)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
m := s.chooseMap(rng.Prefix)
|
|
|
|
for k, v := range m {
|
2022-01-29 08:54:25 +00:00
|
|
|
if v != nil && isKeyOK(k) {
|
2021-09-24 13:00:13 +00:00
|
|
|
memList = append(memList, KeyValue{
|
|
|
|
Key: []byte(k),
|
|
|
|
Value: v,
|
|
|
|
})
|
2019-09-18 15:20:41 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-24 13:00:13 +00:00
|
|
|
sort.Slice(memList, func(i, j int) bool {
|
2021-12-28 13:01:44 +00:00
|
|
|
return less(memList[i].Key, memList[j].Key)
|
2021-09-24 13:00:13 +00:00
|
|
|
})
|
|
|
|
for _, kv := range memList {
|
2022-01-17 17:41:51 +00:00
|
|
|
if !f(kv.Key, kv.Value) {
|
|
|
|
break
|
|
|
|
}
|
2021-09-24 13:00:13 +00:00
|
|
|
}
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-26 15:35:36 +00:00
|
|
|
// Close implements Store interface and clears up memory. Never returns an
|
|
|
|
// error.
|
2019-09-16 15:52:47 +00:00
|
|
|
func (s *MemoryStore) Close() error {
|
2019-09-26 17:49:02 +00:00
|
|
|
s.mut.Lock()
|
2019-09-16 15:52:47 +00:00
|
|
|
s.mem = nil
|
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
|
|
|
s.stor = nil
|
2019-09-26 17:49:02 +00:00
|
|
|
s.mut.Unlock()
|
2019-09-16 15:52:47 +00:00
|
|
|
return nil
|
|
|
|
}
|