github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/pebble_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package storage 12 13 import ( 14 "bytes" 15 "fmt" 16 "io/ioutil" 17 "math/rand" 18 "sort" 19 "strconv" 20 "strings" 21 "testing" 22 23 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 24 "github.com/cockroachdb/cockroach/pkg/util/hlc" 25 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 26 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 27 "github.com/cockroachdb/cockroach/pkg/util/randutil" 28 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 29 "github.com/cockroachdb/datadriven" 30 "github.com/cockroachdb/pebble" 31 ) 32 33 func TestPebbleTimeBoundPropCollector(t *testing.T) { 34 defer leaktest.AfterTest(t)() 35 36 datadriven.RunTest(t, "testdata/time_bound_props", func(t *testing.T, d *datadriven.TestData) string { 37 c := &pebbleTimeBoundPropCollector{} 38 switch d.Cmd { 39 case "build": 40 for _, line := range strings.Split(d.Input, "\n") { 41 parts := strings.Fields(line) 42 if len(parts) != 2 { 43 return fmt.Sprintf("malformed line: %s, expected: <key>/<timestamp> <value>", line) 44 } 45 keyParts := strings.Split(parts[0], "/") 46 if len(keyParts) != 2 { 47 return fmt.Sprintf("malformed key: %s, expected: <key>/<timestamp>", parts[0]) 48 } 49 50 key := []byte(keyParts[0]) 51 timestamp, err := strconv.Atoi(keyParts[1]) 52 if err != nil { 53 return err.Error() 54 } 55 ikey := pebble.InternalKey{ 56 UserKey: EncodeKey(MVCCKey{ 57 Key: key, 58 Timestamp: hlc.Timestamp{WallTime: int64(timestamp)}, 59 }), 60 } 61 62 value := []byte(parts[1]) 63 if timestamp == 0 { 64 if n, err := fmt.Sscanf(string(value), "timestamp=%d", ×tamp); err != nil { 65 return err.Error() 66 } else if n != 1 { 67 return fmt.Sprintf("malformed txn timestamp: %s, expected timestamp=<value>", value) 68 } 69 meta := &enginepb.MVCCMetadata{} 70 meta.Timestamp.WallTime = int64(timestamp) 71 meta.Txn = &enginepb.TxnMeta{} 72 var err error 73 value, err = protoutil.Marshal(meta) 74 if err != nil { 75 return err.Error() 76 } 77 } 78 79 if err := c.Add(ikey, value); err != nil { 80 return err.Error() 81 } 82 } 83 84 // Retrieve the properties and sort them for test determinism. 85 m := make(map[string]string) 86 if err := c.Finish(m); err != nil { 87 return err.Error() 88 } 89 var keys []string 90 for k := range m { 91 keys = append(keys, k) 92 } 93 sort.Strings(keys) 94 95 var buf bytes.Buffer 96 for _, k := range keys { 97 fmt.Fprintf(&buf, "%s: %x\n", k, m[k]) 98 } 99 return buf.String() 100 101 default: 102 return fmt.Sprintf("unknown command: %s", d.Cmd) 103 } 104 }) 105 } 106 107 func TestPebbleIterReuse(t *testing.T) { 108 defer leaktest.AfterTest(t)() 109 // Regression test for https://github.com/cockroachdb/cockroach/issues/42354 110 // and similar issues arising from improper re-initialization of cached 111 // iterators. 112 113 eng := createTestPebbleEngine() 114 defer eng.Close() 115 116 batch := eng.NewBatch() 117 for i := 0; i < 100; i++ { 118 key := MVCCKey{[]byte{byte(i)}, hlc.Timestamp{WallTime: 100}} 119 if err := batch.Put(key, []byte("foo")); err != nil { 120 t.Fatal(err) 121 } 122 } 123 124 iter1 := batch.NewIterator(IterOptions{LowerBound: []byte{40}, UpperBound: []byte{50}}) 125 valuesCount := 0 126 // Seek to a value before the lower bound. Identical to seeking to the lower bound. 127 iter1.SeekGE(MVCCKey{Key: []byte{30}}) 128 for ; ; iter1.Next() { 129 ok, err := iter1.Valid() 130 if err != nil { 131 t.Fatal(err) 132 } else if !ok { 133 break 134 } 135 i := iter1.UnsafeKey().Key[0] 136 if i < 40 || i >= 50 { 137 t.Fatalf("iterator returned key out of bounds: %d", i) 138 } 139 140 valuesCount++ 141 } 142 143 if valuesCount != 10 { 144 t.Fatalf("expected 10 values, got %d", valuesCount) 145 } 146 iter1.Close() 147 148 // Create another iterator, with no lower bound but an upper bound that 149 // is lower than the previous iterator's lower bound. This should still result 150 // in the right amount of keys being returned; the lower bound from the 151 // previous iterator should get zeroed. 152 iter2 := batch.NewIterator(IterOptions{UpperBound: []byte{10}}) 153 valuesCount = 0 154 iter1.SeekGE(MVCCKey{Key: []byte{0}}) 155 for ; ; iter2.Next() { 156 ok, err := iter1.Valid() 157 if err != nil { 158 t.Fatal(err) 159 } else if !ok { 160 break 161 } 162 163 i := iter2.UnsafeKey().Key[0] 164 if i >= 10 { 165 t.Fatalf("iterator returned key out of bounds: %d", i) 166 } 167 valuesCount++ 168 } 169 170 if valuesCount != 10 { 171 t.Fatalf("expected 10 values, got %d", valuesCount) 172 } 173 iter2.Close() 174 } 175 176 func makeMVCCKey(a string) MVCCKey { 177 return MVCCKey{Key: []byte(a)} 178 } 179 180 func TestPebbleSeparatorSuccessor(t *testing.T) { 181 defer leaktest.AfterTest(t)() 182 183 sepCases := []struct { 184 a, b, want MVCCKey 185 }{ 186 // Many cases here are adapted from a Pebble unit test. 187 188 // Non-empty b values. 189 {makeMVCCKey("black"), makeMVCCKey("blue"), makeMVCCKey("blb")}, 190 {makeMVCCKey(""), makeMVCCKey("2"), makeMVCCKey("")}, 191 {makeMVCCKey("1"), makeMVCCKey("2"), makeMVCCKey("1")}, 192 {makeMVCCKey("1"), makeMVCCKey("29"), makeMVCCKey("2")}, 193 {makeMVCCKey("13"), makeMVCCKey("19"), makeMVCCKey("14")}, 194 {makeMVCCKey("13"), makeMVCCKey("99"), makeMVCCKey("2")}, 195 {makeMVCCKey("135"), makeMVCCKey("19"), makeMVCCKey("14")}, 196 {makeMVCCKey("1357"), makeMVCCKey("19"), makeMVCCKey("14")}, 197 {makeMVCCKey("1357"), makeMVCCKey("2"), makeMVCCKey("14")}, 198 {makeMVCCKey("13\xff"), makeMVCCKey("14"), makeMVCCKey("13\xff")}, 199 {makeMVCCKey("13\xff"), makeMVCCKey("19"), makeMVCCKey("14")}, 200 {makeMVCCKey("1\xff\xff"), makeMVCCKey("19"), makeMVCCKey("1\xff\xff")}, 201 {makeMVCCKey("1\xff\xff"), makeMVCCKey("2"), makeMVCCKey("1\xff\xff")}, 202 {makeMVCCKey("1\xff\xff"), makeMVCCKey("9"), makeMVCCKey("2")}, 203 {makeMVCCKey("1\xfd\xff"), makeMVCCKey("1\xff"), makeMVCCKey("1\xfe")}, 204 {MVCCKey{ 205 Key: []byte("1\xff\xff"), 206 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 207 }, makeMVCCKey("9"), makeMVCCKey("2")}, 208 {MVCCKey{ 209 Key: []byte("1\xff\xff"), 210 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 211 }, makeMVCCKey("19"), MVCCKey{ 212 Key: []byte("1\xff\xff"), 213 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 214 }, 215 }, 216 // Empty b values. 217 {makeMVCCKey(""), makeMVCCKey(""), makeMVCCKey("")}, 218 {makeMVCCKey("green"), makeMVCCKey(""), makeMVCCKey("green")}, 219 {makeMVCCKey("1"), makeMVCCKey(""), makeMVCCKey("1")}, 220 {makeMVCCKey("11\xff"), makeMVCCKey(""), makeMVCCKey("11\xff")}, 221 {makeMVCCKey("1\xff"), makeMVCCKey(""), makeMVCCKey("1\xff")}, 222 {makeMVCCKey("1\xff\xff"), makeMVCCKey(""), makeMVCCKey("1\xff\xff")}, 223 {makeMVCCKey("\xff"), makeMVCCKey(""), makeMVCCKey("\xff")}, 224 {makeMVCCKey("\xff\xff"), makeMVCCKey(""), makeMVCCKey("\xff\xff")}, 225 } 226 for _, tc := range sepCases { 227 t.Run("", func(t *testing.T) { 228 got := string(MVCCComparer.Separator(nil, EncodeKey(tc.a), EncodeKey(tc.b))) 229 if got != string(EncodeKey(tc.want)) { 230 t.Errorf("a, b = %q, %q: got %q, want %q", tc.a, tc.b, got, tc.want) 231 } 232 }) 233 } 234 235 succCases := []struct { 236 a, want MVCCKey 237 }{ 238 // Many cases adapted from Pebble test. 239 {makeMVCCKey("black"), makeMVCCKey("c")}, 240 {makeMVCCKey("green"), makeMVCCKey("h")}, 241 {makeMVCCKey(""), makeMVCCKey("")}, 242 {makeMVCCKey("13"), makeMVCCKey("2")}, 243 {makeMVCCKey("135"), makeMVCCKey("2")}, 244 {makeMVCCKey("13\xff"), makeMVCCKey("2")}, 245 {MVCCKey{ 246 Key: []byte("1\xff\xff"), 247 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 248 }, makeMVCCKey("2")}, 249 {makeMVCCKey("\xff"), makeMVCCKey("\xff")}, 250 {makeMVCCKey("\xff\xff"), makeMVCCKey("\xff\xff")}, 251 {makeMVCCKey("\xff\xff\xff"), makeMVCCKey("\xff\xff\xff")}, 252 {makeMVCCKey("\xfe\xff\xff"), makeMVCCKey("\xff")}, 253 {MVCCKey{ 254 Key: []byte("\xff\xff"), 255 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 256 }, MVCCKey{ 257 Key: []byte("\xff\xff"), 258 Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3}, 259 }}, 260 } 261 for _, tc := range succCases { 262 t.Run("", func(t *testing.T) { 263 got := string(MVCCComparer.Successor(nil, EncodeKey(tc.a))) 264 if got != string(EncodeKey(tc.want)) { 265 t.Errorf("a = %q: got %q, want %q", tc.a, got, tc.want) 266 } 267 }) 268 } 269 270 } 271 272 func BenchmarkMVCCKeyCompare(b *testing.B) { 273 rng := rand.New(rand.NewSource(timeutil.Now().Unix())) 274 keys := make([][]byte, 1000) 275 for i := range keys { 276 k := MVCCKey{ 277 Key: randutil.RandBytes(rng, 8), 278 Timestamp: hlc.Timestamp{ 279 WallTime: int64(rng.Intn(5)), 280 }, 281 } 282 keys[i] = EncodeKey(k) 283 } 284 285 b.ResetTimer() 286 var c int 287 for i, j := 0, 0; i < b.N; i, j = i+1, j+3 { 288 c = MVCCKeyCompare(keys[i%len(keys)], keys[j%len(keys)]) 289 } 290 if testing.Verbose() { 291 fmt.Fprint(ioutil.Discard, c) 292 } 293 }