*: keep tombstone if revision == compactAtRev

Before this patch, the tombstone can be deleted if its revision is equal
compacted revision. It causes that the watch subscriber won't get this
DELETE event. Based on Compact API[1], we should keep tombstone revision
if it's not less than the compaction revision.

> CompactionRequest compacts the key-value store up to a given revision.
> All superseded keys with a revision less than the compaction revision
> will be removed.

[1]: https://etcd.io/docs/latest/dev-guide/api_reference_v3/

Signed-off-by: Wei Fu <fuweid89@gmail.com>
This commit is contained in:
Wei Fu
2024-07-04 14:35:51 +08:00
parent ee33652775
commit bbdc94181a
4 changed files with 501 additions and 99 deletions

View File

@ -18,6 +18,7 @@ import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
)
@ -308,12 +309,15 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
key: []byte("foo"),
modified: Revision{Main: 16},
generations: []generation{
{created: Revision{Main: 2}, ver: 3, revs: []Revision{Revision{Main: 6}}},
{created: Revision{Main: 8}, ver: 3, revs: []Revision{Revision{Main: 8}, Revision{Main: 10}, Revision{Main: 12}}},
{created: Revision{Main: 14}, ver: 3, revs: []Revision{Revision{Main: 14}, Revision{Main: 15, Sub: 1}, Revision{Main: 16}}},
{},
},
},
map[Revision]struct{}{},
map[Revision]struct{}{
Revision{Main: 6}: {},
},
},
{
7,
@ -394,11 +398,14 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
key: []byte("foo"),
modified: Revision{Main: 16},
generations: []generation{
{created: Revision{Main: 8}, ver: 3, revs: []Revision{Revision{Main: 12}}},
{created: Revision{Main: 14}, ver: 3, revs: []Revision{Revision{Main: 14}, Revision{Main: 15, Sub: 1}, Revision{Main: 16}}},
{},
},
},
map[Revision]struct{}{},
map[Revision]struct{}{
Revision{Main: 12}: {},
},
},
{
13,
@ -442,6 +449,20 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
},
{
16,
&keyIndex{
key: []byte("foo"),
modified: Revision{Main: 16},
generations: []generation{
{created: Revision{Main: 14}, ver: 3, revs: []Revision{Revision{Main: 16}}},
{},
},
},
map[Revision]struct{}{
Revision{Main: 16}: {},
},
},
{
17,
&keyIndex{
key: []byte("foo"),
modified: Revision{Main: 16},
@ -453,18 +474,36 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
},
}
isTombstoneRevFn := func(ki *keyIndex, rev int64) bool {
for i := 0; i < len(ki.generations)-1; i++ {
g := ki.generations[i]
if l := len(g.revs); l > 0 && g.revs[l-1].Main == rev {
return true
}
}
return false
}
// Continuous Compaction and finding Keep
ki := newTestKeyIndex(zaptest.NewLogger(t))
for i, tt := range tests {
isTombstone := isTombstoneRevFn(ki, tt.compact)
am := make(map[Revision]struct{})
kiclone := cloneKeyIndex(ki)
ki.keep(tt.compact, am)
if !reflect.DeepEqual(ki, kiclone) {
t.Errorf("#%d: ki = %+v, want %+v", i, ki, kiclone)
}
if !reflect.DeepEqual(am, tt.wam) {
t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam)
if isTombstone {
assert.Equal(t, 0, len(am), "#%d: ki = %d, keep result wants empty because tombstone", i, ki)
} else {
assert.Equal(t, tt.wam, am,
"#%d: ki = %d, compact keep should be equal to keep keep if it's not tombstone", i, ki)
}
am = make(map[Revision]struct{})
ki.compact(zaptest.NewLogger(t), tt.compact, am)
if !reflect.DeepEqual(ki, tt.wki) {
@ -478,7 +517,7 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
// Jump Compaction and finding Keep
ki = newTestKeyIndex(zaptest.NewLogger(t))
for i, tt := range tests {
if (i%2 == 0 && i < 6) || (i%2 == 1 && i > 6) {
if !isTombstoneRevFn(ki, tt.compact) {
am := make(map[Revision]struct{})
kiclone := cloneKeyIndex(ki)
ki.keep(tt.compact, am)
@ -508,9 +547,14 @@ func TestKeyIndexCompactAndKeep(t *testing.T) {
if !reflect.DeepEqual(ki, kiClone) {
t.Errorf("#%d: ki = %+v, want %+v", i, ki, kiClone)
}
if !reflect.DeepEqual(am, tt.wam) {
t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam)
if isTombstoneRevFn(ki, tt.compact) {
assert.Equal(t, 0, len(am), "#%d: ki = %d, keep result wants empty because tombstone", i, ki)
} else {
assert.Equal(t, tt.wam, am,
"#%d: ki = %d, compact keep should be equal to keep keep if it's not tombstone", i, ki)
}
am = make(map[Revision]struct{})
ki.compact(zaptest.NewLogger(t), tt.compact, am)
if !reflect.DeepEqual(ki, tt.wki) {