Skip to content

Commit

Permalink
fix(store): handle nil end in cachekv/iterator (#12945)
Browse files Browse the repository at this point in the history
## Description

Closes: #12661 

Adds support for nil end semantics to iterators in cachekv store, addressing [this workaround](https://github.com/osmosis-labs/osmosis/blob/4176b287d48338870bfda3029bfa20a6e45ac126/osmoutils/store_helper.go#L37-L41).  

Note that this has the effect of sorting the dirty cache into the BTree cache store in the bounds `[startIndex, end-of-cache-space]`



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
kocubinski committed Aug 17, 2022
1 parent b515372 commit 0ed7360
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/group) [#12888](https://github.com/cosmos/cosmos-sdk/pull/12888) Fix event propagation to the current context of `x/group` message execution `[]sdk.Result`.
* (sdk/dec_coins) [#12903](https://github.com/cosmos/cosmos-sdk/pull/12903) Fix nil `DecCoin` creation when converting `Coins` to `DecCoins`
* (x/upgrade) [#12906](https://github.com/cosmos/cosmos-sdk/pull/12906) Fix upgrade failure by moving downgrade verification logic after store migration.
* (store) [#12945](https://github.com/cosmos/cosmos-sdk/pull/12945) Fix nil end semantics in store/cachekv/iterator when iterating a dirty cache.

### Deprecated

Expand Down
16 changes: 11 additions & 5 deletions store/cachekv/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ const (
// Constructs a slice of dirty items, to use w/ memIterator.
func (store *Store) dirtyItems(start, end []byte) {
startStr, endStr := conv.UnsafeBytesToStr(start), conv.UnsafeBytesToStr(end)
if startStr > endStr {
if end != nil && startStr > endStr {
// Nothing to do here.
return
}
Expand All @@ -296,6 +296,7 @@ func (store *Store) dirtyItems(start, end []byte) {
// than just not having the cache.
if n < 1024 {
for key := range store.unsortedCache {
// dbm.IsKeyInDomain is nil safe and returns true iff key is greater than start
if dbm.IsKeyInDomain(conv.UnsafeStrToBytes(key), start, end) {
cacheValue := store.cache[key]
unsorted = append(unsorted, &kv.Pair{Key: []byte(key), Value: cacheValue.value})
Expand All @@ -316,13 +317,18 @@ func (store *Store) dirtyItems(start, end []byte) {
// Now find the values within the domain
// [start, end)
startIndex := findStartIndex(strL, startStr)
endIndex := findEndIndex(strL, endStr)
if startIndex < 0 {
startIndex = 0
}

if endIndex < 0 {
var endIndex int
if end == nil {
endIndex = len(strL) - 1
} else {
endIndex = findEndIndex(strL, endStr)
}
if startIndex < 0 {
startIndex = 0
if endIndex < 0 {
endIndex = len(strL) - 1
}

kvL := make([]*kv.Pair, 0)
Expand Down
44 changes: 44 additions & 0 deletions store/cachekv/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,50 @@ func TestCacheKVMergeIteratorRandom(t *testing.T) {
}
}

func TestNilEndIterator(t *testing.T) {
const SIZE = 3000

tests := []struct {
name string
write bool
startIndex int
end []byte
}{
{name: "write=false, end=nil", write: false, end: nil, startIndex: 1000},
{name: "write=false, end=nil; full key scan", write: false, end: nil, startIndex: 2000},
{name: "write=true, end=nil", write: true, end: nil, startIndex: 1000},
{name: "write=false, end=non-nil", write: false, end: keyFmt(3000), startIndex: 1000},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st := newCacheKVStore()

for i := 0; i < SIZE; i++ {
kstr := keyFmt(i)
st.Set(kstr, valFmt(i))
}

if tt.write {
st.Write()
}

itr := st.Iterator(keyFmt(tt.startIndex), tt.end)
i := tt.startIndex
j := 0
for itr.Valid() {
require.Equal(t, keyFmt(i), itr.Key())
require.Equal(t, valFmt(i), itr.Value())
itr.Next()
i++
j++
}

require.Equal(t, SIZE-tt.startIndex, j)
})
}
}

//-------------------------------------------------------------------------------------------
// do some random ops

Expand Down

0 comments on commit 0ed7360

Please sign in to comment.