|
|
@@ -0,0 +1,364 @@
|
|
|
+package storage
|
|
|
+
|
|
|
+import (
|
|
|
+ "reflect"
|
|
|
+ "testing"
|
|
|
+)
|
|
|
+
|
|
|
+func TestKeyIndexGet(t *testing.T) {
|
|
|
+ // key: "foo"
|
|
|
+ // index: 12
|
|
|
+ // generations:
|
|
|
+ // {empty}
|
|
|
+ // {8[1], 10[2], 12(t)[3]}
|
|
|
+ // {4[2], 6(t)[3]}
|
|
|
+ ki := newTestKeyIndex()
|
|
|
+ ki.compact(4, make(map[uint64]struct{}))
|
|
|
+
|
|
|
+ tests := []struct {
|
|
|
+ index uint64
|
|
|
+
|
|
|
+ windex uint64
|
|
|
+ werr error
|
|
|
+ }{
|
|
|
+ // expected not exist on an index that is greater than the last tombstone
|
|
|
+ {13, 0, ErrIndexNotFound},
|
|
|
+ {13, 0, ErrIndexNotFound},
|
|
|
+
|
|
|
+ // get on generation 2
|
|
|
+ {12, 12, nil},
|
|
|
+ {11, 10, nil},
|
|
|
+ {10, 10, nil},
|
|
|
+ {9, 8, nil},
|
|
|
+ {8, 8, nil},
|
|
|
+ {7, 0, ErrIndexNotFound},
|
|
|
+
|
|
|
+ // get on generation 1
|
|
|
+ {6, 6, nil},
|
|
|
+ {5, 4, nil},
|
|
|
+ {4, 4, nil},
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, tt := range tests {
|
|
|
+ index, err := ki.get(tt.index)
|
|
|
+ if err != tt.werr {
|
|
|
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
|
|
|
+ }
|
|
|
+ if index != tt.windex {
|
|
|
+ t.Errorf("#%d: index = %d, want %d", i, index, tt.index)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestKeyIndexPut(t *testing.T) {
|
|
|
+ ki := &keyIndex{key: []byte("foo")}
|
|
|
+ ki.put(5)
|
|
|
+
|
|
|
+ wki := &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 5,
|
|
|
+ generations: []generation{{ver: 1, cont: []uint64{5}}},
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(ki, wki) {
|
|
|
+ t.Errorf("ki = %+v, want %+v", ki, wki)
|
|
|
+ }
|
|
|
+
|
|
|
+ ki.put(7)
|
|
|
+
|
|
|
+ wki = &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 7,
|
|
|
+ generations: []generation{{ver: 2, cont: []uint64{5, 7}}},
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(ki, wki) {
|
|
|
+ t.Errorf("ki = %+v, want %+v", ki, wki)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestKeyIndexTombstone(t *testing.T) {
|
|
|
+ ki := &keyIndex{key: []byte("foo")}
|
|
|
+ ki.put(5)
|
|
|
+
|
|
|
+ ki.tombstone(7)
|
|
|
+
|
|
|
+ wki := &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 7,
|
|
|
+ generations: []generation{{ver: 2, cont: []uint64{5, 7}}, {}},
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(ki, wki) {
|
|
|
+ t.Errorf("ki = %+v, want %+v", ki, wki)
|
|
|
+ }
|
|
|
+
|
|
|
+ ki.put(8)
|
|
|
+ ki.put(9)
|
|
|
+ ki.tombstone(15)
|
|
|
+
|
|
|
+ wki = &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 15,
|
|
|
+ generations: []generation{{ver: 2, cont: []uint64{5, 7}}, {ver: 3, cont: []uint64{8, 9, 15}}, {}},
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(ki, wki) {
|
|
|
+ t.Errorf("ki = %+v, want %+v", ki, wki)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestKeyIndexCompact(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ compact uint64
|
|
|
+
|
|
|
+ wki *keyIndex
|
|
|
+ wam map[uint64]struct{}
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ 1,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{2, 4, 6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 2: struct{}{}, 4: struct{}{}, 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 2,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{2, 4, 6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 2: struct{}{}, 4: struct{}{}, 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 3,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{2, 4, 6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 2: struct{}{}, 4: struct{}{}, 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 4,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{4, 6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 4: struct{}{}, 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 5,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{4, 6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 4: struct{}{}, 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 6,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{6}},
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 6: struct{}{},
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 7,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 8,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 9,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{8, 10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 8: struct{}{}, 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 10,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 11,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{10, 12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 10: struct{}{}, 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 12,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {ver: 3, cont: []uint64{12}},
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{
|
|
|
+ 12: struct{}{},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ 13,
|
|
|
+ &keyIndex{
|
|
|
+ key: []byte("foo"),
|
|
|
+ index: 12,
|
|
|
+ generations: []generation{
|
|
|
+ {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ map[uint64]struct{}{},
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ // Continous Compaction
|
|
|
+ ki := newTestKeyIndex()
|
|
|
+ for i, tt := range tests {
|
|
|
+ am := make(map[uint64]struct{})
|
|
|
+ ki.compact(tt.compact, am)
|
|
|
+ if !reflect.DeepEqual(ki, tt.wki) {
|
|
|
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(am, tt.wam) {
|
|
|
+ t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Jump Compaction
|
|
|
+ for i, tt := range tests {
|
|
|
+ if (i%2 == 0 && i < 6) && (i%2 == 1 && i > 6) {
|
|
|
+ am := make(map[uint64]struct{})
|
|
|
+ ki.compact(tt.compact, am)
|
|
|
+ if !reflect.DeepEqual(ki, tt.wki) {
|
|
|
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(am, tt.wam) {
|
|
|
+ t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // OnceCompaction
|
|
|
+ for i, tt := range tests {
|
|
|
+ ki := newTestKeyIndex()
|
|
|
+ am := make(map[uint64]struct{})
|
|
|
+ ki.compact(tt.compact, am)
|
|
|
+ if !reflect.DeepEqual(ki, tt.wki) {
|
|
|
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
|
|
|
+ }
|
|
|
+ if !reflect.DeepEqual(am, tt.wam) {
|
|
|
+ t.Errorf("#%d: am = %+v, want %+v", am, tt.wam)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func newTestKeyIndex() *keyIndex {
|
|
|
+ // key: "foo"
|
|
|
+ // index: 12
|
|
|
+ // generations:
|
|
|
+ // {empty}
|
|
|
+ // {8[1], 10[2], 12(t)[3]}
|
|
|
+ // {2[1], 4[2], 6(t)[3]}
|
|
|
+
|
|
|
+ ki := &keyIndex{key: []byte("foo")}
|
|
|
+ ki.put(2)
|
|
|
+ ki.put(4)
|
|
|
+ ki.tombstone(6)
|
|
|
+ ki.put(8)
|
|
|
+ ki.put(10)
|
|
|
+ ki.tombstone(12)
|
|
|
+ return ki
|
|
|
+}
|