github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/engine_test.go (about) 1 // Copyright 2014 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 "context" 16 "encoding/binary" 17 "encoding/hex" 18 "fmt" 19 "io" 20 "math/rand" 21 "os" 22 "path/filepath" 23 "reflect" 24 "sort" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/cockroachdb/cockroach/pkg/base" 31 "github.com/cockroachdb/cockroach/pkg/roachpb" 32 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 33 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 34 "github.com/cockroachdb/cockroach/pkg/storage/fs" 35 "github.com/cockroachdb/cockroach/pkg/testutils" 36 "github.com/cockroachdb/cockroach/pkg/util/hlc" 37 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 38 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 39 "github.com/cockroachdb/errors" 40 "github.com/cockroachdb/pebble" 41 "github.com/cockroachdb/pebble/vfs" 42 "github.com/stretchr/testify/assert" 43 "github.com/stretchr/testify/require" 44 ) 45 46 func ensureRangeEqual( 47 t *testing.T, sortedKeys []string, keyMap map[string][]byte, keyvals []MVCCKeyValue, 48 ) { 49 t.Helper() 50 if len(keyvals) != len(sortedKeys) { 51 t.Errorf("length mismatch. expected %s, got %s", sortedKeys, keyvals) 52 } 53 for i, kv := range keyvals { 54 if sortedKeys[i] != string(kv.Key.Key) { 55 t.Errorf("key mismatch at index %d: expected %q, got %q", i, sortedKeys[i], kv.Key) 56 } 57 if !bytes.Equal(keyMap[sortedKeys[i]], kv.Value) { 58 t.Errorf("value mismatch at index %d: expected %q, got %q", i, keyMap[sortedKeys[i]], kv.Value) 59 } 60 } 61 } 62 63 // TestEngineBatchCommit writes a batch containing 10K rows (all the 64 // same key) and concurrently attempts to read the value in a tight 65 // loop. The test verifies that either there is no value for the key 66 // or it contains the final value, but never a value in between. 67 func TestEngineBatchCommit(t *testing.T) { 68 defer leaktest.AfterTest(t)() 69 numWrites := 10000 70 key := mvccKey("a") 71 finalVal := []byte(strconv.Itoa(numWrites - 1)) 72 73 for _, engineImpl := range mvccEngineImpls { 74 t.Run(engineImpl.name, func(t *testing.T) { 75 e := engineImpl.create() 76 defer e.Close() 77 78 // Start a concurrent read operation in a busy loop. 79 readsBegun := make(chan struct{}) 80 readsDone := make(chan error) 81 writesDone := make(chan struct{}) 82 go func() { 83 readsDone <- func() error { 84 readsBegunAlias := readsBegun 85 for { 86 select { 87 case <-writesDone: 88 return nil 89 default: 90 val, err := e.Get(key) 91 if err != nil { 92 return err 93 } 94 if val != nil && !bytes.Equal(val, finalVal) { 95 return errors.Errorf("key value should be empty or %q; got %q", string(finalVal), string(val)) 96 } 97 if readsBegunAlias != nil { 98 close(readsBegunAlias) 99 readsBegunAlias = nil 100 } 101 } 102 } 103 }() 104 }() 105 // Wait until we've succeeded with first read. 106 <-readsBegun 107 108 // Create key/values and put them in a batch to engine. 109 batch := e.NewBatch() 110 defer batch.Close() 111 for i := 0; i < numWrites; i++ { 112 if err := batch.Put(key, []byte(strconv.Itoa(i))); err != nil { 113 t.Fatal(err) 114 } 115 } 116 if err := batch.Commit(false /* sync */); err != nil { 117 t.Fatal(err) 118 } 119 close(writesDone) 120 if err := <-readsDone; err != nil { 121 t.Fatal(err) 122 } 123 }) 124 } 125 } 126 127 func TestEngineBatchStaleCachedIterator(t *testing.T) { 128 defer leaktest.AfterTest(t)() 129 // Prevent regression of a bug which caused spurious MVCC errors due to an 130 // invalid optimization which let an iterator return key-value pairs which 131 // had since been deleted from the underlying engine. 132 // Discovered in #6878. 133 134 for _, engineImpl := range mvccEngineImpls { 135 t.Run(engineImpl.name, func(t *testing.T) { 136 eng := engineImpl.create() 137 defer eng.Close() 138 139 // Focused failure mode: highlights the actual bug. 140 { 141 batch := eng.NewBatch() 142 defer batch.Close() 143 iter := batch.NewIterator(IterOptions{UpperBound: roachpb.KeyMax}) 144 key := MVCCKey{Key: roachpb.Key("b")} 145 146 if err := batch.Put(key, []byte("foo")); err != nil { 147 t.Fatal(err) 148 } 149 150 iter.SeekGE(key) 151 152 if err := batch.Clear(key); err != nil { 153 t.Fatal(err) 154 } 155 156 // Iterator should not reuse its cached result. 157 iter.SeekGE(key) 158 159 if ok, err := iter.Valid(); err != nil { 160 t.Fatal(err) 161 } else if ok { 162 t.Fatalf("iterator unexpectedly valid: %v -> %v", 163 iter.UnsafeKey(), iter.UnsafeValue()) 164 } 165 166 iter.Close() 167 } 168 169 // Higher-level failure mode. Mostly for documentation. 170 { 171 batch := eng.NewBatch() 172 defer batch.Close() 173 174 key := roachpb.Key("z") 175 176 // Put a value so that the deletion below finds a value to seek 177 // to. 178 if err := MVCCPut(context.Background(), batch, nil, key, hlc.Timestamp{}, 179 roachpb.MakeValueFromString("x"), nil); err != nil { 180 t.Fatal(err) 181 } 182 183 // Seek the iterator to `key` and clear the value (but without 184 // telling the iterator about that). 185 if err := MVCCDelete(context.Background(), batch, nil, key, 186 hlc.Timestamp{}, nil); err != nil { 187 t.Fatal(err) 188 } 189 190 // Trigger a seek on the cached iterator by seeking to the (now 191 // absent) key. 192 // The underlying iterator will already be in the right position 193 // due to a seek in MVCCDelete (followed by a Clear, which does not 194 // invalidate the iterator's cache), and if it reports its cached 195 // result back, we'll see the (newly deleted) value (due to the 196 // failure mode above). 197 if v, _, err := MVCCGet(context.Background(), batch, key, 198 hlc.Timestamp{}, MVCCGetOptions{}); err != nil { 199 t.Fatal(err) 200 } else if v != nil { 201 t.Fatalf("expected no value, got %+v", v) 202 } 203 } 204 }) 205 } 206 } 207 208 func TestEngineBatch(t *testing.T) { 209 defer leaktest.AfterTest(t)() 210 211 for _, engineImpl := range mvccEngineImpls { 212 t.Run(engineImpl.name, func(t *testing.T) { 213 engine := engineImpl.create() 214 defer engine.Close() 215 216 numShuffles := 100 217 key := mvccKey("a") 218 // Those are randomized below. 219 type data struct { 220 key MVCCKey 221 value []byte 222 merge bool 223 } 224 batch := []data{ 225 {key, appender("~ockroachDB"), false}, 226 {key, appender("C~ckroachDB"), false}, 227 {key, appender("Co~kroachDB"), false}, 228 {key, appender("Coc~roachDB"), false}, 229 {key, appender("Cock~oachDB"), false}, 230 {key, appender("Cockr~achDB"), false}, 231 {key, appender("Cockro~chDB"), false}, 232 {key, appender("Cockroa~hDB"), false}, 233 {key, appender("Cockroac~DB"), false}, 234 {key, appender("Cockroach~B"), false}, 235 {key, appender("CockroachD~"), false}, 236 {key, nil, false}, 237 {key, appender("C"), true}, 238 {key, appender(" o"), true}, 239 {key, appender(" c"), true}, 240 {key, appender(" k"), true}, 241 {key, appender("r"), true}, 242 {key, appender(" o"), true}, 243 {key, appender(" a"), true}, 244 {key, appender(" c"), true}, 245 {key, appender("h"), true}, 246 {key, appender(" D"), true}, 247 {key, appender(" B"), true}, 248 } 249 250 apply := func(rw ReadWriter, d data) error { 251 if d.value == nil { 252 return rw.Clear(d.key) 253 } else if d.merge { 254 return rw.Merge(d.key, d.value) 255 } 256 return rw.Put(d.key, d.value) 257 } 258 259 get := func(rw ReadWriter, key MVCCKey) []byte { 260 b, err := rw.Get(key) 261 if err != nil { 262 t.Fatal(err) 263 } 264 var m enginepb.MVCCMetadata 265 if err := protoutil.Unmarshal(b, &m); err != nil { 266 t.Fatal(err) 267 } 268 if !m.IsInline() { 269 return nil 270 } 271 valueBytes, err := MakeValue(m).GetBytes() 272 if err != nil { 273 t.Fatal(err) 274 } 275 return valueBytes 276 } 277 278 for i := 0; i < numShuffles; i++ { 279 // In each run, create an array of shuffled operations. 280 shuffledIndices := rand.Perm(len(batch)) 281 currentBatch := make([]data, len(batch)) 282 for k := range currentBatch { 283 currentBatch[k] = batch[shuffledIndices[k]] 284 } 285 // Reset the key 286 if err := engine.Clear(key); err != nil { 287 t.Fatal(err) 288 } 289 // Run it once with individual operations and remember the result. 290 for i, op := range currentBatch { 291 if err := apply(engine, op); err != nil { 292 t.Errorf("%d: op %v: %+v", i, op, err) 293 continue 294 } 295 } 296 expectedValue := get(engine, key) 297 // Run the whole thing as a batch and compare. 298 b := engine.NewBatch() 299 defer b.Close() 300 if err := b.Clear(key); err != nil { 301 t.Fatal(err) 302 } 303 for _, op := range currentBatch { 304 if err := apply(b, op); err != nil { 305 t.Fatal(err) 306 } 307 } 308 // Try getting the value from the batch. 309 actualValue := get(b, key) 310 if !bytes.Equal(actualValue, expectedValue) { 311 t.Errorf("%d: expected %s, but got %s", i, expectedValue, actualValue) 312 } 313 // Try using an iterator to get the value from the batch. 314 iter := b.NewIterator(IterOptions{UpperBound: roachpb.KeyMax}) 315 iter.SeekGE(key) 316 if ok, err := iter.Valid(); !ok { 317 if currentBatch[len(currentBatch)-1].value != nil { 318 t.Errorf("%d: batch seek invalid, err=%v", i, err) 319 } 320 } else if !iter.Key().Equal(key) { 321 t.Errorf("%d: batch seek expected key %s, but got %s", i, key, iter.Key()) 322 } else { 323 var m enginepb.MVCCMetadata 324 if err := iter.ValueProto(&m); err != nil { 325 t.Fatal(err) 326 } 327 valueBytes, err := MakeValue(m).GetBytes() 328 if err != nil { 329 t.Fatal(err) 330 } 331 if !bytes.Equal(valueBytes, expectedValue) { 332 t.Errorf("%d: expected %s, but got %s", i, expectedValue, valueBytes) 333 } 334 } 335 iter.Close() 336 // Commit the batch and try getting the value from the engine. 337 if err := b.Commit(false /* sync */); err != nil { 338 t.Errorf("%d: %+v", i, err) 339 continue 340 } 341 actualValue = get(engine, key) 342 if !bytes.Equal(actualValue, expectedValue) { 343 t.Errorf("%d: expected %s, but got %s", i, expectedValue, actualValue) 344 } 345 } 346 }) 347 } 348 } 349 350 func TestEnginePutGetDelete(t *testing.T) { 351 defer leaktest.AfterTest(t)() 352 353 for _, engineImpl := range mvccEngineImpls { 354 t.Run(engineImpl.name, func(t *testing.T) { 355 engine := engineImpl.create() 356 defer engine.Close() 357 358 // Test for correct handling of empty keys, which should produce errors. 359 for i, err := range []error{ 360 engine.Put(mvccKey(""), []byte("")), 361 engine.Put(NilKey, []byte("")), 362 func() error { 363 _, err := engine.Get(mvccKey("")) 364 return err 365 }(), 366 engine.Clear(NilKey), 367 func() error { 368 _, err := engine.Get(NilKey) 369 return err 370 }(), 371 engine.Clear(NilKey), 372 engine.Clear(mvccKey("")), 373 } { 374 if err == nil { 375 t.Fatalf("%d: illegal handling of empty key", i) 376 } 377 } 378 379 // Test for allowed keys, which should go through. 380 testCases := []struct { 381 key MVCCKey 382 value []byte 383 }{ 384 {mvccKey("dog"), []byte("woof")}, 385 {mvccKey("cat"), []byte("meow")}, 386 {mvccKey("emptyval"), nil}, 387 {mvccKey("emptyval2"), []byte("")}, 388 {mvccKey("server"), []byte("42")}, 389 } 390 for _, c := range testCases { 391 val, err := engine.Get(c.key) 392 if err != nil { 393 t.Errorf("get: expected no error, but got %s", err) 394 } 395 if len(val) != 0 { 396 t.Errorf("expected key %q value.Bytes to be nil: got %+v", c.key, val) 397 } 398 if err := engine.Put(c.key, c.value); err != nil { 399 t.Errorf("put: expected no error, but got %s", err) 400 } 401 val, err = engine.Get(c.key) 402 if err != nil { 403 t.Errorf("get: expected no error, but got %s", err) 404 } 405 if !bytes.Equal(val, c.value) { 406 t.Errorf("expected key value %s to be %+v: got %+v", c.key, c.value, val) 407 } 408 if err := engine.Clear(c.key); err != nil { 409 t.Errorf("delete: expected no error, but got %s", err) 410 } 411 val, err = engine.Get(c.key) 412 if err != nil { 413 t.Errorf("get: expected no error, but got %s", err) 414 } 415 if len(val) != 0 { 416 t.Errorf("expected key %s value.Bytes to be nil: got %+v", c.key, val) 417 } 418 } 419 }) 420 } 421 } 422 423 func addMergeTimestamp(t *testing.T, data []byte, ts int64) []byte { 424 var v enginepb.MVCCMetadata 425 if err := protoutil.Unmarshal(data, &v); err != nil { 426 t.Fatal(err) 427 } 428 v.MergeTimestamp = &hlc.LegacyTimestamp{WallTime: ts} 429 return mustMarshal(&v) 430 } 431 432 // TestEngineMerge tests that the passing through of engine merge operations 433 // to the goMerge function works as expected. The semantics are tested more 434 // exhaustively in the merge tests themselves. 435 func TestEngineMerge(t *testing.T) { 436 defer leaktest.AfterTest(t)() 437 438 engineBytes := make([][][]byte, len(mvccEngineImpls)) 439 for engineIndex, engineImpl := range mvccEngineImpls { 440 t.Run(engineImpl.name, func(t *testing.T) { 441 engine := engineImpl.create() 442 defer engine.Close() 443 444 testcases := []struct { 445 testKey MVCCKey 446 merges [][]byte 447 expected []byte 448 }{ 449 { 450 // Test case with RawBytes only. 451 mvccKey("haste not in life"), 452 [][]byte{ 453 appender("x"), 454 appender("y"), 455 appender("z"), 456 }, 457 appender("xyz"), 458 }, 459 { 460 // Test case with RawBytes and MergeTimestamp. 461 mvccKey("timeseriesmerged"), 462 [][]byte{ 463 addMergeTimestamp(t, timeSeriesRow(testtime, 1000, []tsSample{ 464 {1, 1, 5, 5, 5}, 465 }...), 27), 466 timeSeriesRow(testtime, 1000, []tsSample{ 467 {2, 1, 5, 5, 5}, 468 {1, 2, 10, 7, 3}, 469 }...), 470 addMergeTimestamp(t, timeSeriesRow(testtime, 1000, []tsSample{ 471 {10, 1, 5, 5, 5}, 472 }...), 53), 473 timeSeriesRow(testtime, 1000, []tsSample{ 474 {5, 1, 5, 5, 5}, 475 {3, 1, 5, 5, 5}, 476 }...), 477 }, 478 addMergeTimestamp(t, timeSeriesRow(testtime, 1000, []tsSample{ 479 {1, 2, 10, 7, 3}, 480 {2, 1, 5, 5, 5}, 481 {3, 1, 5, 5, 5}, 482 {5, 1, 5, 5, 5}, 483 {10, 1, 5, 5, 5}, 484 }...), 27), 485 }, 486 } 487 engineBytes[engineIndex] = make([][]byte, len(testcases)) 488 for tcIndex, tc := range testcases { 489 for i, update := range tc.merges { 490 if err := engine.Merge(tc.testKey, update); err != nil { 491 t.Fatalf("%d: %+v", i, err) 492 } 493 } 494 result, _ := engine.Get(tc.testKey) 495 engineBytes[engineIndex][tcIndex] = result 496 var resultV, expectedV enginepb.MVCCMetadata 497 if err := protoutil.Unmarshal(result, &resultV); err != nil { 498 t.Fatal(err) 499 } 500 if err := protoutil.Unmarshal(tc.expected, &expectedV); err != nil { 501 t.Fatal(err) 502 } 503 if !reflect.DeepEqual(resultV, expectedV) { 504 t.Errorf("unexpected append-merge result: %v != %v", resultV, expectedV) 505 } 506 } 507 }) 508 } 509 for i := 0; i < len(engineBytes); i++ { 510 // Pair-wise comparison of bytes since difference in serialization 511 // can trigger replica consistency checker failures #45811 512 if i+1 == len(engineBytes) { 513 break 514 } 515 eng1 := i 516 eng2 := i + 1 517 for j := 0; j < len(engineBytes[eng1]); j++ { 518 if !bytes.Equal(engineBytes[eng1][j], engineBytes[eng2][j]) { 519 t.Errorf("engines %d, %d differ at test %d:\n%s\n != \n%s\n", eng1, eng2, j, 520 hex.Dump(engineBytes[eng1][j]), hex.Dump(engineBytes[eng2][j])) 521 } 522 } 523 } 524 } 525 526 func TestEngineMustExist(t *testing.T) { 527 defer leaktest.AfterTest(t)() 528 529 test := func(engineType enginepb.EngineType, errStr string) { 530 tempDir, dirCleanupFn := testutils.TempDir(t) 531 defer dirCleanupFn() 532 533 _, err := NewEngine( 534 engineType, 535 0, base.StorageConfig{ 536 Dir: tempDir, 537 MustExist: true, 538 }) 539 if err == nil { 540 t.Fatal("expected error related to missing directory") 541 } 542 if !strings.Contains(fmt.Sprint(err), errStr) { 543 t.Fatal(err) 544 } 545 } 546 547 test(enginepb.EngineTypeRocksDB, "does not exist (create_if_missing is false)") 548 test(enginepb.EngineTypePebble, "no such file or directory") 549 } 550 551 func TestEngineTimeBound(t *testing.T) { 552 defer leaktest.AfterTest(t)() 553 554 for _, engineImpl := range mvccEngineImpls { 555 t.Run(engineImpl.name, func(t *testing.T) { 556 engine := engineImpl.create() 557 defer engine.Close() 558 559 var minTimestamp = hlc.Timestamp{WallTime: 1, Logical: 0} 560 var maxTimestamp = hlc.Timestamp{WallTime: 3, Logical: 0} 561 times := []hlc.Timestamp{ 562 {WallTime: 2, Logical: 0}, 563 minTimestamp, 564 maxTimestamp, 565 {WallTime: 2, Logical: 0}, 566 } 567 568 for i, time := range times { 569 s := fmt.Sprintf("%02d", i) 570 key := MVCCKey{Key: roachpb.Key(s), Timestamp: time} 571 if err := engine.Put(key, []byte(s)); err != nil { 572 t.Fatal(err) 573 } 574 } 575 if err := engine.Flush(); err != nil { 576 t.Fatal(err) 577 } 578 579 batch := engine.NewBatch() 580 defer batch.Close() 581 582 check := func(t *testing.T, tbi Iterator, keys, ssts int) { 583 defer tbi.Close() 584 tbi.SeekGE(NilKey) 585 586 var count int 587 for ; ; tbi.Next() { 588 ok, err := tbi.Valid() 589 if err != nil { 590 t.Fatal(err) 591 } 592 if !ok { 593 break 594 } 595 count++ 596 } 597 598 // Make sure the iterator sees no writes. 599 if keys != count { 600 t.Fatalf("saw %d values in time bounded iterator, but expected %d", count, keys) 601 } 602 stats := tbi.Stats() 603 if a := stats.TimeBoundNumSSTs; a != ssts { 604 t.Fatalf("touched %d SSTs, expected %d", a, ssts) 605 } 606 } 607 608 testCases := []struct { 609 iter Iterator 610 keys, ssts int 611 }{ 612 // Completely to the right, not touching. 613 { 614 iter: batch.NewIterator(IterOptions{ 615 MinTimestampHint: maxTimestamp.Next(), 616 MaxTimestampHint: maxTimestamp.Next().Next(), 617 UpperBound: roachpb.KeyMax, 618 WithStats: true, 619 }), 620 keys: 0, 621 ssts: 0, 622 }, 623 // Completely to the left, not touching. 624 { 625 iter: batch.NewIterator(IterOptions{ 626 MinTimestampHint: minTimestamp.Prev().Prev(), 627 MaxTimestampHint: minTimestamp.Prev(), 628 UpperBound: roachpb.KeyMax, 629 WithStats: true, 630 }), 631 keys: 0, 632 ssts: 0, 633 }, 634 // Touching on the right. 635 { 636 iter: batch.NewIterator(IterOptions{ 637 MinTimestampHint: maxTimestamp, 638 MaxTimestampHint: maxTimestamp, 639 UpperBound: roachpb.KeyMax, 640 WithStats: true, 641 }), 642 keys: len(times), 643 ssts: 1, 644 }, 645 // Touching on the left. 646 { 647 iter: batch.NewIterator(IterOptions{ 648 MinTimestampHint: minTimestamp, 649 MaxTimestampHint: minTimestamp, 650 UpperBound: roachpb.KeyMax, 651 WithStats: true, 652 }), 653 keys: len(times), 654 ssts: 1, 655 }, 656 // Copy of last case, but confirm that we don't get SST stats if we don't 657 // ask for them. 658 { 659 iter: batch.NewIterator(IterOptions{ 660 MinTimestampHint: minTimestamp, 661 MaxTimestampHint: minTimestamp, 662 UpperBound: roachpb.KeyMax, 663 WithStats: false, 664 }), 665 keys: len(times), 666 ssts: 0, 667 }, 668 // Copy of last case, but confirm that upper bound is respected. 669 { 670 iter: batch.NewIterator(IterOptions{ 671 MinTimestampHint: minTimestamp, 672 MaxTimestampHint: minTimestamp, 673 UpperBound: []byte("02"), 674 WithStats: false, 675 }), 676 keys: 2, 677 ssts: 0, 678 }, 679 } 680 681 for _, test := range testCases { 682 t.Run("", func(t *testing.T) { 683 check(t, test.iter, test.keys, test.ssts) 684 }) 685 } 686 687 // Make a regular iterator. Before #21721, this would accidentally pick up the 688 // time bounded iterator instead. 689 iter := batch.NewIterator(IterOptions{UpperBound: roachpb.KeyMax}) 690 defer iter.Close() 691 iter.SeekGE(NilKey) 692 693 var count int 694 for ; ; iter.Next() { 695 ok, err := iter.Valid() 696 if err != nil { 697 t.Fatal(err) 698 } 699 if !ok { 700 break 701 } 702 count++ 703 } 704 705 // Make sure the iterator sees the writes (i.e. it's not the time bounded iterator). 706 if expCount := len(times); expCount != count { 707 t.Fatalf("saw %d values in regular iterator, but expected %d", count, expCount) 708 } 709 }) 710 } 711 } 712 713 func TestFlushWithSSTables(t *testing.T) { 714 defer leaktest.AfterTest(t)() 715 716 for _, engineImpl := range mvccEngineImpls { 717 t.Run(engineImpl.name, func(t *testing.T) { 718 engine := engineImpl.create() 719 defer engine.Close() 720 721 batch := engine.NewBatch() 722 for i := 0; i < 10000; i++ { 723 key := make([]byte, 4) 724 binary.BigEndian.PutUint32(key, uint32(i)) 725 err := batch.Put(MVCCKey{Key: key}, []byte("foobar")) 726 if err != nil { 727 t.Fatal(err) 728 } 729 } 730 731 err := batch.Commit(true) 732 if err != nil { 733 t.Fatal(err) 734 } 735 batch.Close() 736 737 err = engine.Flush() 738 if err != nil { 739 t.Fatal(err) 740 } 741 742 ssts := engine.GetSSTables() 743 if len(ssts) == 0 { 744 t.Fatal("expected non-zero sstables, got 0") 745 } 746 }) 747 } 748 } 749 750 func TestEngineScan1(t *testing.T) { 751 defer leaktest.AfterTest(t)() 752 753 for _, engineImpl := range mvccEngineImpls { 754 t.Run(engineImpl.name, func(t *testing.T) { 755 engine := engineImpl.create() 756 defer engine.Close() 757 758 testCases := []struct { 759 key MVCCKey 760 value []byte 761 }{ 762 {mvccKey("dog"), []byte("woof")}, 763 {mvccKey("cat"), []byte("meow")}, 764 {mvccKey("server"), []byte("42")}, 765 {mvccKey("french"), []byte("Allô?")}, 766 {mvccKey("german"), []byte("hallo")}, 767 {mvccKey("chinese"), []byte("你好")}, 768 } 769 keyMap := map[string][]byte{} 770 for _, c := range testCases { 771 if err := engine.Put(c.key, c.value); err != nil { 772 t.Errorf("could not put key %q: %+v", c.key, err) 773 } 774 keyMap[string(c.key.Key)] = c.value 775 } 776 sortedKeys := make([]string, len(testCases)) 777 for i, t := range testCases { 778 sortedKeys[i] = string(t.key.Key) 779 } 780 sort.Strings(sortedKeys) 781 782 keyvals, err := Scan(engine, roachpb.Key("chinese"), roachpb.Key("german"), 0) 783 if err != nil { 784 t.Fatalf("could not run scan: %+v", err) 785 } 786 ensureRangeEqual(t, sortedKeys[1:4], keyMap, keyvals) 787 788 // Check an end of range which does not equal an existing key. 789 keyvals, err = Scan(engine, roachpb.Key("chinese"), roachpb.Key("german1"), 0) 790 if err != nil { 791 t.Fatalf("could not run scan: %+v", err) 792 } 793 ensureRangeEqual(t, sortedKeys[1:5], keyMap, keyvals) 794 795 keyvals, err = Scan(engine, roachpb.Key("chinese"), roachpb.Key("german"), 2) 796 if err != nil { 797 t.Fatalf("could not run scan: %+v", err) 798 } 799 ensureRangeEqual(t, sortedKeys[1:3], keyMap, keyvals) 800 801 // Should return all key/value pairs in lexicographic order. Note that "" 802 // is the lowest key possible and is a special case in engine.scan, that's 803 // why we test it here. 804 startKeys := []roachpb.Key{roachpb.Key("cat"), roachpb.Key("")} 805 for _, startKey := range startKeys { 806 keyvals, err = Scan(engine, startKey, roachpb.KeyMax, 0) 807 if err != nil { 808 t.Fatalf("could not run scan: %+v", err) 809 } 810 ensureRangeEqual(t, sortedKeys, keyMap, keyvals) 811 } 812 }) 813 } 814 } 815 816 func verifyScan(start, end roachpb.Key, max int64, expKeys []MVCCKey, engine Engine, t *testing.T) { 817 kvs, err := Scan(engine, start, end, max) 818 if err != nil { 819 t.Errorf("scan %q-%q: expected no error, but got %s", start, end, err) 820 } 821 if len(kvs) != len(expKeys) { 822 t.Errorf("scan %q-%q: expected scanned keys mismatch %d != %d: %v", 823 start, end, len(kvs), len(expKeys), kvs) 824 } 825 for i, kv := range kvs { 826 if !kv.Key.Equal(expKeys[i]) { 827 t.Errorf("scan %q-%q: expected keys equal %q != %q", start, end, kv.Key, expKeys[i]) 828 } 829 } 830 } 831 832 func TestEngineScan2(t *testing.T) { 833 defer leaktest.AfterTest(t)() 834 // TODO(Tobias): Merge this with TestEngineScan1 and remove 835 // either verifyScan or the other helper function. 836 837 for _, engineImpl := range mvccEngineImpls { 838 t.Run(engineImpl.name, func(t *testing.T) { 839 engine := engineImpl.create() 840 defer engine.Close() 841 842 keys := []MVCCKey{ 843 mvccKey("a"), 844 mvccKey("aa"), 845 mvccKey("aaa"), 846 mvccKey("ab"), 847 mvccKey("abc"), 848 mvccKey(roachpb.RKeyMax), 849 } 850 851 insertKeys(keys, engine, t) 852 853 // Scan all keys (non-inclusive of final key). 854 verifyScan(roachpb.KeyMin, roachpb.KeyMax, 10, keys[:5], engine, t) 855 verifyScan(roachpb.Key("a"), roachpb.KeyMax, 10, keys[:5], engine, t) 856 857 // Scan sub range. 858 verifyScan(roachpb.Key("aab"), roachpb.Key("abcc"), 10, keys[3:5], engine, t) 859 verifyScan(roachpb.Key("aa0"), roachpb.Key("abcc"), 10, keys[2:5], engine, t) 860 861 // Scan with max values. 862 verifyScan(roachpb.KeyMin, roachpb.KeyMax, 3, keys[:3], engine, t) 863 verifyScan(roachpb.Key("a0"), roachpb.KeyMax, 3, keys[1:4], engine, t) 864 865 // Scan with max value 0 gets all values. 866 verifyScan(roachpb.KeyMin, roachpb.KeyMax, 0, keys[:5], engine, t) 867 }) 868 } 869 } 870 871 func testEngineDeleteRange(t *testing.T, clearRange func(engine Engine, start, end MVCCKey) error) { 872 for _, engineImpl := range mvccEngineImpls { 873 t.Run(engineImpl.name, func(t *testing.T) { 874 engine := engineImpl.create() 875 defer engine.Close() 876 877 keys := []MVCCKey{ 878 mvccKey("a"), 879 mvccKey("aa"), 880 mvccKey("aaa"), 881 mvccKey("ab"), 882 mvccKey("abc"), 883 mvccKey(roachpb.RKeyMax), 884 } 885 886 insertKeys(keys, engine, t) 887 888 // Scan all keys (non-inclusive of final key). 889 verifyScan(roachpb.KeyMin, roachpb.KeyMax, 10, keys[:5], engine, t) 890 891 // Delete a range of keys 892 if err := clearRange(engine, mvccKey("aa"), mvccKey("abc")); err != nil { 893 t.Fatal(err) 894 } 895 // Verify what's left 896 verifyScan(roachpb.KeyMin, roachpb.KeyMax, 10, 897 []MVCCKey{mvccKey("a"), mvccKey("abc")}, engine, t) 898 }) 899 } 900 } 901 902 func TestEngineDeleteRange(t *testing.T) { 903 defer leaktest.AfterTest(t)() 904 testEngineDeleteRange(t, func(engine Engine, start, end MVCCKey) error { 905 return engine.ClearRange(start, end) 906 }) 907 } 908 909 func TestEngineDeleteRangeBatch(t *testing.T) { 910 defer leaktest.AfterTest(t)() 911 testEngineDeleteRange(t, func(engine Engine, start, end MVCCKey) error { 912 batch := engine.NewWriteOnlyBatch() 913 defer batch.Close() 914 if err := batch.ClearRange(start, end); err != nil { 915 return err 916 } 917 batch2 := engine.NewWriteOnlyBatch() 918 defer batch2.Close() 919 if err := batch2.ApplyBatchRepr(batch.Repr(), false); err != nil { 920 return err 921 } 922 return batch2.Commit(false) 923 }) 924 } 925 926 func TestEngineDeleteIterRange(t *testing.T) { 927 defer leaktest.AfterTest(t)() 928 testEngineDeleteRange(t, func(engine Engine, start, end MVCCKey) error { 929 iter := engine.NewIterator(IterOptions{UpperBound: roachpb.KeyMax}) 930 defer iter.Close() 931 return engine.ClearIterRange(iter, start.Key, end.Key) 932 }) 933 } 934 935 func TestSnapshot(t *testing.T) { 936 defer leaktest.AfterTest(t)() 937 938 for _, engineImpl := range mvccEngineImpls { 939 t.Run(engineImpl.name, func(t *testing.T) { 940 engine := engineImpl.create() 941 defer engine.Close() 942 943 key := mvccKey("a") 944 val1 := []byte("1") 945 if err := engine.Put(key, val1); err != nil { 946 t.Fatal(err) 947 } 948 val, _ := engine.Get(key) 949 if !bytes.Equal(val, val1) { 950 t.Fatalf("the value %s in get result does not match the value %s in request", 951 val, val1) 952 } 953 954 snap := engine.NewSnapshot() 955 defer snap.Close() 956 957 val2 := []byte("2") 958 if err := engine.Put(key, val2); err != nil { 959 t.Fatal(err) 960 } 961 val, _ = engine.Get(key) 962 valSnapshot, error := snap.Get(key) 963 if error != nil { 964 t.Fatalf("error : %s", error) 965 } 966 if !bytes.Equal(val, val2) { 967 t.Fatalf("the value %s in get result does not match the value %s in request", 968 val, val2) 969 } 970 if !bytes.Equal(valSnapshot, val1) { 971 t.Fatalf("the value %s in get result does not match the value %s in request", 972 valSnapshot, val1) 973 } 974 975 keyvals, _ := Scan(engine, key.Key, roachpb.KeyMax, 0) 976 keyvalsSnapshot, error := Scan(snap, key.Key, roachpb.KeyMax, 0) 977 if error != nil { 978 t.Fatalf("error : %s", error) 979 } 980 if len(keyvals) != 1 || !bytes.Equal(keyvals[0].Value, val2) { 981 t.Fatalf("the value %s in get result does not match the value %s in request", 982 keyvals[0].Value, val2) 983 } 984 if len(keyvalsSnapshot) != 1 || !bytes.Equal(keyvalsSnapshot[0].Value, val1) { 985 t.Fatalf("the value %s in get result does not match the value %s in request", 986 keyvalsSnapshot[0].Value, val1) 987 } 988 }) 989 } 990 } 991 992 // TestSnapshotMethods verifies that snapshots allow only read-only 993 // engine operations. 994 func TestSnapshotMethods(t *testing.T) { 995 defer leaktest.AfterTest(t)() 996 997 for _, engineImpl := range mvccEngineImpls { 998 t.Run(engineImpl.name, func(t *testing.T) { 999 engine := engineImpl.create() 1000 defer engine.Close() 1001 1002 keys := []MVCCKey{mvccKey("a"), mvccKey("b")} 1003 vals := [][]byte{[]byte("1"), []byte("2")} 1004 for i := range keys { 1005 if err := engine.Put(keys[i], vals[i]); err != nil { 1006 t.Fatal(err) 1007 } 1008 if val, err := engine.Get(keys[i]); err != nil { 1009 t.Fatal(err) 1010 } else if !bytes.Equal(vals[i], val) { 1011 t.Fatalf("expected %s, but found %s", vals[i], val) 1012 } 1013 } 1014 snap := engine.NewSnapshot() 1015 defer snap.Close() 1016 1017 // Verify Get. 1018 for i := range keys { 1019 valSnapshot, err := snap.Get(keys[i]) 1020 if err != nil { 1021 t.Fatal(err) 1022 } 1023 if !bytes.Equal(vals[i], valSnapshot) { 1024 t.Fatalf("the value %s in get result does not match the value %s in snapshot", 1025 vals[i], valSnapshot) 1026 } 1027 } 1028 1029 // Verify Scan. 1030 keyvals, _ := Scan(engine, roachpb.KeyMin, roachpb.KeyMax, 0) 1031 keyvalsSnapshot, err := Scan(snap, roachpb.KeyMin, roachpb.KeyMax, 0) 1032 if err != nil { 1033 t.Fatal(err) 1034 } 1035 if !reflect.DeepEqual(keyvals, keyvalsSnapshot) { 1036 t.Fatalf("the key/values %v in scan result does not match the value %s in snapshot", 1037 keyvals, keyvalsSnapshot) 1038 } 1039 1040 // Verify Iterate. 1041 index := 0 1042 if err := snap.Iterate(roachpb.KeyMin, roachpb.KeyMax, func(kv MVCCKeyValue) (bool, error) { 1043 if !kv.Key.Equal(keys[index]) || !bytes.Equal(kv.Value, vals[index]) { 1044 t.Errorf("%d: key/value not equal between expected and snapshot: %s/%s, %s/%s", 1045 index, keys[index], vals[index], kv.Key, kv.Value) 1046 } 1047 index++ 1048 return false, nil 1049 }); err != nil { 1050 t.Fatal(err) 1051 } 1052 1053 // Write a new key to engine. 1054 newKey := mvccKey("c") 1055 newVal := []byte("3") 1056 if err := engine.Put(newKey, newVal); err != nil { 1057 t.Fatal(err) 1058 } 1059 1060 // Verify NewIterator still iterates over original snapshot. 1061 iter := snap.NewIterator(IterOptions{UpperBound: roachpb.KeyMax}) 1062 iter.SeekGE(newKey) 1063 if ok, err := iter.Valid(); err != nil { 1064 t.Fatal(err) 1065 } else if ok { 1066 t.Error("expected invalid iterator when seeking to element which shouldn't be visible to snapshot") 1067 } 1068 iter.Close() 1069 }) 1070 } 1071 } 1072 1073 func insertKeys(keys []MVCCKey, engine Engine, t *testing.T) { 1074 insertKeysAndValues(keys, nil, engine, t) 1075 } 1076 1077 func insertKeysAndValues(keys []MVCCKey, values [][]byte, engine Engine, t *testing.T) { 1078 // Add keys to store in random order (make sure they sort!). 1079 order := rand.Perm(len(keys)) 1080 for _, idx := range order { 1081 var val []byte 1082 if idx < len(values) { 1083 val = values[idx] 1084 } else { 1085 val = []byte("value") 1086 } 1087 if err := engine.Put(keys[idx], val); err != nil { 1088 t.Errorf("put: expected no error, but got %s", err) 1089 } 1090 } 1091 } 1092 1093 func TestCreateCheckpoint(t *testing.T) { 1094 defer leaktest.AfterTest(t)() 1095 1096 dir, cleanup := testutils.TempDir(t) 1097 defer cleanup() 1098 1099 rocksDB, err := NewRocksDB( 1100 RocksDBConfig{ 1101 StorageConfig: base.StorageConfig{ 1102 Settings: cluster.MakeTestingClusterSettings(), 1103 Dir: dir, 1104 }, 1105 }, 1106 RocksDBCache{}, 1107 ) 1108 1109 db := Engine(rocksDB) // be impl neutral from now on 1110 defer db.Close() 1111 1112 dir = filepath.Join(dir, "checkpoint") 1113 1114 assert.NoError(t, err) 1115 assert.NoError(t, db.CreateCheckpoint(dir)) 1116 assert.DirExists(t, dir) 1117 m, err := filepath.Glob(dir + "/*") 1118 assert.NoError(t, err) 1119 assert.True(t, len(m) > 0) 1120 if err := db.CreateCheckpoint(dir); !testutils.IsError(err, "exists") { 1121 t.Fatal(err) 1122 } 1123 } 1124 1125 func TestIngestDelayLimit(t *testing.T) { 1126 defer leaktest.AfterTest(t)() 1127 s := cluster.MakeTestingClusterSettings() 1128 1129 max, ramp := time.Second*5, time.Second*5/10 1130 1131 for _, tc := range []struct { 1132 exp time.Duration 1133 stats Stats 1134 }{ 1135 {0, Stats{}}, 1136 {0, Stats{L0FileCount: 19}}, 1137 {0, Stats{L0FileCount: 20}}, 1138 {ramp, Stats{L0FileCount: 21}}, 1139 {ramp * 2, Stats{L0FileCount: 22}}, 1140 {max, Stats{L0FileCount: 55}}, 1141 } { 1142 require.Equal(t, tc.exp, calculatePreIngestDelay(s, &tc.stats)) 1143 } 1144 } 1145 1146 type stringSorter []string 1147 1148 func (s stringSorter) Len() int { return len(s) } 1149 func (s stringSorter) Swap(i int, j int) { s[i], s[j] = s[j], s[i] } 1150 func (s stringSorter) Less(i int, j int) bool { return strings.Compare(s[i], s[j]) < 0 } 1151 1152 func TestEngineFS(t *testing.T) { 1153 defer leaktest.AfterTest(t)() 1154 1155 for _, engineImpl := range mvccEngineImpls { 1156 t.Run(engineImpl.name, func(t *testing.T) { 1157 e := engineImpl.create() 1158 defer e.Close() 1159 1160 testCases := []string{ 1161 "1a: f = create /bar", 1162 "1b: f.write abcdefghijklmnopqrstuvwxyz", 1163 "1c: f.close", 1164 "2a: f = open /bar", 1165 "2b: f.read 5 == abcde", 1166 "2c: f.readat 2 1 == bc", 1167 "2d: f.readat 5 20 == uvwxy", 1168 "2e: f.close", 1169 "3a: link /bar /baz", 1170 "3b: f = open /baz", 1171 "3c: f.read 5 == abcde", 1172 "3d: f.close", 1173 "4a: delete /bar", 1174 "4b: f = open /baz", 1175 "4c: f.read 5 == abcde", 1176 "4d: f.close", 1177 "4e: open /bar [does-not-exist]", 1178 "5a: rename /baz /foo", 1179 "5b: f = open /foo", 1180 "5c: f.readat 5 20 == uvwxy", 1181 "5d: f.close", 1182 "5e: open /baz [does-not-exist]", 1183 "6a: f = create /red", 1184 "6b: f.write blue", 1185 "6c: f.sync", 1186 "6d: f.close", 1187 "7a: f = opendir /", 1188 "7b: f.sync", 1189 "7c: f.close", 1190 "8a: f = create-with-sync /bar", 1191 "8b: f.write ghe", 1192 "8c: f.close", 1193 "8d: f = open /bar", 1194 "8e: f.read 3 == ghe", 1195 "9a: create-dir /dir1", 1196 "9b: create /dir1/bar", 1197 "9c: list-dir /dir1 == bar", 1198 "9d: create /dir1/baz", 1199 "9e: list-dir /dir1 == bar,baz", 1200 "9f: delete /dir1/bar", 1201 "9g: delete /dir1/baz", 1202 "9h: delete-dir /dir1", 1203 } 1204 1205 var f fs.File 1206 for _, tc := range testCases { 1207 s := strings.Split(tc, " ")[1:] 1208 1209 saveF := s[0] == "f" && s[1] == "=" 1210 if saveF { 1211 s = s[2:] 1212 } 1213 1214 fails := s[len(s)-1][0] == '[' 1215 var errorStr string 1216 if fails { 1217 errorStr = s[len(s)-1][1:] 1218 errorStr = errorStr[:len(errorStr)-1] 1219 s = s[:len(s)-1] 1220 } 1221 1222 var ( 1223 g fs.File 1224 err error 1225 ) 1226 switch s[0] { 1227 case "create": 1228 g, err = e.Create(s[1]) 1229 case "create-with-sync": 1230 g, err = e.CreateWithSync(s[1], 1) 1231 case "link": 1232 err = e.Link(s[1], s[2]) 1233 case "open": 1234 g, err = e.Open(s[1]) 1235 case "opendir": 1236 g, err = e.OpenDir(s[1]) 1237 case "delete": 1238 err = e.Remove(s[1]) 1239 case "rename": 1240 err = e.Rename(s[1], s[2]) 1241 case "create-dir": 1242 err = e.MkdirAll(s[1]) 1243 case "delete-dir": 1244 err = e.RemoveDir(s[1]) 1245 case "list-dir": 1246 result, err := e.List(s[1]) 1247 if err != nil { 1248 break 1249 } 1250 sort.Sort(stringSorter(result)) 1251 got := strings.Join(result, ",") 1252 want := s[3] 1253 if got != want { 1254 t.Fatalf("%q: got %s, want %s", tc, got, want) 1255 } 1256 case "f.write": 1257 _, err = f.Write([]byte(s[1])) 1258 case "f.read": 1259 n, _ := strconv.Atoi(s[1]) 1260 buf := make([]byte, n) 1261 _, err = io.ReadFull(f, buf) 1262 if err != nil { 1263 break 1264 } 1265 if got, want := string(buf), s[3]; got != want { 1266 t.Fatalf("%q: got %q, want %q", tc, got, want) 1267 } 1268 case "f.readat": 1269 n, _ := strconv.Atoi(s[1]) 1270 off, _ := strconv.Atoi(s[2]) 1271 buf := make([]byte, n) 1272 _, err = f.ReadAt(buf, int64(off)) 1273 if err != nil { 1274 break 1275 } 1276 if got, want := string(buf), s[4]; got != want { 1277 t.Fatalf("%q: got %q, want %q", tc, got, want) 1278 } 1279 case "f.close": 1280 f, err = nil, f.Close() 1281 case "f.sync": 1282 err = f.Sync() 1283 default: 1284 t.Fatalf("bad test case: %q", tc) 1285 } 1286 1287 if saveF { 1288 f, g = g, nil 1289 } else if g != nil { 1290 g.Close() 1291 } 1292 1293 if fails { 1294 if err == nil { 1295 t.Fatalf("%q: got nil error, want non-nil %s", tc, errorStr) 1296 } 1297 var actualErrStr string 1298 if os.IsExist(err) { 1299 actualErrStr = "exists" 1300 } else if os.IsNotExist(err) { 1301 actualErrStr = "does-not-exist" 1302 } else { 1303 actualErrStr = "error" 1304 } 1305 if errorStr != actualErrStr { 1306 t.Fatalf("%q: got %s, want %s", tc, actualErrStr, errorStr) 1307 } 1308 } else { 1309 if err != nil { 1310 t.Fatalf("%q: %v", tc, err) 1311 } 1312 } 1313 } 1314 }) 1315 } 1316 } 1317 1318 type engineImpl struct { 1319 name string 1320 create func(*testing.T, string) Engine 1321 } 1322 1323 // These FS implementations are not in-memory. 1324 var engineRealFSImpls = []engineImpl{ 1325 {"rocksdb", func(t *testing.T, dir string) Engine { 1326 db, err := NewRocksDB( 1327 RocksDBConfig{ 1328 StorageConfig: base.StorageConfig{ 1329 Settings: cluster.MakeTestingClusterSettings(), 1330 Dir: dir, 1331 }, 1332 }, 1333 RocksDBCache{}, 1334 ) 1335 if err != nil { 1336 t.Fatalf("could not create new rocksdb instance at %s: %+v", dir, err) 1337 } 1338 return db 1339 }}, 1340 {"pebble", func(t *testing.T, dir string) Engine { 1341 1342 opts := DefaultPebbleOptions() 1343 opts.FS = vfs.Default 1344 opts.Cache = pebble.NewCache(testCacheSize) 1345 defer opts.Cache.Unref() 1346 1347 db, err := NewPebble( 1348 context.Background(), 1349 PebbleConfig{ 1350 StorageConfig: base.StorageConfig{ 1351 Dir: dir, 1352 }, 1353 Opts: opts, 1354 }) 1355 if err != nil { 1356 t.Fatalf("could not create new pebble instance at %s: %+v", dir, err) 1357 } 1358 return db 1359 }}, 1360 } 1361 1362 func TestEngineFSFileNotFoundError(t *testing.T) { 1363 defer leaktest.AfterTest(t)() 1364 1365 for _, engineImpl := range engineRealFSImpls { 1366 t.Run(engineImpl.name, func(t *testing.T) { 1367 dir, dirCleanup := testutils.TempDir(t) 1368 defer dirCleanup() 1369 db := engineImpl.create(t, dir) 1370 defer db.Close() 1371 1372 // Verify Remove returns os.ErrNotExist if file does not exist. 1373 if err := db.Remove("/non/existent/file"); !os.IsNotExist(err) { 1374 t.Fatalf("expected IsNotExist, but got %v (%T)", err, err) 1375 } 1376 1377 // Verify RemoveAll returns nil if path does not exist. 1378 if err := db.RemoveAll("/non/existent/file"); err != nil { 1379 t.Fatalf("expected nil, but got %v (%T)", err, err) 1380 } 1381 1382 fname := filepath.Join(dir, "random.file") 1383 data := "random data" 1384 if f, err := db.Create(fname); err != nil { 1385 t.Fatalf("unable to open file with filename %s, got err %v", fname, err) 1386 } else { 1387 // Write data to file so we can read it later. 1388 if _, err := f.Write([]byte(data)); err != nil { 1389 t.Fatalf("error writing data: '%s' to file %s, got err %v", data, fname, err) 1390 } 1391 if err := f.Sync(); err != nil { 1392 t.Fatalf("error syncing data, got err %v", err) 1393 } 1394 if err := f.Close(); err != nil { 1395 t.Fatalf("error closing file %s, got err %v", fname, err) 1396 } 1397 } 1398 1399 if b, err := db.ReadFile(fname); err != nil { 1400 t.Errorf("unable to read file with filename %s, got err %v", fname, err) 1401 } else if string(b) != data { 1402 t.Errorf("expected content in %s is '%s', got '%s'", fname, data, string(b)) 1403 } 1404 1405 if err := db.Remove(fname); err != nil { 1406 t.Errorf("unable to delete file with filename %s, got err %v", fname, err) 1407 } 1408 1409 // Verify ReadFile returns os.ErrNotExist if reading an already deleted file. 1410 if _, err := db.ReadFile(fname); !os.IsNotExist(err) { 1411 t.Fatalf("expected IsNotExist, but got %v (%T)", err, err) 1412 } 1413 1414 // Verify Remove returns os.ErrNotExist if deleting an already deleted file. 1415 if err := db.Remove(fname); !os.IsNotExist(err) { 1416 t.Fatalf("expected IsNotExist, but got %v (%T)", err, err) 1417 } 1418 }) 1419 } 1420 } 1421 1422 func TestFS(t *testing.T) { 1423 defer leaktest.AfterTest(t)() 1424 1425 var engineImpls []engineImpl 1426 engineImpls = append(engineImpls, engineRealFSImpls...) 1427 engineImpls = append(engineImpls, 1428 engineImpl{ 1429 name: "rocksdb_mem", 1430 create: func(_ *testing.T, _ string) Engine { 1431 return createTestRocksDBEngine() 1432 }, 1433 }, 1434 engineImpl{ 1435 name: "pebble_mem", 1436 create: func(_ *testing.T, _ string) Engine { 1437 return createTestPebbleEngine() 1438 }, 1439 }) 1440 1441 for _, impl := range engineImpls { 1442 t.Run(impl.name, func(t *testing.T) { 1443 dir, cleanupDir := testutils.TempDir(t) 1444 defer cleanupDir() 1445 fs := impl.create(t, dir) 1446 defer fs.Close() 1447 1448 path := func(rel string) string { 1449 return filepath.Join(dir, rel) 1450 } 1451 expectLS := func(dir string, want []string) { 1452 t.Helper() 1453 1454 got, err := fs.List(dir) 1455 require.NoError(t, err) 1456 if !reflect.DeepEqual(got, want) { 1457 t.Fatalf("fs.List(%q) = %#v, want %#v", dir, got, want) 1458 } 1459 } 1460 1461 // Create a/ and assert that it's empty. 1462 require.NoError(t, fs.MkdirAll(path("a"))) 1463 expectLS(path("a"), []string{}) 1464 1465 // Create a/b/ and a/b/c/ in a single MkdirAll call. 1466 // Then ensure that a duplicate call returns a nil error. 1467 require.NoError(t, fs.MkdirAll(path("a/b/c"))) 1468 require.NoError(t, fs.MkdirAll(path("a/b/c"))) 1469 expectLS(path("a"), []string{"b"}) 1470 expectLS(path("a/b"), []string{"c"}) 1471 expectLS(path("a/b/c"), []string{}) 1472 1473 // Create a file at a/b/c/foo. 1474 f, err := fs.Create(path("a/b/c/foo")) 1475 require.NoError(t, err) 1476 require.NoError(t, f.Close()) 1477 expectLS(path("a/b/c"), []string{"foo"}) 1478 1479 // Create a file at a/b/c/bar. 1480 f, err = fs.Create(path("a/b/c/bar")) 1481 require.NoError(t, err) 1482 require.NoError(t, f.Close()) 1483 expectLS(path("a/b/c"), []string{"bar", "foo"}) 1484 1485 // RemoveAll a file. 1486 require.NoError(t, fs.RemoveAll(path("a/b/c/bar"))) 1487 expectLS(path("a/b/c"), []string{"foo"}) 1488 1489 // RemoveAll a directory that contains subdirectories and 1490 // descendant files. 1491 require.NoError(t, fs.RemoveAll(path("a/b"))) 1492 expectLS(path("a"), []string{}) 1493 }) 1494 } 1495 }