2019-02-01 17:03:09 -08:00
|
|
|
package cachekv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"sort"
|
|
|
|
"sync"
|
2020-07-17 08:34:37 -07:00
|
|
|
"time"
|
2019-02-01 17:03:09 -08:00
|
|
|
|
2019-08-02 06:20:39 -07:00
|
|
|
dbm "github.com/tendermint/tm-db"
|
2019-02-01 17:03:09 -08:00
|
|
|
|
2021-03-01 07:10:22 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/internal/conv"
|
2021-03-30 13:13:51 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/listenkv"
|
2019-02-01 17:03:09 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/tracekv"
|
2020-01-16 13:46:51 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/types"
|
2020-06-18 11:12:44 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/telemetry"
|
2020-07-30 07:53:02 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/types/kv"
|
2019-02-01 17:03:09 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
// If value is nil but deleted is false, it means the parent doesn't have the
|
|
|
|
// key. (No need to delete upon Write())
|
|
|
|
type cValue struct {
|
|
|
|
value []byte
|
|
|
|
deleted bool
|
|
|
|
dirty bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store wraps an in-memory cache around an underlying types.KVStore.
|
|
|
|
type Store struct {
|
2019-05-15 07:42:06 -07:00
|
|
|
mtx sync.Mutex
|
|
|
|
cache map[string]*cValue
|
|
|
|
unsortedCache map[string]struct{}
|
store/cachekv: use typed types/kv.List instead of container/list.List (#8811)
Reduces CPU burn by using a typed List to avoid the expensive type
assertions from using an interface. This is the only option for now
until Go makes generics generally available.
The change brings time spent on the time assertion cummulatively to:
580ms down from 6.88s
Explanation:
The type assertions were showing up heavily in profiles:
* Before this commit
```shell
Total: 42.18s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
14.01s 18.87s (flat, cum) 44.74% of Total
. . 17: items []*kv.Pair
. . 18: ascending bool
. . 19:}
. . 20:
. . 21:func newMemIterator(start, end []byte, items *list.List, ascending bool) *memIterator {
. 620ms 22: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 23:
. . 24: var entered bool
. . 25:
510ms 870ms 26: for e := items.Front(); e != nil; e = e.Next() {
6.85s 6.88s 27: item := e.Value.(*kv.Pair)
5.71s 8.19s 28: if !dbm.IsKeyInDomain(item.Key, start, end) {
120ms 120ms 29: if entered {
. . 30: break
. . 31: }
. . 32:
. . 33: continue
. . 34: }
. . 35:
820ms 980ms 36: itemsInDomain = append(itemsInDomain, item)
. . 37: entered = true
. . 38: }
. . 39:
. 1.21s 40: return &memIterator{
. . 41: start: start,
. . 42: end: end,
. . 43: items: itemsInDomain,
. . 44: ascending: ascending,
. . 45: }
```
and given that the list only uses that type, it is only right to lift the
code from container/list.List, and only modify Element.Value's type.
For emphasis, the code is basically just a retrofit of
container/list/list.go but with a typing, and we'll keep it as is
until perhaps Go1.17 or Go1.18 or when everyone uses Go1.17+ after
generics have landed.
* After this commit
```shell
Total: 45.25s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
4.84s 6.77s (flat, cum) 14.96% of Total
. . 16: items []*kv.Pair
. . 17: ascending bool
. . 18:}
. . 19:
. . 20:func newMemIterator(start, end []byte, items *kv.List, ascending bool) *memIterator {
. 330ms 21: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 22:
. . 23: var entered bool
. . 24:
60ms 160ms 25: for e := items.Front(); e != nil; e = e.Next() {
580ms 580ms 26: item := e.Value
3.68s 4.78s 27: if !dbm.IsKeyInDomain(item.Key, start, end) {
80ms 80ms 28: if entered {
. . 29: break
. . 30: }
. . 31:
. . 32: continue
. . 33: }
. . 34:
440ms 580ms 35: itemsInDomain = append(itemsInDomain, item)
. . 36: entered = true
. . 37: }
. . 38:
. 260ms 39: return &memIterator{
. . 40: start: start,
. . 41: end: end,
. . 42: items: itemsInDomain,
. . 43: ascending: ascending,
. . 44: }
```
Fixes #8810
2021-03-08 09:16:23 -08:00
|
|
|
sortedCache *kv.List // always ascending sorted
|
2019-05-15 07:42:06 -07:00
|
|
|
parent types.KVStore
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.CacheKVStore = (*Store)(nil)
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// NewStore creates a new Store object
|
2019-02-01 17:03:09 -08:00
|
|
|
func NewStore(parent types.KVStore) *Store {
|
|
|
|
return &Store{
|
2019-05-15 07:42:06 -07:00
|
|
|
cache: make(map[string]*cValue),
|
|
|
|
unsortedCache: make(map[string]struct{}),
|
store/cachekv: use typed types/kv.List instead of container/list.List (#8811)
Reduces CPU burn by using a typed List to avoid the expensive type
assertions from using an interface. This is the only option for now
until Go makes generics generally available.
The change brings time spent on the time assertion cummulatively to:
580ms down from 6.88s
Explanation:
The type assertions were showing up heavily in profiles:
* Before this commit
```shell
Total: 42.18s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
14.01s 18.87s (flat, cum) 44.74% of Total
. . 17: items []*kv.Pair
. . 18: ascending bool
. . 19:}
. . 20:
. . 21:func newMemIterator(start, end []byte, items *list.List, ascending bool) *memIterator {
. 620ms 22: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 23:
. . 24: var entered bool
. . 25:
510ms 870ms 26: for e := items.Front(); e != nil; e = e.Next() {
6.85s 6.88s 27: item := e.Value.(*kv.Pair)
5.71s 8.19s 28: if !dbm.IsKeyInDomain(item.Key, start, end) {
120ms 120ms 29: if entered {
. . 30: break
. . 31: }
. . 32:
. . 33: continue
. . 34: }
. . 35:
820ms 980ms 36: itemsInDomain = append(itemsInDomain, item)
. . 37: entered = true
. . 38: }
. . 39:
. 1.21s 40: return &memIterator{
. . 41: start: start,
. . 42: end: end,
. . 43: items: itemsInDomain,
. . 44: ascending: ascending,
. . 45: }
```
and given that the list only uses that type, it is only right to lift the
code from container/list.List, and only modify Element.Value's type.
For emphasis, the code is basically just a retrofit of
container/list/list.go but with a typing, and we'll keep it as is
until perhaps Go1.17 or Go1.18 or when everyone uses Go1.17+ after
generics have landed.
* After this commit
```shell
Total: 45.25s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
4.84s 6.77s (flat, cum) 14.96% of Total
. . 16: items []*kv.Pair
. . 17: ascending bool
. . 18:}
. . 19:
. . 20:func newMemIterator(start, end []byte, items *kv.List, ascending bool) *memIterator {
. 330ms 21: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 22:
. . 23: var entered bool
. . 24:
60ms 160ms 25: for e := items.Front(); e != nil; e = e.Next() {
580ms 580ms 26: item := e.Value
3.68s 4.78s 27: if !dbm.IsKeyInDomain(item.Key, start, end) {
80ms 80ms 28: if entered {
. . 29: break
. . 30: }
. . 31:
. . 32: continue
. . 33: }
. . 34:
440ms 580ms 35: itemsInDomain = append(itemsInDomain, item)
. . 36: entered = true
. . 37: }
. . 38:
. 260ms 39: return &memIterator{
. . 40: start: start,
. . 41: end: end,
. . 42: items: itemsInDomain,
. . 43: ascending: ascending,
. . 44: }
```
Fixes #8810
2021-03-08 09:16:23 -08:00
|
|
|
sortedCache: kv.NewList(),
|
2019-05-15 07:42:06 -07:00
|
|
|
parent: parent,
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// GetStoreType implements Store.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) GetStoreType() types.StoreType {
|
|
|
|
return store.parent.GetStoreType()
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// Get implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) Get(key []byte) (value []byte) {
|
|
|
|
store.mtx.Lock()
|
|
|
|
defer store.mtx.Unlock()
|
2020-07-17 12:33:50 -07:00
|
|
|
defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "get")
|
2019-09-04 10:33:32 -07:00
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
types.AssertValidKey(key)
|
|
|
|
|
2021-05-06 06:33:01 -07:00
|
|
|
cacheValue, ok := store.cache[conv.UnsafeBytesToStr(key)]
|
2019-02-01 17:03:09 -08:00
|
|
|
if !ok {
|
|
|
|
value = store.parent.Get(key)
|
|
|
|
store.setCacheValue(key, value, false, false)
|
|
|
|
} else {
|
|
|
|
value = cacheValue.value
|
|
|
|
}
|
|
|
|
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// Set implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) Set(key []byte, value []byte) {
|
|
|
|
store.mtx.Lock()
|
|
|
|
defer store.mtx.Unlock()
|
2020-07-17 12:33:50 -07:00
|
|
|
defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "set")
|
2019-09-04 10:33:32 -07:00
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
types.AssertValidKey(key)
|
|
|
|
types.AssertValidValue(value)
|
|
|
|
|
|
|
|
store.setCacheValue(key, value, false, true)
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// Has implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) Has(key []byte) bool {
|
|
|
|
value := store.Get(key)
|
|
|
|
return value != nil
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// Delete implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) Delete(key []byte) {
|
|
|
|
store.mtx.Lock()
|
|
|
|
defer store.mtx.Unlock()
|
2020-07-17 12:33:50 -07:00
|
|
|
defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "delete")
|
2019-09-04 10:33:32 -07:00
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
types.AssertValidKey(key)
|
|
|
|
store.setCacheValue(key, nil, true, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Cachetypes.KVStore.
|
|
|
|
func (store *Store) Write() {
|
|
|
|
store.mtx.Lock()
|
|
|
|
defer store.mtx.Unlock()
|
2020-07-17 12:33:50 -07:00
|
|
|
defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "write")
|
2019-02-01 17:03:09 -08:00
|
|
|
|
|
|
|
// We need a copy of all of the keys.
|
|
|
|
// Not the best, but probably not a bottleneck depending.
|
|
|
|
keys := make([]string, 0, len(store.cache))
|
2020-04-29 19:36:34 -07:00
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
for key, dbValue := range store.cache {
|
|
|
|
if dbValue.dirty {
|
|
|
|
keys = append(keys, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
// TODO: Consider allowing usage of Batch, which would allow the write to
|
|
|
|
// at least happen atomically.
|
|
|
|
for _, key := range keys {
|
|
|
|
cacheValue := store.cache[key]
|
2020-04-29 19:36:34 -07:00
|
|
|
|
2019-08-19 09:06:27 -07:00
|
|
|
switch {
|
|
|
|
case cacheValue.deleted:
|
2019-02-01 17:03:09 -08:00
|
|
|
store.parent.Delete([]byte(key))
|
2019-08-19 09:06:27 -07:00
|
|
|
case cacheValue.value == nil:
|
2019-02-01 17:03:09 -08:00
|
|
|
// Skip, it already doesn't exist in parent.
|
2019-08-19 09:06:27 -07:00
|
|
|
default:
|
2019-02-01 17:03:09 -08:00
|
|
|
store.parent.Set([]byte(key), cacheValue.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the cache
|
2019-05-15 07:42:06 -07:00
|
|
|
store.cache = make(map[string]*cValue)
|
|
|
|
store.unsortedCache = make(map[string]struct{})
|
store/cachekv: use typed types/kv.List instead of container/list.List (#8811)
Reduces CPU burn by using a typed List to avoid the expensive type
assertions from using an interface. This is the only option for now
until Go makes generics generally available.
The change brings time spent on the time assertion cummulatively to:
580ms down from 6.88s
Explanation:
The type assertions were showing up heavily in profiles:
* Before this commit
```shell
Total: 42.18s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
14.01s 18.87s (flat, cum) 44.74% of Total
. . 17: items []*kv.Pair
. . 18: ascending bool
. . 19:}
. . 20:
. . 21:func newMemIterator(start, end []byte, items *list.List, ascending bool) *memIterator {
. 620ms 22: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 23:
. . 24: var entered bool
. . 25:
510ms 870ms 26: for e := items.Front(); e != nil; e = e.Next() {
6.85s 6.88s 27: item := e.Value.(*kv.Pair)
5.71s 8.19s 28: if !dbm.IsKeyInDomain(item.Key, start, end) {
120ms 120ms 29: if entered {
. . 30: break
. . 31: }
. . 32:
. . 33: continue
. . 34: }
. . 35:
820ms 980ms 36: itemsInDomain = append(itemsInDomain, item)
. . 37: entered = true
. . 38: }
. . 39:
. 1.21s 40: return &memIterator{
. . 41: start: start,
. . 42: end: end,
. . 43: items: itemsInDomain,
. . 44: ascending: ascending,
. . 45: }
```
and given that the list only uses that type, it is only right to lift the
code from container/list.List, and only modify Element.Value's type.
For emphasis, the code is basically just a retrofit of
container/list/list.go but with a typing, and we'll keep it as is
until perhaps Go1.17 or Go1.18 or when everyone uses Go1.17+ after
generics have landed.
* After this commit
```shell
Total: 45.25s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
4.84s 6.77s (flat, cum) 14.96% of Total
. . 16: items []*kv.Pair
. . 17: ascending bool
. . 18:}
. . 19:
. . 20:func newMemIterator(start, end []byte, items *kv.List, ascending bool) *memIterator {
. 330ms 21: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 22:
. . 23: var entered bool
. . 24:
60ms 160ms 25: for e := items.Front(); e != nil; e = e.Next() {
580ms 580ms 26: item := e.Value
3.68s 4.78s 27: if !dbm.IsKeyInDomain(item.Key, start, end) {
80ms 80ms 28: if entered {
. . 29: break
. . 30: }
. . 31:
. . 32: continue
. . 33: }
. . 34:
440ms 580ms 35: itemsInDomain = append(itemsInDomain, item)
. . 36: entered = true
. . 37: }
. . 38:
. 260ms 39: return &memIterator{
. . 40: start: start,
. . 41: end: end,
. . 42: items: itemsInDomain,
. . 43: ascending: ascending,
. . 44: }
```
Fixes #8810
2021-03-08 09:16:23 -08:00
|
|
|
store.sortedCache = kv.NewList()
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// CacheWrap implements CacheWrapper.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) CacheWrap() types.CacheWrap {
|
|
|
|
return NewStore(store)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CacheWrapWithTrace implements the CacheWrapper interface.
|
|
|
|
func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap {
|
|
|
|
return NewStore(tracekv.NewStore(store, w, tc))
|
|
|
|
}
|
|
|
|
|
2021-03-30 13:13:51 -07:00
|
|
|
// CacheWrapWithListeners implements the CacheWrapper interface.
|
|
|
|
func (store *Store) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap {
|
|
|
|
return NewStore(listenkv.NewStore(store, storeKey, listeners))
|
|
|
|
}
|
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
//----------------------------------------
|
|
|
|
// Iteration
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// Iterator implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) Iterator(start, end []byte) types.Iterator {
|
|
|
|
return store.iterator(start, end, true)
|
|
|
|
}
|
|
|
|
|
2021-01-05 07:57:33 -08:00
|
|
|
// ReverseIterator implements types.KVStore.
|
2019-02-01 17:03:09 -08:00
|
|
|
func (store *Store) ReverseIterator(start, end []byte) types.Iterator {
|
|
|
|
return store.iterator(start, end, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator {
|
2019-05-15 07:42:06 -07:00
|
|
|
store.mtx.Lock()
|
|
|
|
defer store.mtx.Unlock()
|
|
|
|
|
2019-02-01 17:03:09 -08:00
|
|
|
var parent, cache types.Iterator
|
|
|
|
|
|
|
|
if ascending {
|
|
|
|
parent = store.parent.Iterator(start, end)
|
|
|
|
} else {
|
|
|
|
parent = store.parent.ReverseIterator(start, end)
|
|
|
|
}
|
|
|
|
|
2019-05-15 07:42:06 -07:00
|
|
|
store.dirtyItems(start, end)
|
|
|
|
cache = newMemIterator(start, end, store.sortedCache, ascending)
|
2019-02-01 17:03:09 -08:00
|
|
|
|
|
|
|
return newCacheMergeIterator(parent, cache, ascending)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructs a slice of dirty items, to use w/ memIterator.
|
2019-05-15 07:42:06 -07:00
|
|
|
func (store *Store) dirtyItems(start, end []byte) {
|
2020-07-30 07:53:02 -07:00
|
|
|
unsorted := make([]*kv.Pair, 0)
|
2019-02-01 17:03:09 -08:00
|
|
|
|
2021-02-27 07:26:22 -08:00
|
|
|
n := len(store.unsortedCache)
|
2019-05-15 07:42:06 -07:00
|
|
|
for key := range store.unsortedCache {
|
2021-03-01 07:10:22 -08:00
|
|
|
if dbm.IsKeyInDomain(conv.UnsafeStrToBytes(key), start, end) {
|
2021-02-27 07:26:22 -08:00
|
|
|
cacheValue := store.cache[key]
|
2020-07-30 07:53:02 -07:00
|
|
|
unsorted = append(unsorted, &kv.Pair{Key: []byte(key), Value: cacheValue.value})
|
2021-02-27 07:26:22 -08:00
|
|
|
}
|
|
|
|
}
|
2020-04-29 19:36:34 -07:00
|
|
|
|
2021-02-27 07:26:22 -08:00
|
|
|
if len(unsorted) == n { // This pattern allows the Go compiler to emit the map clearing idiom for the entire map.
|
|
|
|
for key := range store.unsortedCache {
|
2019-05-15 07:42:06 -07:00
|
|
|
delete(store.unsortedCache, key)
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
2021-02-27 07:26:22 -08:00
|
|
|
} else { // Otherwise, normally delete the unsorted keys from the map.
|
|
|
|
for _, kv := range unsorted {
|
2021-03-01 07:10:22 -08:00
|
|
|
delete(store.unsortedCache, conv.UnsafeBytesToStr(kv.Key))
|
2021-02-27 07:26:22 -08:00
|
|
|
}
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
|
|
|
|
2019-05-15 07:42:06 -07:00
|
|
|
sort.Slice(unsorted, func(i, j int) bool {
|
|
|
|
return bytes.Compare(unsorted[i].Key, unsorted[j].Key) < 0
|
2019-02-01 17:03:09 -08:00
|
|
|
})
|
|
|
|
|
2019-05-15 07:42:06 -07:00
|
|
|
for e := store.sortedCache.Front(); e != nil && len(unsorted) != 0; {
|
|
|
|
uitem := unsorted[0]
|
store/cachekv: use typed types/kv.List instead of container/list.List (#8811)
Reduces CPU burn by using a typed List to avoid the expensive type
assertions from using an interface. This is the only option for now
until Go makes generics generally available.
The change brings time spent on the time assertion cummulatively to:
580ms down from 6.88s
Explanation:
The type assertions were showing up heavily in profiles:
* Before this commit
```shell
Total: 42.18s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
14.01s 18.87s (flat, cum) 44.74% of Total
. . 17: items []*kv.Pair
. . 18: ascending bool
. . 19:}
. . 20:
. . 21:func newMemIterator(start, end []byte, items *list.List, ascending bool) *memIterator {
. 620ms 22: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 23:
. . 24: var entered bool
. . 25:
510ms 870ms 26: for e := items.Front(); e != nil; e = e.Next() {
6.85s 6.88s 27: item := e.Value.(*kv.Pair)
5.71s 8.19s 28: if !dbm.IsKeyInDomain(item.Key, start, end) {
120ms 120ms 29: if entered {
. . 30: break
. . 31: }
. . 32:
. . 33: continue
. . 34: }
. . 35:
820ms 980ms 36: itemsInDomain = append(itemsInDomain, item)
. . 37: entered = true
. . 38: }
. . 39:
. 1.21s 40: return &memIterator{
. . 41: start: start,
. . 42: end: end,
. . 43: items: itemsInDomain,
. . 44: ascending: ascending,
. . 45: }
```
and given that the list only uses that type, it is only right to lift the
code from container/list.List, and only modify Element.Value's type.
For emphasis, the code is basically just a retrofit of
container/list/list.go but with a typing, and we'll keep it as is
until perhaps Go1.17 or Go1.18 or when everyone uses Go1.17+ after
generics have landed.
* After this commit
```shell
Total: 45.25s
ROUTINE ======================== github.com/cosmos/cosmos-sdk/store/cachekv.newMemIterator
in /Users/emmanuelodeke/go/src/github.com/cosmos/cosmos-sdk/store/cachekv/memiterator.go
4.84s 6.77s (flat, cum) 14.96% of Total
. . 16: items []*kv.Pair
. . 17: ascending bool
. . 18:}
. . 19:
. . 20:func newMemIterator(start, end []byte, items *kv.List, ascending bool) *memIterator {
. 330ms 21: itemsInDomain := make([]*kv.Pair, 0, items.Len())
. . 22:
. . 23: var entered bool
. . 24:
60ms 160ms 25: for e := items.Front(); e != nil; e = e.Next() {
580ms 580ms 26: item := e.Value
3.68s 4.78s 27: if !dbm.IsKeyInDomain(item.Key, start, end) {
80ms 80ms 28: if entered {
. . 29: break
. . 30: }
. . 31:
. . 32: continue
. . 33: }
. . 34:
440ms 580ms 35: itemsInDomain = append(itemsInDomain, item)
. . 36: entered = true
. . 37: }
. . 38:
. 260ms 39: return &memIterator{
. . 40: start: start,
. . 41: end: end,
. . 42: items: itemsInDomain,
. . 43: ascending: ascending,
. . 44: }
```
Fixes #8810
2021-03-08 09:16:23 -08:00
|
|
|
sitem := e.Value
|
2019-05-15 07:42:06 -07:00
|
|
|
comp := bytes.Compare(uitem.Key, sitem.Key)
|
2020-04-29 19:36:34 -07:00
|
|
|
|
2019-05-15 07:42:06 -07:00
|
|
|
switch comp {
|
|
|
|
case -1:
|
|
|
|
unsorted = unsorted[1:]
|
2020-04-29 19:36:34 -07:00
|
|
|
|
2019-05-15 07:42:06 -07:00
|
|
|
store.sortedCache.InsertBefore(uitem, e)
|
|
|
|
case 1:
|
|
|
|
e = e.Next()
|
|
|
|
case 0:
|
|
|
|
unsorted = unsorted[1:]
|
|
|
|
e.Value = uitem
|
|
|
|
e = e.Next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, kvp := range unsorted {
|
|
|
|
store.sortedCache.PushBack(kvp)
|
|
|
|
}
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
// etc
|
|
|
|
|
|
|
|
// Only entrypoint to mutate store.cache.
|
|
|
|
func (store *Store) setCacheValue(key, value []byte, deleted bool, dirty bool) {
|
2021-05-06 06:33:01 -07:00
|
|
|
store.cache[conv.UnsafeBytesToStr(key)] = &cValue{
|
2019-02-01 17:03:09 -08:00
|
|
|
value: value,
|
|
|
|
deleted: deleted,
|
|
|
|
dirty: dirty,
|
|
|
|
}
|
2019-05-15 07:42:06 -07:00
|
|
|
if dirty {
|
2021-05-06 06:33:01 -07:00
|
|
|
store.unsortedCache[conv.UnsafeBytesToStr(key)] = struct{}{}
|
2019-05-15 07:42:06 -07:00
|
|
|
}
|
2019-02-01 17:03:09 -08:00
|
|
|
}
|