Skip to content

Commit

Permalink
etcdserver: Fix invalid count returned on Range with Limit
Browse files Browse the repository at this point in the history
  • Loading branch information
serathius committed May 31, 2021
1 parent c7cbc6b commit a8a738a
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 36 deletions.
23 changes: 10 additions & 13 deletions server/mvcc/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
type index interface {
Get(key []byte, atRev int64) (rev, created revision, ver int64, err error)
Range(key, end []byte, atRev int64) ([][]byte, []revision)
Revisions(key, end []byte, atRev int64, limit int) []revision
CountRevisions(key, end []byte, atRev int64, limit int) int
Revisions(key, end []byte, atRev int64, limit int) ([]revision, int)
CountRevisions(key, end []byte, atRev int64) int
Put(key []byte, rev revision)
Tombstone(key []byte, rev revision) error
RangeSince(key, end []byte, rev int64) []revision
Expand Down Expand Up @@ -106,27 +106,27 @@ func (ti *treeIndex) visit(key, end []byte, f func(ki *keyIndex) bool) {
})
}

func (ti *treeIndex) Revisions(key, end []byte, atRev int64, limit int) (revs []revision) {
func (ti *treeIndex) Revisions(key, end []byte, atRev int64, limit int) (revs []revision, total int) {
if end == nil {
rev, _, _, err := ti.Get(key, atRev)
if err != nil {
return nil
return nil, 0
}
return []revision{rev}
return []revision{rev}, 1
}
ti.visit(key, end, func(ki *keyIndex) bool {
if rev, _, _, err := ki.get(ti.lg, atRev); err == nil {
revs = append(revs, rev)
if len(revs) == limit {
return false
if limit <= 0 || len(revs) < limit {
revs = append(revs, rev)
}
total++
}
return true
})
return revs
return revs, total
}

func (ti *treeIndex) CountRevisions(key, end []byte, atRev int64, limit int) int {
func (ti *treeIndex) CountRevisions(key, end []byte, atRev int64) int {
if end == nil {
_, _, _, err := ti.Get(key, atRev)
if err != nil {
Expand All @@ -138,9 +138,6 @@ func (ti *treeIndex) CountRevisions(key, end []byte, atRev int64, limit int) int
ti.visit(key, end, func(ki *keyIndex) bool {
if _, _, _, err := ki.get(ti.lg, atRev); err == nil {
total++
if total == limit {
return false
}
}
return true
})
Expand Down
19 changes: 10 additions & 9 deletions server/mvcc/kv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,18 @@ func testKVRangeLimit(t *testing.T, f rangeFunc) {

wrev := int64(4)
tests := []struct {
limit int64
wkvs []mvccpb.KeyValue
limit int64
wcounts int64
wkvs []mvccpb.KeyValue
}{
// no limit
{-1, kvs},
{-1, 3, kvs},
// no limit
{0, kvs},
{1, kvs[:1]},
{2, kvs[:2]},
{3, kvs},
{100, kvs},
{0, 3, kvs},
{1, 3, kvs[:1]},
{2, 3, kvs[:2]},
{3, 3, kvs},
{100, 3, kvs},
}
for i, tt := range tests {
r, err := f(s, []byte("foo"), []byte("foo3"), RangeOptions{Limit: tt.limit})
Expand All @@ -248,7 +249,7 @@ func testKVRangeLimit(t *testing.T, f rangeFunc) {
if r.Count != len(kvs) {
t.Errorf("#%d: count = %d, want %d", i, r.Count, len(kvs))
}
} else if r.Count != int(tt.limit) {
} else if r.Count != int(tt.wcounts) {
t.Errorf("#%d: count = %d, want %d", i, r.Count, tt.limit)
}
}
Expand Down
9 changes: 6 additions & 3 deletions server/mvcc/kvstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,12 +937,15 @@ type fakeIndex struct {
indexCompactRespc chan map[revision]struct{}
}

func (i *fakeIndex) Revisions(key, end []byte, atRev int64, limit int) []revision {
func (i *fakeIndex) Revisions(key, end []byte, atRev int64, limit int) ([]revision, int) {
_, rev := i.Range(key, end, atRev)
return rev
if len(rev) >= limit {
rev = rev[:limit]
}
return rev, len(rev)
}

func (i *fakeIndex) CountRevisions(key, end []byte, atRev int64, limit int) int {
func (i *fakeIndex) CountRevisions(key, end []byte, atRev int64) int {
_, rev := i.Range(key, end, atRev)
return len(rev)
}
Expand Down
8 changes: 4 additions & 4 deletions server/mvcc/kvstore_txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ func (tr *storeTxnRead) rangeKeys(ctx context.Context, key, end []byte, curRev i
return &RangeResult{KVs: nil, Count: -1, Rev: 0}, ErrCompacted
}
if ro.Count {
total := tr.s.kvindex.CountRevisions(key, end, rev, int(ro.Limit))
total := tr.s.kvindex.CountRevisions(key, end, rev)
tr.trace.Step("count revisions from in-memory index tree")
return &RangeResult{KVs: nil, Count: total, Rev: curRev}, nil
}
revpairs := tr.s.kvindex.Revisions(key, end, rev, int(ro.Limit))
revpairs, total := tr.s.kvindex.Revisions(key, end, rev, int(ro.Limit))
tr.trace.Step("range keys from in-memory index tree")
if len(revpairs) == 0 {
return &RangeResult{KVs: nil, Count: 0, Rev: curRev}, nil
return &RangeResult{KVs: nil, Count: total, Rev: curRev}, nil
}

limit := int(ro.Limit)
Expand Down Expand Up @@ -176,7 +176,7 @@ func (tr *storeTxnRead) rangeKeys(ctx context.Context, key, end []byte, curRev i
}
}
tr.trace.Step("range keys from bolt db")
return &RangeResult{KVs: kvs, Count: len(revpairs), Rev: curRev}, nil
return &RangeResult{KVs: kvs, Count: total, Rev: curRev}, nil
}

func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) {
Expand Down
31 changes: 24 additions & 7 deletions tests/integration/v3_grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1289,8 +1289,9 @@ func TestV3RangeRequest(t *testing.T) {
putKeys []string
reqs []pb.RangeRequest

wresps [][]string
wmores []bool
wresps [][]string
wmores []bool
wcounts []int64
}{
// single key
{
Expand All @@ -1307,6 +1308,7 @@ func TestV3RangeRequest(t *testing.T) {
{},
},
[]bool{false, false},
[]int64{1, 0},
},
// multi-key
{
Expand Down Expand Up @@ -1335,6 +1337,7 @@ func TestV3RangeRequest(t *testing.T) {
{"a", "b", "c", "d", "e"},
},
[]bool{false, false, false, false, false, false},
[]int64{5, 2, 0, 0, 0, 5},
},
// revision
{
Expand All @@ -1353,22 +1356,30 @@ func TestV3RangeRequest(t *testing.T) {
{"a", "b"},
},
[]bool{false, false, false, false},
[]int64{5, 0, 1, 2},
},
// limit
{
[]string{"foo", "bar"},
[]string{"a", "b", "c"},
[]pb.RangeRequest{
// more
{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 1},
// no more
// half
{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 2},
// no more
{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 3},
// limit over
{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 4},
},

[][]string{
{"bar"},
{"bar", "foo"},
{"a"},
{"a", "b"},
{"a", "b", "c"},
{"a", "b", "c"},
},
[]bool{true, false},
[]bool{true, true, false},
[]int64{3, 3, 3, 3},
},
// sort
{
Expand Down Expand Up @@ -1421,6 +1432,7 @@ func TestV3RangeRequest(t *testing.T) {
{"b", "a", "c", "d"},
},
[]bool{true, true, true, true, false, false},
[]int64{4, 4, 4, 4, 0, 4},
},
// min/max mod rev
{
Expand Down Expand Up @@ -1452,6 +1464,7 @@ func TestV3RangeRequest(t *testing.T) {
{"rev2", "rev3", "rev4", "rev5", "rev6"},
},
[]bool{false, false, false, false},
[]int64{5, 5, 5, 5},
},
// min/max create rev
{
Expand Down Expand Up @@ -1483,6 +1496,7 @@ func TestV3RangeRequest(t *testing.T) {
{"rev2", "rev3", "rev6"},
},
[]bool{false, false, false, false},
[]int64{3, 3, 3, 3},
},
}

Expand Down Expand Up @@ -1516,6 +1530,9 @@ func TestV3RangeRequest(t *testing.T) {
if resp.More != tt.wmores[j] {
t.Errorf("#%d.%d: bad more. got = %v, want = %v, ", i, j, resp.More, tt.wmores[j])
}
if resp.GetCount() != tt.wcounts[j] {
t.Errorf("#%d.%d: bad count. got = %v, want = %v, ", i, j, resp.GetCount(), tt.wcounts[j])
}
wrev := int64(len(tt.putKeys) + 1)
if resp.Header.Revision != wrev {
t.Errorf("#%d.%d: bad header revision. got = %d. want = %d", i, j, resp.Header.Revision, wrev)
Expand Down

0 comments on commit a8a738a

Please sign in to comment.