Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Return exact prefix match from memory iterator #48

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 9 additions & 20 deletions memory/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ type iterator struct {
// The key at which this iterator ends, inclusive.
end []byte

// If this is true, `start` is inclusive, else `start` is exclusive.
isStartInclusive bool
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fredcarle Sorry as I pushed back on doing this in an earlier PR as I thought this would be more involved than this and require cleanup in badger, however the badger code that I thought was trying to do this was trying to do something else instead and this change was fairly easy.


// If true, the iterator will iterate in reverse order, from the largest
// key to the smallest.
reverse bool
Expand All @@ -43,23 +40,20 @@ func newPrefixIter(db *Datastore, prefix []byte, reverse bool, version uint64) *
it: db.values.Iter(),
start: prefix,
end: bytesPrefixEnd(prefix),
// A prefix iterator must not return a key exactly matching itself.
isStartInclusive: false,
reverse: reverse,
reset: true,
reverse: reverse,
reset: true,
}
}

func newRangeIter(db *Datastore, start, end []byte, reverse bool, version uint64) *iterator {
return &iterator{
db: db,
version: version,
it: db.values.Iter(),
start: start,
end: end,
isStartInclusive: true,
reverse: reverse,
reset: true,
db: db,
version: version,
it: db.values.Iter(),
start: start,
end: end,
reverse: reverse,
reset: true,
}
}

Expand Down Expand Up @@ -109,11 +103,6 @@ func (iter *iterator) valid() bool {
return false
}

if !iter.isStartInclusive && (!iter.reverse && bytes.Equal(iter.it.Item().key, iter.start) ||
iter.reverse && bytes.Equal(iter.it.Item().key, iter.end)) {
return false
}

if len(iter.end) > 0 && !lt(iter.it.Item().key, iter.end) {
return false
}
Expand Down
14 changes: 0 additions & 14 deletions namespace/namespace.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package namespace

import (
"bytes"
"context"

"github.com/sourcenetwork/corekv"
Expand Down Expand Up @@ -110,19 +109,6 @@ func (nIter *namespaceIterator) Reset() {
nIter.it.Reset()
}

func (nIter *namespaceIterator) Valid() bool {
// make sure our keys contain the namespace BUT NOT exactly matching
key := nIter.it.Key()
if bytes.Equal(key, nIter.namespace) {
return false
}
if len(key) >= len(nIter.namespace) && !bytes.Equal(key[:len(nIter.namespace)], nIter.namespace) {
return false
}

return true
}

func (nIter *namespaceIterator) Next() (bool, error) {
return nIter.it.Next()
}
Expand Down
24 changes: 6 additions & 18 deletions test/integration/iterator/prefix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/sourcenetwork/corekv"
"github.com/sourcenetwork/corekv/test/action"
"github.com/sourcenetwork/corekv/test/integration"
"github.com/sourcenetwork/corekv/test/state"
)

func TestIteratorPrefix(t *testing.T) {
Expand All @@ -33,11 +32,8 @@ func TestIteratorPrefix(t *testing.T) {
test.Execute(t)
}

func TestIteratorPrefix_DoesNotReturnSelf_Memory(t *testing.T) {
func TestIteratorPrefix_DoesNotReturnSelf(t *testing.T) {
test := &integration.Test{
SupportedStoreTypes: []state.StoreType{
state.MemoryStoreType,
},
Actions: []action.Action{
action.Set([]byte("k"), []byte("v")),
action.Set([]byte("k1"), []byte("v1")),
Expand All @@ -46,7 +42,7 @@ func TestIteratorPrefix_DoesNotReturnSelf_Memory(t *testing.T) {
Prefix: []byte("k"),
},
Expected: []action.KeyValue{
// `k` must not be yielded, as prefxes do not contain themselves
{Key: []byte("k"), Value: []byte("v")},
{Key: []byte("k1"), Value: []byte("v1")},
},
},
Expand All @@ -56,23 +52,15 @@ func TestIteratorPrefix_DoesNotReturnSelf_Memory(t *testing.T) {
test.Execute(t)
}

// This test documents unwanted behaviour, it is tracked by:
// https://github.com/sourcenetwork/corekv/issues/27
func TestIteratorPrefix_DoesNotReturnSelf_Badger(t *testing.T) {
func TestIteratorPrefix_DoesNotReturnSelf_NamespaceMatch(t *testing.T) {
test := &integration.Test{
SupportedStoreTypes: []state.StoreType{
state.BadgerStoreType,
},
Actions: []action.Action{
action.Set([]byte("k"), []byte("v")),
action.Set([]byte("namespace"), []byte("namespace exact match")),
action.Namespace([]byte("namespace")),
action.Set([]byte("k1"), []byte("v1")),
&action.Iterate{
IterOptions: corekv.IterOptions{
Prefix: []byte("k"),
},
Expected: []action.KeyValue{
// `k` should not be yielded, but it is.
{Key: []byte("k"), Value: []byte("v")},
{Key: []byte(""), Value: []byte("namespace exact match")},
{Key: []byte("k1"), Value: []byte("v1")},
},
},
Expand Down