mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-27 00:55:17 +00:00
430 lines
10 KiB
Go
430 lines
10 KiB
Go
// Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use
|
|
// of this source code is governed by a BSD-style license that can be found in
|
|
// the LICENSE file.
|
|
|
|
package manifest
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/cockroachdb/datadriven"
|
|
"github.com/cockroachdb/pebble/internal/base"
|
|
"github.com/cockroachdb/pebble/internal/testkeys"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func levelMetadata(level int, files ...*FileMetadata) LevelMetadata {
|
|
return makeLevelMetadata(base.DefaultComparer.Compare, level, files)
|
|
}
|
|
|
|
func ikey(s string) InternalKey {
|
|
return base.MakeInternalKey([]byte(s), 0, base.InternalKeyKindSet)
|
|
}
|
|
|
|
func TestIkeyRange(t *testing.T) {
|
|
cmp := base.DefaultComparer.Compare
|
|
testCases := []struct {
|
|
input, want string
|
|
}{
|
|
{
|
|
"",
|
|
"-",
|
|
},
|
|
{
|
|
"a-e",
|
|
"a-e",
|
|
},
|
|
{
|
|
"a-e a-e",
|
|
"a-e",
|
|
},
|
|
{
|
|
"c-g a-e",
|
|
"a-g",
|
|
},
|
|
{
|
|
"a-e c-g a-e",
|
|
"a-g",
|
|
},
|
|
{
|
|
"b-d f-g",
|
|
"b-g",
|
|
},
|
|
{
|
|
"d-e b-d",
|
|
"b-e",
|
|
},
|
|
{
|
|
"e-e",
|
|
"e-e",
|
|
},
|
|
{
|
|
"f-g e-e d-e c-g b-d a-e",
|
|
"a-g",
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
var f []*FileMetadata
|
|
if tc.input != "" {
|
|
for i, s := range strings.Split(tc.input, " ") {
|
|
m := (&FileMetadata{
|
|
FileNum: base.FileNum(i),
|
|
}).ExtendPointKeyBounds(cmp, ikey(s[0:1]), ikey(s[2:3]))
|
|
m.InitPhysicalBacking()
|
|
f = append(f, m)
|
|
}
|
|
}
|
|
levelMetadata := makeLevelMetadata(base.DefaultComparer.Compare, 0, f)
|
|
|
|
sm, la := KeyRange(base.DefaultComparer.Compare, levelMetadata.Iter())
|
|
got := string(sm.UserKey) + "-" + string(la.UserKey)
|
|
if got != tc.want {
|
|
t.Errorf("KeyRange(%q) = %q, %q", tc.input, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOverlaps(t *testing.T) {
|
|
var v *Version
|
|
cmp := testkeys.Comparer.Compare
|
|
fmtKey := testkeys.Comparer.FormatKey
|
|
datadriven.RunTest(t, "testdata/overlaps", func(t *testing.T, d *datadriven.TestData) string {
|
|
switch d.Cmd {
|
|
case "define":
|
|
var err error
|
|
v, err = ParseVersionDebug(cmp, fmtKey, 64>>10 /* flush split bytes */, d.Input)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return v.String()
|
|
case "overlaps":
|
|
var level int
|
|
var start, end string
|
|
var exclusiveEnd bool
|
|
d.ScanArgs(t, "level", &level)
|
|
d.ScanArgs(t, "start", &start)
|
|
d.ScanArgs(t, "end", &end)
|
|
d.ScanArgs(t, "exclusive-end", &exclusiveEnd)
|
|
overlaps := v.Overlaps(level, testkeys.Comparer.Compare, []byte(start), []byte(end), exclusiveEnd)
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "%d files:\n", overlaps.Len())
|
|
overlaps.Each(func(f *FileMetadata) {
|
|
fmt.Fprintf(&buf, "%s\n", f.DebugString(base.DefaultFormatter, false))
|
|
})
|
|
return buf.String()
|
|
default:
|
|
return fmt.Sprintf("unknown command: %s", d.Cmd)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestContains(t *testing.T) {
|
|
cmp := base.DefaultComparer.Compare
|
|
newFileMeta := func(fileNum base.FileNum, size uint64, smallest, largest base.InternalKey) *FileMetadata {
|
|
m := (&FileMetadata{
|
|
FileNum: fileNum,
|
|
Size: size,
|
|
}).ExtendPointKeyBounds(cmp, smallest, largest)
|
|
m.InitPhysicalBacking()
|
|
return m
|
|
}
|
|
m00 := newFileMeta(
|
|
700,
|
|
1,
|
|
base.ParseInternalKey("b.SET.7008"),
|
|
base.ParseInternalKey("e.SET.7009"),
|
|
)
|
|
m01 := newFileMeta(
|
|
701,
|
|
1,
|
|
base.ParseInternalKey("c.SET.7018"),
|
|
base.ParseInternalKey("f.SET.7019"),
|
|
)
|
|
m02 := newFileMeta(
|
|
702,
|
|
1,
|
|
base.ParseInternalKey("f.SET.7028"),
|
|
base.ParseInternalKey("g.SET.7029"),
|
|
)
|
|
m03 := newFileMeta(
|
|
703,
|
|
1,
|
|
base.ParseInternalKey("x.SET.7038"),
|
|
base.ParseInternalKey("y.SET.7039"),
|
|
)
|
|
m04 := newFileMeta(
|
|
704,
|
|
1,
|
|
base.ParseInternalKey("n.SET.7048"),
|
|
base.ParseInternalKey("p.SET.7049"),
|
|
)
|
|
m05 := newFileMeta(
|
|
705,
|
|
1,
|
|
base.ParseInternalKey("p.SET.7058"),
|
|
base.ParseInternalKey("p.SET.7059"),
|
|
)
|
|
m06 := newFileMeta(
|
|
706,
|
|
1,
|
|
base.ParseInternalKey("p.SET.7068"),
|
|
base.ParseInternalKey("u.SET.7069"),
|
|
)
|
|
m07 := newFileMeta(
|
|
707,
|
|
1,
|
|
base.ParseInternalKey("r.SET.7078"),
|
|
base.ParseInternalKey("s.SET.7079"),
|
|
)
|
|
|
|
m10 := newFileMeta(
|
|
710,
|
|
1,
|
|
base.ParseInternalKey("d.SET.7108"),
|
|
base.ParseInternalKey("g.SET.7109"),
|
|
)
|
|
m11 := newFileMeta(
|
|
711,
|
|
1,
|
|
base.ParseInternalKey("g.SET.7118"),
|
|
base.ParseInternalKey("j.SET.7119"),
|
|
)
|
|
m12 := newFileMeta(
|
|
712,
|
|
1,
|
|
base.ParseInternalKey("n.SET.7128"),
|
|
base.ParseInternalKey("p.SET.7129"),
|
|
)
|
|
m13 := newFileMeta(
|
|
713,
|
|
1,
|
|
base.ParseInternalKey("p.SET.7148"),
|
|
base.ParseInternalKey("p.SET.7149"),
|
|
)
|
|
m14 := newFileMeta(
|
|
714,
|
|
1,
|
|
base.ParseInternalKey("p.SET.7138"),
|
|
base.ParseInternalKey("u.SET.7139"),
|
|
)
|
|
|
|
v := Version{
|
|
Levels: [NumLevels]LevelMetadata{
|
|
0: levelMetadata(0, m00, m01, m02, m03, m04, m05, m06, m07),
|
|
1: levelMetadata(1, m10, m11, m12, m13, m14),
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
level int
|
|
file *FileMetadata
|
|
want bool
|
|
}{
|
|
// Level 0: m00=b-e, m01=c-f, m02=f-g, m03=x-y, m04=n-p, m05=p-p, m06=p-u, m07=r-s.
|
|
// Note that:
|
|
// - the slice isn't sorted (e.g. m02=f-g, m03=x-y, m04=n-p),
|
|
// - m00 and m01 overlap (not just touch),
|
|
// - m06 contains m07,
|
|
// - m00, m01 and m02 transitively overlap/touch each other, and
|
|
// - m04, m05, m06 and m07 transitively overlap/touch each other.
|
|
{0, m00, true},
|
|
{0, m01, true},
|
|
{0, m02, true},
|
|
{0, m03, true},
|
|
{0, m04, true},
|
|
{0, m05, true},
|
|
{0, m06, true},
|
|
{0, m07, true},
|
|
{0, m10, false},
|
|
{0, m11, false},
|
|
{0, m12, false},
|
|
{0, m13, false},
|
|
{0, m14, false},
|
|
{1, m00, false},
|
|
{1, m01, false},
|
|
{1, m02, false},
|
|
{1, m03, false},
|
|
{1, m04, false},
|
|
{1, m05, false},
|
|
{1, m06, false},
|
|
{1, m07, false},
|
|
{1, m10, true},
|
|
{1, m11, true},
|
|
{1, m12, true},
|
|
{1, m13, true},
|
|
{1, m14, true},
|
|
|
|
// Level 2: empty.
|
|
{2, m00, false},
|
|
{2, m14, false},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
got := v.Contains(tc.level, cmp, tc.file)
|
|
if got != tc.want {
|
|
t.Errorf("level=%d, file=%s\ngot %t\nwant %t", tc.level, tc.file, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestVersionUnref(t *testing.T) {
|
|
list := &VersionList{}
|
|
list.Init(&sync.Mutex{})
|
|
v := &Version{Deleted: func([]*FileBacking) {}}
|
|
v.Ref()
|
|
list.PushBack(v)
|
|
v.Unref()
|
|
if !list.Empty() {
|
|
t.Fatalf("expected version list to be empty")
|
|
}
|
|
}
|
|
|
|
func TestCheckOrdering(t *testing.T) {
|
|
cmp := base.DefaultComparer.Compare
|
|
fmtKey := base.DefaultComparer.FormatKey
|
|
datadriven.RunTest(t, "testdata/version_check_ordering",
|
|
func(t *testing.T, d *datadriven.TestData) string {
|
|
switch d.Cmd {
|
|
case "check-ordering":
|
|
orderingInvariants := ProhibitSplitUserKeys
|
|
if d.HasArg("allow-split-user-keys") {
|
|
orderingInvariants = AllowSplitUserKeys
|
|
}
|
|
v, err := ParseVersionDebug(cmp, fmtKey, 10<<20, d.Input)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
// L0 files compare on sequence numbers. Use the seqnums from the
|
|
// smallest / largest bounds for the table.
|
|
v.Levels[0].Slice().Each(func(m *FileMetadata) {
|
|
m.SmallestSeqNum = m.Smallest.SeqNum()
|
|
m.LargestSeqNum = m.Largest.SeqNum()
|
|
})
|
|
if err = v.CheckOrdering(cmp, base.DefaultFormatter, orderingInvariants); err != nil {
|
|
return err.Error()
|
|
}
|
|
return "OK"
|
|
|
|
default:
|
|
return fmt.Sprintf("unknown command: %s", d.Cmd)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestExtendBounds(t *testing.T) {
|
|
cmp := base.DefaultComparer.Compare
|
|
parseBounds := func(line string) (lower, upper InternalKey) {
|
|
parts := strings.Split(line, "-")
|
|
if len(parts) == 1 {
|
|
parts = strings.Split(parts[0], ":")
|
|
start, end := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
|
|
lower = base.ParseInternalKey(start)
|
|
switch k := lower.Kind(); k {
|
|
case base.InternalKeyKindRangeDelete:
|
|
upper = base.MakeRangeDeleteSentinelKey([]byte(end))
|
|
case base.InternalKeyKindRangeKeySet, base.InternalKeyKindRangeKeyUnset, base.InternalKeyKindRangeKeyDelete:
|
|
upper = base.MakeExclusiveSentinelKey(k, []byte(end))
|
|
default:
|
|
panic(fmt.Sprintf("unknown kind %s with end key", k))
|
|
}
|
|
} else {
|
|
l, u := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
|
|
lower, upper = base.ParseInternalKey(l), base.ParseInternalKey(u)
|
|
}
|
|
return
|
|
}
|
|
format := func(m *FileMetadata) string {
|
|
var b bytes.Buffer
|
|
var smallest, largest string
|
|
switch m.boundTypeSmallest {
|
|
case boundTypePointKey:
|
|
smallest = "point"
|
|
case boundTypeRangeKey:
|
|
smallest = "range"
|
|
default:
|
|
return fmt.Sprintf("unknown bound type %d", m.boundTypeSmallest)
|
|
}
|
|
switch m.boundTypeLargest {
|
|
case boundTypePointKey:
|
|
largest = "point"
|
|
case boundTypeRangeKey:
|
|
largest = "range"
|
|
default:
|
|
return fmt.Sprintf("unknown bound type %d", m.boundTypeLargest)
|
|
}
|
|
bounds, err := m.boundsMarker()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fmt.Fprintf(&b, "%s\n", m.DebugString(base.DefaultFormatter, true))
|
|
fmt.Fprintf(&b, " bounds: (smallest=%s,largest=%s) (0x%08b)\n", smallest, largest, bounds)
|
|
return b.String()
|
|
}
|
|
m := &FileMetadata{}
|
|
datadriven.RunTest(t, "testdata/file_metadata_bounds", func(t *testing.T, d *datadriven.TestData) string {
|
|
switch d.Cmd {
|
|
case "reset":
|
|
m = &FileMetadata{}
|
|
return ""
|
|
case "extend-point-key-bounds":
|
|
u, l := parseBounds(d.Input)
|
|
m.ExtendPointKeyBounds(cmp, u, l)
|
|
return format(m)
|
|
case "extend-range-key-bounds":
|
|
u, l := parseBounds(d.Input)
|
|
m.ExtendRangeKeyBounds(cmp, u, l)
|
|
return format(m)
|
|
default:
|
|
return fmt.Sprintf("unknown command %s\n", d.Cmd)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFileMetadata_ParseRoundTrip(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
input string
|
|
output string
|
|
}{
|
|
{
|
|
name: "point keys only",
|
|
input: "000001:[a#0,SET-z#0,DEL] seqnums:[0-0] points:[a#0,SET-z#0,DEL]",
|
|
},
|
|
{
|
|
name: "range keys only",
|
|
input: "000001:[a#0,RANGEKEYSET-z#0,RANGEKEYDEL] seqnums:[0-0] ranges:[a#0,RANGEKEYSET-z#0,RANGEKEYDEL]",
|
|
},
|
|
{
|
|
name: "point and range keys",
|
|
input: "000001:[a#0,RANGEKEYSET-d#0,DEL] seqnums:[0-0] points:[b#0,SET-d#0,DEL] ranges:[a#0,RANGEKEYSET-c#0,RANGEKEYDEL]",
|
|
},
|
|
{
|
|
name: "point and range keys with nonzero senums",
|
|
input: "000001:[a#3,RANGEKEYSET-d#4,DEL] seqnums:[3-7] points:[b#3,SET-d#4,DEL] ranges:[a#3,RANGEKEYSET-c#5,RANGEKEYDEL]",
|
|
},
|
|
{
|
|
name: "whitespace",
|
|
input: " 000001 : [ a#0,SET - z#0,DEL] points : [ a#0,SET - z#0,DEL] ",
|
|
output: "000001:[a#0,SET-z#0,DEL] seqnums:[0-0] points:[a#0,SET-z#0,DEL]",
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
m, err := ParseFileMetadataDebug(tc.input)
|
|
require.NoError(t, err)
|
|
err = m.Validate(base.DefaultComparer.Compare, base.DefaultFormatter)
|
|
require.NoError(t, err)
|
|
got := m.DebugString(base.DefaultFormatter, true)
|
|
want := tc.input
|
|
if tc.output != "" {
|
|
want = tc.output
|
|
}
|
|
require.Equal(t, want, got)
|
|
})
|
|
}
|
|
}
|