github.com/btcsuite/btcd@v0.24.0/database/ffldb/interface_test.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 // This file intended to be copied into each backend driver directory. Each 6 // driver should have their own driver_test.go file which creates a database and 7 // invokes the testInterface function in this file to ensure the driver properly 8 // implements the interface. 9 // 10 // NOTE: When copying this file into the backend driver folder, the package name 11 // will need to be changed accordingly. 12 13 package ffldb_test 14 15 import ( 16 "bytes" 17 "compress/bzip2" 18 "encoding/binary" 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 "reflect" 24 "sync/atomic" 25 "testing" 26 "time" 27 28 "github.com/btcsuite/btcd/btcutil" 29 "github.com/btcsuite/btcd/chaincfg" 30 "github.com/btcsuite/btcd/chaincfg/chainhash" 31 "github.com/btcsuite/btcd/database" 32 "github.com/btcsuite/btcd/wire" 33 ) 34 35 var ( 36 // blockDataNet is the expected network in the test block data. 37 blockDataNet = wire.MainNet 38 39 // blockDataFile is the path to a file containing the first 256 blocks 40 // of the block chain. 41 blockDataFile = filepath.Join("..", "testdata", "blocks1-256.bz2") 42 43 // errSubTestFail is used to signal that a sub test returned false. 44 errSubTestFail = fmt.Errorf("sub test failure") 45 ) 46 47 // loadBlocks loads the blocks contained in the testdata directory and returns 48 // a slice of them. 49 func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*btcutil.Block, error) { 50 // Open the file that contains the blocks for reading. 51 fi, err := os.Open(dataFile) 52 if err != nil { 53 t.Errorf("failed to open file %v, err %v", dataFile, err) 54 return nil, err 55 } 56 defer func() { 57 if err := fi.Close(); err != nil { 58 t.Errorf("failed to close file %v %v", dataFile, 59 err) 60 } 61 }() 62 dr := bzip2.NewReader(fi) 63 64 // Set the first block as the genesis block. 65 blocks := make([]*btcutil.Block, 0, 256) 66 genesis := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) 67 blocks = append(blocks, genesis) 68 69 // Load the remaining blocks. 70 for height := 1; ; height++ { 71 var net uint32 72 err := binary.Read(dr, binary.LittleEndian, &net) 73 if err == io.EOF { 74 // Hit end of file at the expected offset. No error. 75 break 76 } 77 if err != nil { 78 t.Errorf("Failed to load network type for block %d: %v", 79 height, err) 80 return nil, err 81 } 82 if net != uint32(network) { 83 t.Errorf("Block doesn't match network: %v expects %v", 84 net, network) 85 return nil, err 86 } 87 88 var blockLen uint32 89 err = binary.Read(dr, binary.LittleEndian, &blockLen) 90 if err != nil { 91 t.Errorf("Failed to load block size for block %d: %v", 92 height, err) 93 return nil, err 94 } 95 96 // Read the block. 97 blockBytes := make([]byte, blockLen) 98 _, err = io.ReadFull(dr, blockBytes) 99 if err != nil { 100 t.Errorf("Failed to load block %d: %v", height, err) 101 return nil, err 102 } 103 104 // Deserialize and store the block. 105 block, err := btcutil.NewBlockFromBytes(blockBytes) 106 if err != nil { 107 t.Errorf("Failed to parse block %v: %v", height, err) 108 return nil, err 109 } 110 blocks = append(blocks, block) 111 } 112 113 return blocks, nil 114 } 115 116 // checkDbError ensures the passed error is a database.Error with an error code 117 // that matches the passed error code. 118 func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool { 119 dbErr, ok := gotErr.(database.Error) 120 if !ok { 121 t.Errorf("%s: unexpected error type - got %T, want %T", 122 testName, gotErr, database.Error{}) 123 return false 124 } 125 if dbErr.ErrorCode != wantErrCode { 126 t.Errorf("%s: unexpected error code - got %s (%s), want %s", 127 testName, dbErr.ErrorCode, dbErr.Description, 128 wantErrCode) 129 return false 130 } 131 132 return true 133 } 134 135 // testContext is used to store context information about a running test which 136 // is passed into helper functions. 137 type testContext struct { 138 t *testing.T 139 db database.DB 140 bucketDepth int 141 isWritable bool 142 blocks []*btcutil.Block 143 } 144 145 // keyPair houses a key/value pair. It is used over maps so ordering can be 146 // maintained. 147 type keyPair struct { 148 key []byte 149 value []byte 150 } 151 152 // lookupKey is a convenience method to lookup the requested key from the 153 // provided keypair slice along with whether or not the key was found. 154 func lookupKey(key []byte, values []keyPair) ([]byte, bool) { 155 for _, item := range values { 156 if bytes.Equal(item.key, key) { 157 return item.value, true 158 } 159 } 160 161 return nil, false 162 } 163 164 // toGetValues returns a copy of the provided keypairs with all of the nil 165 // values set to an empty byte slice. This is used to ensure that keys set to 166 // nil values result in empty byte slices when retrieved instead of nil. 167 func toGetValues(values []keyPair) []keyPair { 168 ret := make([]keyPair, len(values)) 169 copy(ret, values) 170 for i := range ret { 171 if ret[i].value == nil { 172 ret[i].value = make([]byte, 0) 173 } 174 } 175 return ret 176 } 177 178 // rollbackValues returns a copy of the provided keypairs with all values set to 179 // nil. This is used to test that values are properly rolled back. 180 func rollbackValues(values []keyPair) []keyPair { 181 ret := make([]keyPair, len(values)) 182 copy(ret, values) 183 for i := range ret { 184 ret[i].value = nil 185 } 186 return ret 187 } 188 189 // testCursorKeyPair checks that the provide key and value match the expected 190 // keypair at the provided index. It also ensures the index is in range for the 191 // provided slice of expected keypairs. 192 func testCursorKeyPair(tc *testContext, k, v []byte, index int, values []keyPair) bool { 193 if index >= len(values) || index < 0 { 194 tc.t.Errorf("Cursor: exceeded the expected range of values - "+ 195 "index %d, num values %d", index, len(values)) 196 return false 197 } 198 199 pair := &values[index] 200 if !bytes.Equal(k, pair.key) { 201 tc.t.Errorf("Mismatched cursor key: index %d does not match "+ 202 "the expected key - got %q, want %q", index, k, 203 pair.key) 204 return false 205 } 206 if !bytes.Equal(v, pair.value) { 207 tc.t.Errorf("Mismatched cursor value: index %d does not match "+ 208 "the expected value - got %q, want %q", index, v, 209 pair.value) 210 return false 211 } 212 213 return true 214 } 215 216 // testGetValues checks that all of the provided key/value pairs can be 217 // retrieved from the database and the retrieved values match the provided 218 // values. 219 func testGetValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { 220 for _, item := range values { 221 gotValue := bucket.Get(item.key) 222 if !reflect.DeepEqual(gotValue, item.value) { 223 tc.t.Errorf("Get: unexpected value for %q - got %q, "+ 224 "want %q", item.key, gotValue, item.value) 225 return false 226 } 227 } 228 229 return true 230 } 231 232 // testPutValues stores all of the provided key/value pairs in the provided 233 // bucket while checking for errors. 234 func testPutValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { 235 for _, item := range values { 236 if err := bucket.Put(item.key, item.value); err != nil { 237 tc.t.Errorf("Put: unexpected error: %v", err) 238 return false 239 } 240 } 241 242 return true 243 } 244 245 // testDeleteValues removes all of the provided key/value pairs from the 246 // provided bucket. 247 func testDeleteValues(tc *testContext, bucket database.Bucket, values []keyPair) bool { 248 for _, item := range values { 249 if err := bucket.Delete(item.key); err != nil { 250 tc.t.Errorf("Delete: unexpected error: %v", err) 251 return false 252 } 253 } 254 255 return true 256 } 257 258 // testCursorInterface ensures the cursor itnerface is working properly by 259 // exercising all of its functions on the passed bucket. 260 func testCursorInterface(tc *testContext, bucket database.Bucket) bool { 261 // Ensure a cursor can be obtained for the bucket. 262 cursor := bucket.Cursor() 263 if cursor == nil { 264 tc.t.Error("Bucket.Cursor: unexpected nil cursor returned") 265 return false 266 } 267 268 // Ensure the cursor returns the same bucket it was created for. 269 if cursor.Bucket() != bucket { 270 tc.t.Error("Cursor.Bucket: does not match the bucket it was " + 271 "created for") 272 return false 273 } 274 275 if tc.isWritable { 276 unsortedValues := []keyPair{ 277 {[]byte("cursor"), []byte("val1")}, 278 {[]byte("abcd"), []byte("val2")}, 279 {[]byte("bcd"), []byte("val3")}, 280 {[]byte("defg"), nil}, 281 } 282 sortedValues := []keyPair{ 283 {[]byte("abcd"), []byte("val2")}, 284 {[]byte("bcd"), []byte("val3")}, 285 {[]byte("cursor"), []byte("val1")}, 286 {[]byte("defg"), nil}, 287 } 288 289 // Store the values to be used in the cursor tests in unsorted 290 // order and ensure they were actually stored. 291 if !testPutValues(tc, bucket, unsortedValues) { 292 return false 293 } 294 if !testGetValues(tc, bucket, toGetValues(unsortedValues)) { 295 return false 296 } 297 298 // Ensure the cursor returns all items in byte-sorted order when 299 // iterating forward. 300 curIdx := 0 301 for ok := cursor.First(); ok; ok = cursor.Next() { 302 k, v := cursor.Key(), cursor.Value() 303 if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { 304 return false 305 } 306 curIdx++ 307 } 308 if curIdx != len(unsortedValues) { 309 tc.t.Errorf("Cursor: expected to iterate %d values, "+ 310 "but only iterated %d", len(unsortedValues), 311 curIdx) 312 return false 313 } 314 315 // Ensure the cursor returns all items in reverse byte-sorted 316 // order when iterating in reverse. 317 curIdx = len(sortedValues) - 1 318 for ok := cursor.Last(); ok; ok = cursor.Prev() { 319 k, v := cursor.Key(), cursor.Value() 320 if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { 321 return false 322 } 323 curIdx-- 324 } 325 if curIdx > -1 { 326 tc.t.Errorf("Reverse cursor: expected to iterate %d "+ 327 "values, but only iterated %d", 328 len(sortedValues), len(sortedValues)-(curIdx+1)) 329 return false 330 } 331 332 // Ensure forward iteration works as expected after seeking. 333 middleIdx := (len(sortedValues) - 1) / 2 334 seekKey := sortedValues[middleIdx].key 335 curIdx = middleIdx 336 for ok := cursor.Seek(seekKey); ok; ok = cursor.Next() { 337 k, v := cursor.Key(), cursor.Value() 338 if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { 339 return false 340 } 341 curIdx++ 342 } 343 if curIdx != len(sortedValues) { 344 tc.t.Errorf("Cursor after seek: expected to iterate "+ 345 "%d values, but only iterated %d", 346 len(sortedValues)-middleIdx, curIdx-middleIdx) 347 return false 348 } 349 350 // Ensure reverse iteration works as expected after seeking. 351 curIdx = middleIdx 352 for ok := cursor.Seek(seekKey); ok; ok = cursor.Prev() { 353 k, v := cursor.Key(), cursor.Value() 354 if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) { 355 return false 356 } 357 curIdx-- 358 } 359 if curIdx > -1 { 360 tc.t.Errorf("Reverse cursor after seek: expected to "+ 361 "iterate %d values, but only iterated %d", 362 len(sortedValues)-middleIdx, middleIdx-curIdx) 363 return false 364 } 365 366 // Ensure the cursor deletes items properly. 367 if !cursor.First() { 368 tc.t.Errorf("Cursor.First: no value") 369 return false 370 } 371 k := cursor.Key() 372 if err := cursor.Delete(); err != nil { 373 tc.t.Errorf("Cursor.Delete: unexpected error: %v", err) 374 return false 375 } 376 if val := bucket.Get(k); val != nil { 377 tc.t.Errorf("Cursor.Delete: value for key %q was not "+ 378 "deleted", k) 379 return false 380 } 381 } 382 383 return true 384 } 385 386 // testNestedBucket reruns the testBucketInterface against a nested bucket along 387 // with a counter to only test a couple of level deep. 388 func testNestedBucket(tc *testContext, testBucket database.Bucket) bool { 389 // Don't go more than 2 nested levels deep. 390 if tc.bucketDepth > 1 { 391 return true 392 } 393 394 tc.bucketDepth++ 395 defer func() { 396 tc.bucketDepth-- 397 }() 398 return testBucketInterface(tc, testBucket) 399 } 400 401 // testBucketInterface ensures the bucket interface is working properly by 402 // exercising all of its functions. This includes the cursor interface for the 403 // cursor returned from the bucket. 404 func testBucketInterface(tc *testContext, bucket database.Bucket) bool { 405 if bucket.Writable() != tc.isWritable { 406 tc.t.Errorf("Bucket writable state does not match.") 407 return false 408 } 409 410 if tc.isWritable { 411 // keyValues holds the keys and values to use when putting 412 // values into the bucket. 413 keyValues := []keyPair{ 414 {[]byte("bucketkey1"), []byte("foo1")}, 415 {[]byte("bucketkey2"), []byte("foo2")}, 416 {[]byte("bucketkey3"), []byte("foo3")}, 417 {[]byte("bucketkey4"), nil}, 418 } 419 expectedKeyValues := toGetValues(keyValues) 420 if !testPutValues(tc, bucket, keyValues) { 421 return false 422 } 423 424 if !testGetValues(tc, bucket, expectedKeyValues) { 425 return false 426 } 427 428 // Ensure errors returned from the user-supplied ForEach 429 // function are returned. 430 forEachError := fmt.Errorf("example foreach error") 431 err := bucket.ForEach(func(k, v []byte) error { 432 return forEachError 433 }) 434 if err != forEachError { 435 tc.t.Errorf("ForEach: inner function error not "+ 436 "returned - got %v, want %v", err, forEachError) 437 return false 438 } 439 440 // Iterate all of the keys using ForEach while making sure the 441 // stored values are the expected values. 442 keysFound := make(map[string]struct{}, len(keyValues)) 443 err = bucket.ForEach(func(k, v []byte) error { 444 wantV, found := lookupKey(k, expectedKeyValues) 445 if !found { 446 return fmt.Errorf("ForEach: key '%s' should "+ 447 "exist", k) 448 } 449 450 if !reflect.DeepEqual(v, wantV) { 451 return fmt.Errorf("ForEach: value for key '%s' "+ 452 "does not match - got %s, want %s", k, 453 v, wantV) 454 } 455 456 keysFound[string(k)] = struct{}{} 457 return nil 458 }) 459 if err != nil { 460 tc.t.Errorf("%v", err) 461 return false 462 } 463 464 // Ensure all keys were iterated. 465 for _, item := range keyValues { 466 if _, ok := keysFound[string(item.key)]; !ok { 467 tc.t.Errorf("ForEach: key '%s' was not iterated "+ 468 "when it should have been", item.key) 469 return false 470 } 471 } 472 473 // Delete the keys and ensure they were deleted. 474 if !testDeleteValues(tc, bucket, keyValues) { 475 return false 476 } 477 if !testGetValues(tc, bucket, rollbackValues(keyValues)) { 478 return false 479 } 480 481 // Ensure creating a new bucket works as expected. 482 testBucketName := []byte("testbucket") 483 testBucket, err := bucket.CreateBucket(testBucketName) 484 if err != nil { 485 tc.t.Errorf("CreateBucket: unexpected error: %v", err) 486 return false 487 } 488 if !testNestedBucket(tc, testBucket) { 489 return false 490 } 491 492 // Ensure errors returned from the user-supplied ForEachBucket 493 // function are returned. 494 err = bucket.ForEachBucket(func(k []byte) error { 495 return forEachError 496 }) 497 if err != forEachError { 498 tc.t.Errorf("ForEachBucket: inner function error not "+ 499 "returned - got %v, want %v", err, forEachError) 500 return false 501 } 502 503 // Ensure creating a bucket that already exists fails with the 504 // expected error. 505 wantErrCode := database.ErrBucketExists 506 _, err = bucket.CreateBucket(testBucketName) 507 if !checkDbError(tc.t, "CreateBucket", err, wantErrCode) { 508 return false 509 } 510 511 // Ensure CreateBucketIfNotExists returns an existing bucket. 512 testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) 513 if err != nil { 514 tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ 515 "error: %v", err) 516 return false 517 } 518 if !testNestedBucket(tc, testBucket) { 519 return false 520 } 521 522 // Ensure retrieving an existing bucket works as expected. 523 testBucket = bucket.Bucket(testBucketName) 524 if !testNestedBucket(tc, testBucket) { 525 return false 526 } 527 528 // Ensure deleting a bucket works as intended. 529 if err := bucket.DeleteBucket(testBucketName); err != nil { 530 tc.t.Errorf("DeleteBucket: unexpected error: %v", err) 531 return false 532 } 533 if b := bucket.Bucket(testBucketName); b != nil { 534 tc.t.Errorf("DeleteBucket: bucket '%s' still exists", 535 testBucketName) 536 return false 537 } 538 539 // Ensure deleting a bucket that doesn't exist returns the 540 // expected error. 541 wantErrCode = database.ErrBucketNotFound 542 err = bucket.DeleteBucket(testBucketName) 543 if !checkDbError(tc.t, "DeleteBucket", err, wantErrCode) { 544 return false 545 } 546 547 // Ensure CreateBucketIfNotExists creates a new bucket when 548 // it doesn't already exist. 549 testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) 550 if err != nil { 551 tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ 552 "error: %v", err) 553 return false 554 } 555 if !testNestedBucket(tc, testBucket) { 556 return false 557 } 558 559 // Ensure the cursor interface works as expected. 560 if !testCursorInterface(tc, testBucket) { 561 return false 562 } 563 564 // Delete the test bucket to avoid leaving it around for future 565 // calls. 566 if err := bucket.DeleteBucket(testBucketName); err != nil { 567 tc.t.Errorf("DeleteBucket: unexpected error: %v", err) 568 return false 569 } 570 if b := bucket.Bucket(testBucketName); b != nil { 571 tc.t.Errorf("DeleteBucket: bucket '%s' still exists", 572 testBucketName) 573 return false 574 } 575 } else { 576 // Put should fail with bucket that is not writable. 577 testName := "unwritable tx put" 578 wantErrCode := database.ErrTxNotWritable 579 failBytes := []byte("fail") 580 err := bucket.Put(failBytes, failBytes) 581 if !checkDbError(tc.t, testName, err, wantErrCode) { 582 return false 583 } 584 585 // Delete should fail with bucket that is not writable. 586 testName = "unwritable tx delete" 587 err = bucket.Delete(failBytes) 588 if !checkDbError(tc.t, testName, err, wantErrCode) { 589 return false 590 } 591 592 // CreateBucket should fail with bucket that is not writable. 593 testName = "unwritable tx create bucket" 594 _, err = bucket.CreateBucket(failBytes) 595 if !checkDbError(tc.t, testName, err, wantErrCode) { 596 return false 597 } 598 599 // CreateBucketIfNotExists should fail with bucket that is not 600 // writable. 601 testName = "unwritable tx create bucket if not exists" 602 _, err = bucket.CreateBucketIfNotExists(failBytes) 603 if !checkDbError(tc.t, testName, err, wantErrCode) { 604 return false 605 } 606 607 // DeleteBucket should fail with bucket that is not writable. 608 testName = "unwritable tx delete bucket" 609 err = bucket.DeleteBucket(failBytes) 610 if !checkDbError(tc.t, testName, err, wantErrCode) { 611 return false 612 } 613 614 // Ensure the cursor interface works as expected with read-only 615 // buckets. 616 if !testCursorInterface(tc, bucket) { 617 return false 618 } 619 } 620 621 return true 622 } 623 624 // rollbackOnPanic rolls the passed transaction back if the code in the calling 625 // function panics. This is useful in case the tests unexpectedly panic which 626 // would leave any manually created transactions with the database mutex locked 627 // thereby leading to a deadlock and masking the real reason for the panic. It 628 // also logs a test error and repanics so the original panic can be traced. 629 func rollbackOnPanic(t *testing.T, tx database.Tx) { 630 if err := recover(); err != nil { 631 t.Errorf("Unexpected panic: %v", err) 632 _ = tx.Rollback() 633 panic(err) 634 } 635 } 636 637 // testMetadataManualTxInterface ensures that the manual transactions metadata 638 // interface works as expected. 639 func testMetadataManualTxInterface(tc *testContext) bool { 640 // populateValues tests that populating values works as expected. 641 // 642 // When the writable flag is false, a read-only tranasction is created, 643 // standard bucket tests for read-only transactions are performed, and 644 // the Commit function is checked to ensure it fails as expected. 645 // 646 // Otherwise, a read-write transaction is created, the values are 647 // written, standard bucket tests for read-write transactions are 648 // performed, and then the transaction is either committed or rolled 649 // back depending on the flag. 650 bucket1Name := []byte("bucket1") 651 populateValues := func(writable, rollback bool, putValues []keyPair) bool { 652 tx, err := tc.db.Begin(writable) 653 if err != nil { 654 tc.t.Errorf("Begin: unexpected error %v", err) 655 return false 656 } 657 defer rollbackOnPanic(tc.t, tx) 658 659 metadataBucket := tx.Metadata() 660 if metadataBucket == nil { 661 tc.t.Errorf("Metadata: unexpected nil bucket") 662 _ = tx.Rollback() 663 return false 664 } 665 666 bucket1 := metadataBucket.Bucket(bucket1Name) 667 if bucket1 == nil { 668 tc.t.Errorf("Bucket1: unexpected nil bucket") 669 return false 670 } 671 672 tc.isWritable = writable 673 if !testBucketInterface(tc, bucket1) { 674 _ = tx.Rollback() 675 return false 676 } 677 678 if !writable { 679 // The transaction is not writable, so it should fail 680 // the commit. 681 testName := "unwritable tx commit" 682 wantErrCode := database.ErrTxNotWritable 683 err := tx.Commit() 684 if !checkDbError(tc.t, testName, err, wantErrCode) { 685 _ = tx.Rollback() 686 return false 687 } 688 } else { 689 if !testPutValues(tc, bucket1, putValues) { 690 return false 691 } 692 693 if rollback { 694 // Rollback the transaction. 695 if err := tx.Rollback(); err != nil { 696 tc.t.Errorf("Rollback: unexpected "+ 697 "error %v", err) 698 return false 699 } 700 } else { 701 // The commit should succeed. 702 if err := tx.Commit(); err != nil { 703 tc.t.Errorf("Commit: unexpected error "+ 704 "%v", err) 705 return false 706 } 707 } 708 } 709 710 return true 711 } 712 713 // checkValues starts a read-only transaction and checks that all of 714 // the key/value pairs specified in the expectedValues parameter match 715 // what's in the database. 716 checkValues := func(expectedValues []keyPair) bool { 717 tx, err := tc.db.Begin(false) 718 if err != nil { 719 tc.t.Errorf("Begin: unexpected error %v", err) 720 return false 721 } 722 defer rollbackOnPanic(tc.t, tx) 723 724 metadataBucket := tx.Metadata() 725 if metadataBucket == nil { 726 tc.t.Errorf("Metadata: unexpected nil bucket") 727 _ = tx.Rollback() 728 return false 729 } 730 731 bucket1 := metadataBucket.Bucket(bucket1Name) 732 if bucket1 == nil { 733 tc.t.Errorf("Bucket1: unexpected nil bucket") 734 return false 735 } 736 737 if !testGetValues(tc, bucket1, expectedValues) { 738 _ = tx.Rollback() 739 return false 740 } 741 742 // Rollback the read-only transaction. 743 if err := tx.Rollback(); err != nil { 744 tc.t.Errorf("Commit: unexpected error %v", err) 745 return false 746 } 747 748 return true 749 } 750 751 // deleteValues starts a read-write transaction and deletes the keys 752 // in the passed key/value pairs. 753 deleteValues := func(values []keyPair) bool { 754 tx, err := tc.db.Begin(true) 755 if err != nil { 756 757 } 758 defer rollbackOnPanic(tc.t, tx) 759 760 metadataBucket := tx.Metadata() 761 if metadataBucket == nil { 762 tc.t.Errorf("Metadata: unexpected nil bucket") 763 _ = tx.Rollback() 764 return false 765 } 766 767 bucket1 := metadataBucket.Bucket(bucket1Name) 768 if bucket1 == nil { 769 tc.t.Errorf("Bucket1: unexpected nil bucket") 770 return false 771 } 772 773 // Delete the keys and ensure they were deleted. 774 if !testDeleteValues(tc, bucket1, values) { 775 _ = tx.Rollback() 776 return false 777 } 778 if !testGetValues(tc, bucket1, rollbackValues(values)) { 779 _ = tx.Rollback() 780 return false 781 } 782 783 // Commit the changes and ensure it was successful. 784 if err := tx.Commit(); err != nil { 785 tc.t.Errorf("Commit: unexpected error %v", err) 786 return false 787 } 788 789 return true 790 } 791 792 // keyValues holds the keys and values to use when putting values into a 793 // bucket. 794 var keyValues = []keyPair{ 795 {[]byte("umtxkey1"), []byte("foo1")}, 796 {[]byte("umtxkey2"), []byte("foo2")}, 797 {[]byte("umtxkey3"), []byte("foo3")}, 798 {[]byte("umtxkey4"), nil}, 799 } 800 801 // Ensure that attempting populating the values using a read-only 802 // transaction fails as expected. 803 if !populateValues(false, true, keyValues) { 804 return false 805 } 806 if !checkValues(rollbackValues(keyValues)) { 807 return false 808 } 809 810 // Ensure that attempting populating the values using a read-write 811 // transaction and then rolling it back yields the expected values. 812 if !populateValues(true, true, keyValues) { 813 return false 814 } 815 if !checkValues(rollbackValues(keyValues)) { 816 return false 817 } 818 819 // Ensure that attempting populating the values using a read-write 820 // transaction and then committing it stores the expected values. 821 if !populateValues(true, false, keyValues) { 822 return false 823 } 824 if !checkValues(toGetValues(keyValues)) { 825 return false 826 } 827 828 // Clean up the keys. 829 if !deleteValues(keyValues) { 830 return false 831 } 832 833 return true 834 } 835 836 // testManagedTxPanics ensures calling Rollback of Commit inside a managed 837 // transaction panics. 838 func testManagedTxPanics(tc *testContext) bool { 839 testPanic := func(fn func()) (paniced bool) { 840 // Setup a defer to catch the expected panic and update the 841 // return variable. 842 defer func() { 843 if err := recover(); err != nil { 844 paniced = true 845 } 846 }() 847 848 fn() 849 return false 850 } 851 852 // Ensure calling Commit on a managed read-only transaction panics. 853 paniced := testPanic(func() { 854 tc.db.View(func(tx database.Tx) error { 855 tx.Commit() 856 return nil 857 }) 858 }) 859 if !paniced { 860 tc.t.Error("Commit called inside View did not panic") 861 return false 862 } 863 864 // Ensure calling Rollback on a managed read-only transaction panics. 865 paniced = testPanic(func() { 866 tc.db.View(func(tx database.Tx) error { 867 tx.Rollback() 868 return nil 869 }) 870 }) 871 if !paniced { 872 tc.t.Error("Rollback called inside View did not panic") 873 return false 874 } 875 876 // Ensure calling Commit on a managed read-write transaction panics. 877 paniced = testPanic(func() { 878 tc.db.Update(func(tx database.Tx) error { 879 tx.Commit() 880 return nil 881 }) 882 }) 883 if !paniced { 884 tc.t.Error("Commit called inside Update did not panic") 885 return false 886 } 887 888 // Ensure calling Rollback on a managed read-write transaction panics. 889 paniced = testPanic(func() { 890 tc.db.Update(func(tx database.Tx) error { 891 tx.Rollback() 892 return nil 893 }) 894 }) 895 if !paniced { 896 tc.t.Error("Rollback called inside Update did not panic") 897 return false 898 } 899 900 return true 901 } 902 903 // testMetadataTxInterface tests all facets of the managed read/write and 904 // manual transaction metadata interfaces as well as the bucket interfaces under 905 // them. 906 func testMetadataTxInterface(tc *testContext) bool { 907 if !testManagedTxPanics(tc) { 908 return false 909 } 910 911 bucket1Name := []byte("bucket1") 912 err := tc.db.Update(func(tx database.Tx) error { 913 _, err := tx.Metadata().CreateBucket(bucket1Name) 914 return err 915 }) 916 if err != nil { 917 tc.t.Errorf("Update: unexpected error creating bucket: %v", err) 918 return false 919 } 920 921 if !testMetadataManualTxInterface(tc) { 922 return false 923 } 924 925 // keyValues holds the keys and values to use when putting values 926 // into a bucket. 927 keyValues := []keyPair{ 928 {[]byte("mtxkey1"), []byte("foo1")}, 929 {[]byte("mtxkey2"), []byte("foo2")}, 930 {[]byte("mtxkey3"), []byte("foo3")}, 931 {[]byte("mtxkey4"), nil}, 932 } 933 934 // Test the bucket interface via a managed read-only transaction. 935 err = tc.db.View(func(tx database.Tx) error { 936 metadataBucket := tx.Metadata() 937 if metadataBucket == nil { 938 return fmt.Errorf("Metadata: unexpected nil bucket") 939 } 940 941 bucket1 := metadataBucket.Bucket(bucket1Name) 942 if bucket1 == nil { 943 return fmt.Errorf("Bucket1: unexpected nil bucket") 944 } 945 946 tc.isWritable = false 947 if !testBucketInterface(tc, bucket1) { 948 return errSubTestFail 949 } 950 951 return nil 952 }) 953 if err != nil { 954 if err != errSubTestFail { 955 tc.t.Errorf("%v", err) 956 } 957 return false 958 } 959 960 // Ensure errors returned from the user-supplied View function are 961 // returned. 962 viewError := fmt.Errorf("example view error") 963 err = tc.db.View(func(tx database.Tx) error { 964 return viewError 965 }) 966 if err != viewError { 967 tc.t.Errorf("View: inner function error not returned - got "+ 968 "%v, want %v", err, viewError) 969 return false 970 } 971 972 // Test the bucket interface via a managed read-write transaction. 973 // Also, put a series of values and force a rollback so the following 974 // code can ensure the values were not stored. 975 forceRollbackError := fmt.Errorf("force rollback") 976 err = tc.db.Update(func(tx database.Tx) error { 977 metadataBucket := tx.Metadata() 978 if metadataBucket == nil { 979 return fmt.Errorf("Metadata: unexpected nil bucket") 980 } 981 982 bucket1 := metadataBucket.Bucket(bucket1Name) 983 if bucket1 == nil { 984 return fmt.Errorf("Bucket1: unexpected nil bucket") 985 } 986 987 tc.isWritable = true 988 if !testBucketInterface(tc, bucket1) { 989 return errSubTestFail 990 } 991 992 if !testPutValues(tc, bucket1, keyValues) { 993 return errSubTestFail 994 } 995 996 // Return an error to force a rollback. 997 return forceRollbackError 998 }) 999 if err != forceRollbackError { 1000 if err == errSubTestFail { 1001 return false 1002 } 1003 1004 tc.t.Errorf("Update: inner function error not returned - got "+ 1005 "%v, want %v", err, forceRollbackError) 1006 return false 1007 } 1008 1009 // Ensure the values that should not have been stored due to the forced 1010 // rollback above were not actually stored. 1011 err = tc.db.View(func(tx database.Tx) error { 1012 metadataBucket := tx.Metadata() 1013 if metadataBucket == nil { 1014 return fmt.Errorf("Metadata: unexpected nil bucket") 1015 } 1016 1017 if !testGetValues(tc, metadataBucket, rollbackValues(keyValues)) { 1018 return errSubTestFail 1019 } 1020 1021 return nil 1022 }) 1023 if err != nil { 1024 if err != errSubTestFail { 1025 tc.t.Errorf("%v", err) 1026 } 1027 return false 1028 } 1029 1030 // Store a series of values via a managed read-write transaction. 1031 err = tc.db.Update(func(tx database.Tx) error { 1032 metadataBucket := tx.Metadata() 1033 if metadataBucket == nil { 1034 return fmt.Errorf("Metadata: unexpected nil bucket") 1035 } 1036 1037 bucket1 := metadataBucket.Bucket(bucket1Name) 1038 if bucket1 == nil { 1039 return fmt.Errorf("Bucket1: unexpected nil bucket") 1040 } 1041 1042 if !testPutValues(tc, bucket1, keyValues) { 1043 return errSubTestFail 1044 } 1045 1046 return nil 1047 }) 1048 if err != nil { 1049 if err != errSubTestFail { 1050 tc.t.Errorf("%v", err) 1051 } 1052 return false 1053 } 1054 1055 // Ensure the values stored above were committed as expected. 1056 err = tc.db.View(func(tx database.Tx) error { 1057 metadataBucket := tx.Metadata() 1058 if metadataBucket == nil { 1059 return fmt.Errorf("Metadata: unexpected nil bucket") 1060 } 1061 1062 bucket1 := metadataBucket.Bucket(bucket1Name) 1063 if bucket1 == nil { 1064 return fmt.Errorf("Bucket1: unexpected nil bucket") 1065 } 1066 1067 if !testGetValues(tc, bucket1, toGetValues(keyValues)) { 1068 return errSubTestFail 1069 } 1070 1071 return nil 1072 }) 1073 if err != nil { 1074 if err != errSubTestFail { 1075 tc.t.Errorf("%v", err) 1076 } 1077 return false 1078 } 1079 1080 // Clean up the values stored above in a managed read-write transaction. 1081 err = tc.db.Update(func(tx database.Tx) error { 1082 metadataBucket := tx.Metadata() 1083 if metadataBucket == nil { 1084 return fmt.Errorf("Metadata: unexpected nil bucket") 1085 } 1086 1087 bucket1 := metadataBucket.Bucket(bucket1Name) 1088 if bucket1 == nil { 1089 return fmt.Errorf("Bucket1: unexpected nil bucket") 1090 } 1091 1092 if !testDeleteValues(tc, bucket1, keyValues) { 1093 return errSubTestFail 1094 } 1095 1096 return nil 1097 }) 1098 if err != nil { 1099 if err != errSubTestFail { 1100 tc.t.Errorf("%v", err) 1101 } 1102 return false 1103 } 1104 1105 return true 1106 } 1107 1108 // testFetchBlockIOMissing ensures that all of the block retrieval API functions 1109 // work as expected when requesting blocks that don't exist. 1110 func testFetchBlockIOMissing(tc *testContext, tx database.Tx) bool { 1111 wantErrCode := database.ErrBlockNotFound 1112 1113 // --------------------- 1114 // Non-bulk Block IO API 1115 // --------------------- 1116 1117 // Test the individual block APIs one block at a time to ensure they 1118 // return the expected error. Also, build the data needed to test the 1119 // bulk APIs below while looping. 1120 allBlockHashes := make([]chainhash.Hash, len(tc.blocks)) 1121 allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) 1122 for i, block := range tc.blocks { 1123 blockHash := block.Hash() 1124 allBlockHashes[i] = *blockHash 1125 1126 txLocs, err := block.TxLoc() 1127 if err != nil { 1128 tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, 1129 err) 1130 return false 1131 } 1132 1133 // Ensure FetchBlock returns expected error. 1134 testName := fmt.Sprintf("FetchBlock #%d on missing block", i) 1135 _, err = tx.FetchBlock(blockHash) 1136 if !checkDbError(tc.t, testName, err, wantErrCode) { 1137 return false 1138 } 1139 1140 // Ensure FetchBlockHeader returns expected error. 1141 testName = fmt.Sprintf("FetchBlockHeader #%d on missing block", 1142 i) 1143 _, err = tx.FetchBlockHeader(blockHash) 1144 if !checkDbError(tc.t, testName, err, wantErrCode) { 1145 return false 1146 } 1147 1148 // Ensure the first transaction fetched as a block region from 1149 // the database returns the expected error. 1150 region := database.BlockRegion{ 1151 Hash: blockHash, 1152 Offset: uint32(txLocs[0].TxStart), 1153 Len: uint32(txLocs[0].TxLen), 1154 } 1155 allBlockRegions[i] = region 1156 _, err = tx.FetchBlockRegion(®ion) 1157 if !checkDbError(tc.t, testName, err, wantErrCode) { 1158 return false 1159 } 1160 1161 // Ensure HasBlock returns false. 1162 hasBlock, err := tx.HasBlock(blockHash) 1163 if err != nil { 1164 tc.t.Errorf("HasBlock #%d: unexpected err: %v", i, err) 1165 return false 1166 } 1167 if hasBlock { 1168 tc.t.Errorf("HasBlock #%d: should not have block", i) 1169 return false 1170 } 1171 } 1172 1173 // ----------------- 1174 // Bulk Block IO API 1175 // ----------------- 1176 1177 // Ensure FetchBlocks returns expected error. 1178 testName := "FetchBlocks on missing blocks" 1179 _, err := tx.FetchBlocks(allBlockHashes) 1180 if !checkDbError(tc.t, testName, err, wantErrCode) { 1181 return false 1182 } 1183 1184 // Ensure FetchBlockHeaders returns expected error. 1185 testName = "FetchBlockHeaders on missing blocks" 1186 _, err = tx.FetchBlockHeaders(allBlockHashes) 1187 if !checkDbError(tc.t, testName, err, wantErrCode) { 1188 return false 1189 } 1190 1191 // Ensure FetchBlockRegions returns expected error. 1192 testName = "FetchBlockRegions on missing blocks" 1193 _, err = tx.FetchBlockRegions(allBlockRegions) 1194 if !checkDbError(tc.t, testName, err, wantErrCode) { 1195 return false 1196 } 1197 1198 // Ensure HasBlocks returns false for all blocks. 1199 hasBlocks, err := tx.HasBlocks(allBlockHashes) 1200 if err != nil { 1201 tc.t.Errorf("HasBlocks: unexpected err: %v", err) 1202 } 1203 for i, hasBlock := range hasBlocks { 1204 if hasBlock { 1205 tc.t.Errorf("HasBlocks #%d: should not have block", i) 1206 return false 1207 } 1208 } 1209 1210 return true 1211 } 1212 1213 // testFetchBlockIO ensures all of the block retrieval API functions work as 1214 // expected for the provide set of blocks. The blocks must already be stored in 1215 // the database, or at least stored into the passed transaction. It also 1216 // tests several error conditions such as ensuring the expected errors are 1217 // returned when fetching blocks, headers, and regions that don't exist. 1218 func testFetchBlockIO(tc *testContext, tx database.Tx) bool { 1219 // --------------------- 1220 // Non-bulk Block IO API 1221 // --------------------- 1222 1223 // Test the individual block APIs one block at a time. Also, build the 1224 // data needed to test the bulk APIs below while looping. 1225 allBlockHashes := make([]chainhash.Hash, len(tc.blocks)) 1226 allBlockBytes := make([][]byte, len(tc.blocks)) 1227 allBlockTxLocs := make([][]wire.TxLoc, len(tc.blocks)) 1228 allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) 1229 for i, block := range tc.blocks { 1230 blockHash := block.Hash() 1231 allBlockHashes[i] = *blockHash 1232 1233 blockBytes, err := block.Bytes() 1234 if err != nil { 1235 tc.t.Errorf("block.Bytes(%d): unexpected error: %v", i, 1236 err) 1237 return false 1238 } 1239 allBlockBytes[i] = blockBytes 1240 1241 txLocs, err := block.TxLoc() 1242 if err != nil { 1243 tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, 1244 err) 1245 return false 1246 } 1247 allBlockTxLocs[i] = txLocs 1248 1249 // Ensure the block data fetched from the database matches the 1250 // expected bytes. 1251 gotBlockBytes, err := tx.FetchBlock(blockHash) 1252 if err != nil { 1253 tc.t.Errorf("FetchBlock(%s): unexpected error: %v", 1254 blockHash, err) 1255 return false 1256 } 1257 if !bytes.Equal(gotBlockBytes, blockBytes) { 1258 tc.t.Errorf("FetchBlock(%s): bytes mismatch: got %x, "+ 1259 "want %x", blockHash, gotBlockBytes, blockBytes) 1260 return false 1261 } 1262 1263 // Ensure the block header fetched from the database matches the 1264 // expected bytes. 1265 wantHeaderBytes := blockBytes[0:wire.MaxBlockHeaderPayload] 1266 gotHeaderBytes, err := tx.FetchBlockHeader(blockHash) 1267 if err != nil { 1268 tc.t.Errorf("FetchBlockHeader(%s): unexpected error: %v", 1269 blockHash, err) 1270 return false 1271 } 1272 if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) { 1273 tc.t.Errorf("FetchBlockHeader(%s): bytes mismatch: "+ 1274 "got %x, want %x", blockHash, gotHeaderBytes, 1275 wantHeaderBytes) 1276 return false 1277 } 1278 1279 // Ensure the first transaction fetched as a block region from 1280 // the database matches the expected bytes. 1281 region := database.BlockRegion{ 1282 Hash: blockHash, 1283 Offset: uint32(txLocs[0].TxStart), 1284 Len: uint32(txLocs[0].TxLen), 1285 } 1286 allBlockRegions[i] = region 1287 endRegionOffset := region.Offset + region.Len 1288 wantRegionBytes := blockBytes[region.Offset:endRegionOffset] 1289 gotRegionBytes, err := tx.FetchBlockRegion(®ion) 1290 if err != nil { 1291 tc.t.Errorf("FetchBlockRegion(%s): unexpected error: %v", 1292 blockHash, err) 1293 return false 1294 } 1295 if !bytes.Equal(gotRegionBytes, wantRegionBytes) { 1296 tc.t.Errorf("FetchBlockRegion(%s): bytes mismatch: "+ 1297 "got %x, want %x", blockHash, gotRegionBytes, 1298 wantRegionBytes) 1299 return false 1300 } 1301 1302 // Ensure block hash exists as expected. 1303 hasBlock, err := tx.HasBlock(blockHash) 1304 if err != nil { 1305 tc.t.Errorf("HasBlock(%s): unexpected error: %v", 1306 blockHash, err) 1307 return false 1308 } 1309 if !hasBlock { 1310 tc.t.Errorf("HasBlock(%s): database claims it doesn't "+ 1311 "have the block when it should", blockHash) 1312 return false 1313 } 1314 1315 // ----------------------- 1316 // Invalid blocks/regions. 1317 // ----------------------- 1318 1319 // Ensure fetching a block that doesn't exist returns the 1320 // expected error. 1321 badBlockHash := &chainhash.Hash{} 1322 testName := fmt.Sprintf("FetchBlock(%s) invalid block", 1323 badBlockHash) 1324 wantErrCode := database.ErrBlockNotFound 1325 _, err = tx.FetchBlock(badBlockHash) 1326 if !checkDbError(tc.t, testName, err, wantErrCode) { 1327 return false 1328 } 1329 1330 // Ensure fetching a block header that doesn't exist returns 1331 // the expected error. 1332 testName = fmt.Sprintf("FetchBlockHeader(%s) invalid block", 1333 badBlockHash) 1334 _, err = tx.FetchBlockHeader(badBlockHash) 1335 if !checkDbError(tc.t, testName, err, wantErrCode) { 1336 return false 1337 } 1338 1339 // Ensure fetching a block region in a block that doesn't exist 1340 // return the expected error. 1341 testName = fmt.Sprintf("FetchBlockRegion(%s) invalid hash", 1342 badBlockHash) 1343 wantErrCode = database.ErrBlockNotFound 1344 region.Hash = badBlockHash 1345 region.Offset = ^uint32(0) 1346 _, err = tx.FetchBlockRegion(®ion) 1347 if !checkDbError(tc.t, testName, err, wantErrCode) { 1348 return false 1349 } 1350 1351 // Ensure fetching a block region that is out of bounds returns 1352 // the expected error. 1353 testName = fmt.Sprintf("FetchBlockRegion(%s) invalid region", 1354 blockHash) 1355 wantErrCode = database.ErrBlockRegionInvalid 1356 region.Hash = blockHash 1357 region.Offset = ^uint32(0) 1358 _, err = tx.FetchBlockRegion(®ion) 1359 if !checkDbError(tc.t, testName, err, wantErrCode) { 1360 return false 1361 } 1362 } 1363 1364 // ----------------- 1365 // Bulk Block IO API 1366 // ----------------- 1367 1368 // Ensure the bulk block data fetched from the database matches the 1369 // expected bytes. 1370 blockData, err := tx.FetchBlocks(allBlockHashes) 1371 if err != nil { 1372 tc.t.Errorf("FetchBlocks: unexpected error: %v", err) 1373 return false 1374 } 1375 if len(blockData) != len(allBlockBytes) { 1376 tc.t.Errorf("FetchBlocks: unexpected number of results - got "+ 1377 "%d, want %d", len(blockData), len(allBlockBytes)) 1378 return false 1379 } 1380 for i := 0; i < len(blockData); i++ { 1381 blockHash := allBlockHashes[i] 1382 wantBlockBytes := allBlockBytes[i] 1383 gotBlockBytes := blockData[i] 1384 if !bytes.Equal(gotBlockBytes, wantBlockBytes) { 1385 tc.t.Errorf("FetchBlocks(%s): bytes mismatch: got %x, "+ 1386 "want %x", blockHash, gotBlockBytes, 1387 wantBlockBytes) 1388 return false 1389 } 1390 } 1391 1392 // Ensure the bulk block headers fetched from the database match the 1393 // expected bytes. 1394 blockHeaderData, err := tx.FetchBlockHeaders(allBlockHashes) 1395 if err != nil { 1396 tc.t.Errorf("FetchBlockHeaders: unexpected error: %v", err) 1397 return false 1398 } 1399 if len(blockHeaderData) != len(allBlockBytes) { 1400 tc.t.Errorf("FetchBlockHeaders: unexpected number of results "+ 1401 "- got %d, want %d", len(blockHeaderData), 1402 len(allBlockBytes)) 1403 return false 1404 } 1405 for i := 0; i < len(blockHeaderData); i++ { 1406 blockHash := allBlockHashes[i] 1407 wantHeaderBytes := allBlockBytes[i][0:wire.MaxBlockHeaderPayload] 1408 gotHeaderBytes := blockHeaderData[i] 1409 if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) { 1410 tc.t.Errorf("FetchBlockHeaders(%s): bytes mismatch: "+ 1411 "got %x, want %x", blockHash, gotHeaderBytes, 1412 wantHeaderBytes) 1413 return false 1414 } 1415 } 1416 1417 // Ensure the first transaction of every block fetched in bulk block 1418 // regions from the database matches the expected bytes. 1419 allRegionBytes, err := tx.FetchBlockRegions(allBlockRegions) 1420 if err != nil { 1421 tc.t.Errorf("FetchBlockRegions: unexpected error: %v", err) 1422 return false 1423 1424 } 1425 if len(allRegionBytes) != len(allBlockRegions) { 1426 tc.t.Errorf("FetchBlockRegions: unexpected number of results "+ 1427 "- got %d, want %d", len(allRegionBytes), 1428 len(allBlockRegions)) 1429 return false 1430 } 1431 for i, gotRegionBytes := range allRegionBytes { 1432 region := &allBlockRegions[i] 1433 endRegionOffset := region.Offset + region.Len 1434 wantRegionBytes := blockData[i][region.Offset:endRegionOffset] 1435 if !bytes.Equal(gotRegionBytes, wantRegionBytes) { 1436 tc.t.Errorf("FetchBlockRegions(%d): bytes mismatch: "+ 1437 "got %x, want %x", i, gotRegionBytes, 1438 wantRegionBytes) 1439 return false 1440 } 1441 } 1442 1443 // Ensure the bulk determination of whether a set of block hashes are in 1444 // the database returns true for all loaded blocks. 1445 hasBlocks, err := tx.HasBlocks(allBlockHashes) 1446 if err != nil { 1447 tc.t.Errorf("HasBlocks: unexpected error: %v", err) 1448 return false 1449 } 1450 for i, hasBlock := range hasBlocks { 1451 if !hasBlock { 1452 tc.t.Errorf("HasBlocks(%d): should have block", i) 1453 return false 1454 } 1455 } 1456 1457 // ----------------------- 1458 // Invalid blocks/regions. 1459 // ----------------------- 1460 1461 // Ensure fetching blocks for which one doesn't exist returns the 1462 // expected error. 1463 testName := "FetchBlocks invalid hash" 1464 badBlockHashes := make([]chainhash.Hash, len(allBlockHashes)+1) 1465 copy(badBlockHashes, allBlockHashes) 1466 badBlockHashes[len(badBlockHashes)-1] = chainhash.Hash{} 1467 wantErrCode := database.ErrBlockNotFound 1468 _, err = tx.FetchBlocks(badBlockHashes) 1469 if !checkDbError(tc.t, testName, err, wantErrCode) { 1470 return false 1471 } 1472 1473 // Ensure fetching block headers for which one doesn't exist returns the 1474 // expected error. 1475 testName = "FetchBlockHeaders invalid hash" 1476 _, err = tx.FetchBlockHeaders(badBlockHashes) 1477 if !checkDbError(tc.t, testName, err, wantErrCode) { 1478 return false 1479 } 1480 1481 // Ensure fetching block regions for which one of blocks doesn't exist 1482 // returns expected error. 1483 testName = "FetchBlockRegions invalid hash" 1484 badBlockRegions := make([]database.BlockRegion, len(allBlockRegions)+1) 1485 copy(badBlockRegions, allBlockRegions) 1486 badBlockRegions[len(badBlockRegions)-1].Hash = &chainhash.Hash{} 1487 wantErrCode = database.ErrBlockNotFound 1488 _, err = tx.FetchBlockRegions(badBlockRegions) 1489 if !checkDbError(tc.t, testName, err, wantErrCode) { 1490 return false 1491 } 1492 1493 // Ensure fetching block regions that are out of bounds returns the 1494 // expected error. 1495 testName = "FetchBlockRegions invalid regions" 1496 badBlockRegions = badBlockRegions[:len(badBlockRegions)-1] 1497 for i := range badBlockRegions { 1498 badBlockRegions[i].Offset = ^uint32(0) 1499 } 1500 wantErrCode = database.ErrBlockRegionInvalid 1501 _, err = tx.FetchBlockRegions(badBlockRegions) 1502 return checkDbError(tc.t, testName, err, wantErrCode) 1503 } 1504 1505 // testBlockIOTxInterface ensures that the block IO interface works as expected 1506 // for both managed read/write and manual transactions. This function leaves 1507 // all of the stored blocks in the database. 1508 func testBlockIOTxInterface(tc *testContext) bool { 1509 // Ensure attempting to store a block with a read-only transaction fails 1510 // with the expected error. 1511 err := tc.db.View(func(tx database.Tx) error { 1512 wantErrCode := database.ErrTxNotWritable 1513 for i, block := range tc.blocks { 1514 testName := fmt.Sprintf("StoreBlock(%d) on ro tx", i) 1515 err := tx.StoreBlock(block) 1516 if !checkDbError(tc.t, testName, err, wantErrCode) { 1517 return errSubTestFail 1518 } 1519 } 1520 1521 return nil 1522 }) 1523 if err != nil { 1524 if err != errSubTestFail { 1525 tc.t.Errorf("%v", err) 1526 } 1527 return false 1528 } 1529 1530 // Populate the database with loaded blocks and ensure all of the data 1531 // fetching APIs work properly on them within the transaction before a 1532 // commit or rollback. Then, force a rollback so the code below can 1533 // ensure none of the data actually gets stored. 1534 forceRollbackError := fmt.Errorf("force rollback") 1535 err = tc.db.Update(func(tx database.Tx) error { 1536 // Store all blocks in the same transaction. 1537 for i, block := range tc.blocks { 1538 err := tx.StoreBlock(block) 1539 if err != nil { 1540 tc.t.Errorf("StoreBlock #%d: unexpected error: "+ 1541 "%v", i, err) 1542 return errSubTestFail 1543 } 1544 } 1545 1546 // Ensure attempting to store the same block again, before the 1547 // transaction has been committed, returns the expected error. 1548 wantErrCode := database.ErrBlockExists 1549 for i, block := range tc.blocks { 1550 testName := fmt.Sprintf("duplicate block entry #%d "+ 1551 "(before commit)", i) 1552 err := tx.StoreBlock(block) 1553 if !checkDbError(tc.t, testName, err, wantErrCode) { 1554 return errSubTestFail 1555 } 1556 } 1557 1558 // Ensure that all data fetches from the stored blocks before 1559 // the transaction has been committed work as expected. 1560 if !testFetchBlockIO(tc, tx) { 1561 return errSubTestFail 1562 } 1563 1564 return forceRollbackError 1565 }) 1566 if err != forceRollbackError { 1567 if err == errSubTestFail { 1568 return false 1569 } 1570 1571 tc.t.Errorf("Update: inner function error not returned - got "+ 1572 "%v, want %v", err, forceRollbackError) 1573 return false 1574 } 1575 1576 // Ensure rollback was successful 1577 err = tc.db.View(func(tx database.Tx) error { 1578 if !testFetchBlockIOMissing(tc, tx) { 1579 return errSubTestFail 1580 } 1581 return nil 1582 }) 1583 if err != nil { 1584 if err != errSubTestFail { 1585 tc.t.Errorf("%v", err) 1586 } 1587 return false 1588 } 1589 1590 // Populate the database with loaded blocks and ensure all of the data 1591 // fetching APIs work properly. 1592 err = tc.db.Update(func(tx database.Tx) error { 1593 // Store a bunch of blocks in the same transaction. 1594 for i, block := range tc.blocks { 1595 err := tx.StoreBlock(block) 1596 if err != nil { 1597 tc.t.Errorf("StoreBlock #%d: unexpected error: "+ 1598 "%v", i, err) 1599 return errSubTestFail 1600 } 1601 } 1602 1603 // Ensure attempting to store the same block again while in the 1604 // same transaction, but before it has been committed, returns 1605 // the expected error. 1606 for i, block := range tc.blocks { 1607 testName := fmt.Sprintf("duplicate block entry #%d "+ 1608 "(before commit)", i) 1609 wantErrCode := database.ErrBlockExists 1610 err := tx.StoreBlock(block) 1611 if !checkDbError(tc.t, testName, err, wantErrCode) { 1612 return errSubTestFail 1613 } 1614 } 1615 1616 // Ensure that all data fetches from the stored blocks before 1617 // the transaction has been committed work as expected. 1618 if !testFetchBlockIO(tc, tx) { 1619 return errSubTestFail 1620 } 1621 1622 return nil 1623 }) 1624 if err != nil { 1625 if err != errSubTestFail { 1626 tc.t.Errorf("%v", err) 1627 } 1628 return false 1629 } 1630 1631 // Ensure all data fetch tests work as expected using a managed 1632 // read-only transaction after the data was successfully committed 1633 // above. 1634 err = tc.db.View(func(tx database.Tx) error { 1635 if !testFetchBlockIO(tc, tx) { 1636 return errSubTestFail 1637 } 1638 1639 return nil 1640 }) 1641 if err != nil { 1642 if err != errSubTestFail { 1643 tc.t.Errorf("%v", err) 1644 } 1645 return false 1646 } 1647 1648 // Ensure all data fetch tests work as expected using a managed 1649 // read-write transaction after the data was successfully committed 1650 // above. 1651 err = tc.db.Update(func(tx database.Tx) error { 1652 if !testFetchBlockIO(tc, tx) { 1653 return errSubTestFail 1654 } 1655 1656 // Ensure attempting to store existing blocks again returns the 1657 // expected error. Note that this is different from the 1658 // previous version since this is a new transaction after the 1659 // blocks have been committed. 1660 wantErrCode := database.ErrBlockExists 1661 for i, block := range tc.blocks { 1662 testName := fmt.Sprintf("duplicate block entry #%d "+ 1663 "(before commit)", i) 1664 err := tx.StoreBlock(block) 1665 if !checkDbError(tc.t, testName, err, wantErrCode) { 1666 return errSubTestFail 1667 } 1668 } 1669 1670 return nil 1671 }) 1672 if err != nil { 1673 if err != errSubTestFail { 1674 tc.t.Errorf("%v", err) 1675 } 1676 return false 1677 } 1678 1679 return true 1680 } 1681 1682 // testClosedTxInterface ensures that both the metadata and block IO API 1683 // functions behave as expected when attempted against a closed transaction. 1684 func testClosedTxInterface(tc *testContext, tx database.Tx) bool { 1685 wantErrCode := database.ErrTxClosed 1686 bucket := tx.Metadata() 1687 cursor := tx.Metadata().Cursor() 1688 bucketName := []byte("closedtxbucket") 1689 keyName := []byte("closedtxkey") 1690 1691 // ------------ 1692 // Metadata API 1693 // ------------ 1694 1695 // Ensure that attempting to get an existing bucket returns nil when the 1696 // transaction is closed. 1697 if b := bucket.Bucket(bucketName); b != nil { 1698 tc.t.Errorf("Bucket: did not return nil on closed tx") 1699 return false 1700 } 1701 1702 // Ensure CreateBucket returns expected error. 1703 testName := "CreateBucket on closed tx" 1704 _, err := bucket.CreateBucket(bucketName) 1705 if !checkDbError(tc.t, testName, err, wantErrCode) { 1706 return false 1707 } 1708 1709 // Ensure CreateBucketIfNotExists returns expected error. 1710 testName = "CreateBucketIfNotExists on closed tx" 1711 _, err = bucket.CreateBucketIfNotExists(bucketName) 1712 if !checkDbError(tc.t, testName, err, wantErrCode) { 1713 return false 1714 } 1715 1716 // Ensure Delete returns expected error. 1717 testName = "Delete on closed tx" 1718 err = bucket.Delete(keyName) 1719 if !checkDbError(tc.t, testName, err, wantErrCode) { 1720 return false 1721 } 1722 1723 // Ensure DeleteBucket returns expected error. 1724 testName = "DeleteBucket on closed tx" 1725 err = bucket.DeleteBucket(bucketName) 1726 if !checkDbError(tc.t, testName, err, wantErrCode) { 1727 return false 1728 } 1729 1730 // Ensure ForEach returns expected error. 1731 testName = "ForEach on closed tx" 1732 err = bucket.ForEach(nil) 1733 if !checkDbError(tc.t, testName, err, wantErrCode) { 1734 return false 1735 } 1736 1737 // Ensure ForEachBucket returns expected error. 1738 testName = "ForEachBucket on closed tx" 1739 err = bucket.ForEachBucket(nil) 1740 if !checkDbError(tc.t, testName, err, wantErrCode) { 1741 return false 1742 } 1743 1744 // Ensure Get returns expected error. 1745 testName = "Get on closed tx" 1746 if k := bucket.Get(keyName); k != nil { 1747 tc.t.Errorf("Get: did not return nil on closed tx") 1748 return false 1749 } 1750 1751 // Ensure Put returns expected error. 1752 testName = "Put on closed tx" 1753 err = bucket.Put(keyName, []byte("test")) 1754 if !checkDbError(tc.t, testName, err, wantErrCode) { 1755 return false 1756 } 1757 1758 // ------------------- 1759 // Metadata Cursor API 1760 // ------------------- 1761 1762 // Ensure attempting to get a bucket from a cursor on a closed tx gives 1763 // back nil. 1764 if b := cursor.Bucket(); b != nil { 1765 tc.t.Error("Cursor.Bucket: returned non-nil on closed tx") 1766 return false 1767 } 1768 1769 // Ensure Cursor.Delete returns expected error. 1770 testName = "Cursor.Delete on closed tx" 1771 err = cursor.Delete() 1772 if !checkDbError(tc.t, testName, err, wantErrCode) { 1773 return false 1774 } 1775 1776 // Ensure Cursor.First on a closed tx returns false and nil key/value. 1777 if cursor.First() { 1778 tc.t.Error("Cursor.First: claims ok on closed tx") 1779 return false 1780 } 1781 if cursor.Key() != nil || cursor.Value() != nil { 1782 tc.t.Error("Cursor.First: key and/or value are not nil on " + 1783 "closed tx") 1784 return false 1785 } 1786 1787 // Ensure Cursor.Last on a closed tx returns false and nil key/value. 1788 if cursor.Last() { 1789 tc.t.Error("Cursor.Last: claims ok on closed tx") 1790 return false 1791 } 1792 if cursor.Key() != nil || cursor.Value() != nil { 1793 tc.t.Error("Cursor.Last: key and/or value are not nil on " + 1794 "closed tx") 1795 return false 1796 } 1797 1798 // Ensure Cursor.Next on a closed tx returns false and nil key/value. 1799 if cursor.Next() { 1800 tc.t.Error("Cursor.Next: claims ok on closed tx") 1801 return false 1802 } 1803 if cursor.Key() != nil || cursor.Value() != nil { 1804 tc.t.Error("Cursor.Next: key and/or value are not nil on " + 1805 "closed tx") 1806 return false 1807 } 1808 1809 // Ensure Cursor.Prev on a closed tx returns false and nil key/value. 1810 if cursor.Prev() { 1811 tc.t.Error("Cursor.Prev: claims ok on closed tx") 1812 return false 1813 } 1814 if cursor.Key() != nil || cursor.Value() != nil { 1815 tc.t.Error("Cursor.Prev: key and/or value are not nil on " + 1816 "closed tx") 1817 return false 1818 } 1819 1820 // Ensure Cursor.Seek on a closed tx returns false and nil key/value. 1821 if cursor.Seek([]byte{}) { 1822 tc.t.Error("Cursor.Seek: claims ok on closed tx") 1823 return false 1824 } 1825 if cursor.Key() != nil || cursor.Value() != nil { 1826 tc.t.Error("Cursor.Seek: key and/or value are not nil on " + 1827 "closed tx") 1828 return false 1829 } 1830 1831 // --------------------- 1832 // Non-bulk Block IO API 1833 // --------------------- 1834 1835 // Test the individual block APIs one block at a time to ensure they 1836 // return the expected error. Also, build the data needed to test the 1837 // bulk APIs below while looping. 1838 allBlockHashes := make([]chainhash.Hash, len(tc.blocks)) 1839 allBlockRegions := make([]database.BlockRegion, len(tc.blocks)) 1840 for i, block := range tc.blocks { 1841 blockHash := block.Hash() 1842 allBlockHashes[i] = *blockHash 1843 1844 txLocs, err := block.TxLoc() 1845 if err != nil { 1846 tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i, 1847 err) 1848 return false 1849 } 1850 1851 // Ensure StoreBlock returns expected error. 1852 testName = "StoreBlock on closed tx" 1853 err = tx.StoreBlock(block) 1854 if !checkDbError(tc.t, testName, err, wantErrCode) { 1855 return false 1856 } 1857 1858 // Ensure FetchBlock returns expected error. 1859 testName = fmt.Sprintf("FetchBlock #%d on closed tx", i) 1860 _, err = tx.FetchBlock(blockHash) 1861 if !checkDbError(tc.t, testName, err, wantErrCode) { 1862 return false 1863 } 1864 1865 // Ensure FetchBlockHeader returns expected error. 1866 testName = fmt.Sprintf("FetchBlockHeader #%d on closed tx", i) 1867 _, err = tx.FetchBlockHeader(blockHash) 1868 if !checkDbError(tc.t, testName, err, wantErrCode) { 1869 return false 1870 } 1871 1872 // Ensure the first transaction fetched as a block region from 1873 // the database returns the expected error. 1874 region := database.BlockRegion{ 1875 Hash: blockHash, 1876 Offset: uint32(txLocs[0].TxStart), 1877 Len: uint32(txLocs[0].TxLen), 1878 } 1879 allBlockRegions[i] = region 1880 _, err = tx.FetchBlockRegion(®ion) 1881 if !checkDbError(tc.t, testName, err, wantErrCode) { 1882 return false 1883 } 1884 1885 // Ensure HasBlock returns expected error. 1886 testName = fmt.Sprintf("HasBlock #%d on closed tx", i) 1887 _, err = tx.HasBlock(blockHash) 1888 if !checkDbError(tc.t, testName, err, wantErrCode) { 1889 return false 1890 } 1891 } 1892 1893 // ----------------- 1894 // Bulk Block IO API 1895 // ----------------- 1896 1897 // Ensure FetchBlocks returns expected error. 1898 testName = "FetchBlocks on closed tx" 1899 _, err = tx.FetchBlocks(allBlockHashes) 1900 if !checkDbError(tc.t, testName, err, wantErrCode) { 1901 return false 1902 } 1903 1904 // Ensure FetchBlockHeaders returns expected error. 1905 testName = "FetchBlockHeaders on closed tx" 1906 _, err = tx.FetchBlockHeaders(allBlockHashes) 1907 if !checkDbError(tc.t, testName, err, wantErrCode) { 1908 return false 1909 } 1910 1911 // Ensure FetchBlockRegions returns expected error. 1912 testName = "FetchBlockRegions on closed tx" 1913 _, err = tx.FetchBlockRegions(allBlockRegions) 1914 if !checkDbError(tc.t, testName, err, wantErrCode) { 1915 return false 1916 } 1917 1918 // Ensure HasBlocks returns expected error. 1919 testName = "HasBlocks on closed tx" 1920 _, err = tx.HasBlocks(allBlockHashes) 1921 if !checkDbError(tc.t, testName, err, wantErrCode) { 1922 return false 1923 } 1924 1925 // --------------- 1926 // Commit/Rollback 1927 // --------------- 1928 1929 // Ensure that attempting to rollback or commit a transaction that is 1930 // already closed returns the expected error. 1931 err = tx.Rollback() 1932 if !checkDbError(tc.t, "closed tx rollback", err, wantErrCode) { 1933 return false 1934 } 1935 err = tx.Commit() 1936 return checkDbError(tc.t, "closed tx commit", err, wantErrCode) 1937 } 1938 1939 // testTxClosed ensures that both the metadata and block IO API functions behave 1940 // as expected when attempted against both read-only and read-write 1941 // transactions. 1942 func testTxClosed(tc *testContext) bool { 1943 bucketName := []byte("closedtxbucket") 1944 keyName := []byte("closedtxkey") 1945 1946 // Start a transaction, create a bucket and key used for testing, and 1947 // immediately perform a commit on it so it is closed. 1948 tx, err := tc.db.Begin(true) 1949 if err != nil { 1950 tc.t.Errorf("Begin(true): unexpected error: %v", err) 1951 return false 1952 } 1953 defer rollbackOnPanic(tc.t, tx) 1954 if _, err := tx.Metadata().CreateBucket(bucketName); err != nil { 1955 tc.t.Errorf("CreateBucket: unexpected error: %v", err) 1956 return false 1957 } 1958 if err := tx.Metadata().Put(keyName, []byte("test")); err != nil { 1959 tc.t.Errorf("Put: unexpected error: %v", err) 1960 return false 1961 } 1962 if err := tx.Commit(); err != nil { 1963 tc.t.Errorf("Commit: unexpected error: %v", err) 1964 return false 1965 } 1966 1967 // Ensure invoking all of the functions on the closed read-write 1968 // transaction behave as expected. 1969 if !testClosedTxInterface(tc, tx) { 1970 return false 1971 } 1972 1973 // Repeat the tests with a rolled-back read-only transaction. 1974 tx, err = tc.db.Begin(false) 1975 if err != nil { 1976 tc.t.Errorf("Begin(false): unexpected error: %v", err) 1977 return false 1978 } 1979 defer rollbackOnPanic(tc.t, tx) 1980 if err := tx.Rollback(); err != nil { 1981 tc.t.Errorf("Rollback: unexpected error: %v", err) 1982 return false 1983 } 1984 1985 // Ensure invoking all of the functions on the closed read-only 1986 // transaction behave as expected. 1987 return testClosedTxInterface(tc, tx) 1988 } 1989 1990 // testConcurrecy ensure the database properly supports concurrent readers and 1991 // only a single writer. It also ensures views act as snapshots at the time 1992 // they are acquired. 1993 func testConcurrecy(tc *testContext) bool { 1994 // sleepTime is how long each of the concurrent readers should sleep to 1995 // aid in detection of whether or not the data is actually being read 1996 // concurrently. It starts with a sane lower bound. 1997 var sleepTime = time.Millisecond * 250 1998 1999 // Determine about how long it takes for a single block read. When it's 2000 // longer than the default minimum sleep time, adjust the sleep time to 2001 // help prevent durations that are too short which would cause erroneous 2002 // test failures on slower systems. 2003 startTime := time.Now() 2004 err := tc.db.View(func(tx database.Tx) error { 2005 _, err := tx.FetchBlock(tc.blocks[0].Hash()) 2006 return err 2007 }) 2008 if err != nil { 2009 tc.t.Errorf("Unexpected error in view: %v", err) 2010 return false 2011 } 2012 elapsed := time.Since(startTime) 2013 if sleepTime < elapsed { 2014 sleepTime = elapsed 2015 } 2016 tc.t.Logf("Time to load block 0: %v, using sleep time: %v", elapsed, 2017 sleepTime) 2018 2019 // reader takes a block number to load and channel to return the result 2020 // of the operation on. It is used below to launch multiple concurrent 2021 // readers. 2022 numReaders := len(tc.blocks) 2023 resultChan := make(chan bool, numReaders) 2024 reader := func(blockNum int) { 2025 err := tc.db.View(func(tx database.Tx) error { 2026 time.Sleep(sleepTime) 2027 _, err := tx.FetchBlock(tc.blocks[blockNum].Hash()) 2028 return err 2029 }) 2030 if err != nil { 2031 tc.t.Errorf("Unexpected error in concurrent view: %v", 2032 err) 2033 resultChan <- false 2034 } 2035 resultChan <- true 2036 } 2037 2038 // Start up several concurrent readers for the same block and wait for 2039 // the results. 2040 startTime = time.Now() 2041 for i := 0; i < numReaders; i++ { 2042 go reader(0) 2043 } 2044 for i := 0; i < numReaders; i++ { 2045 if result := <-resultChan; !result { 2046 return false 2047 } 2048 } 2049 elapsed = time.Since(startTime) 2050 tc.t.Logf("%d concurrent reads of same block elapsed: %v", numReaders, 2051 elapsed) 2052 2053 // Consider it a failure if it took longer than half the time it would 2054 // take with no concurrency. 2055 if elapsed > sleepTime*time.Duration(numReaders/2) { 2056 tc.t.Errorf("Concurrent views for same block did not appear to "+ 2057 "run simultaneously: elapsed %v", elapsed) 2058 return false 2059 } 2060 2061 // Start up several concurrent readers for different blocks and wait for 2062 // the results. 2063 startTime = time.Now() 2064 for i := 0; i < numReaders; i++ { 2065 go reader(i) 2066 } 2067 for i := 0; i < numReaders; i++ { 2068 if result := <-resultChan; !result { 2069 return false 2070 } 2071 } 2072 elapsed = time.Since(startTime) 2073 tc.t.Logf("%d concurrent reads of different blocks elapsed: %v", 2074 numReaders, elapsed) 2075 2076 // Consider it a failure if it took longer than half the time it would 2077 // take with no concurrency. 2078 if elapsed > sleepTime*time.Duration(numReaders/2) { 2079 tc.t.Errorf("Concurrent views for different blocks did not "+ 2080 "appear to run simultaneously: elapsed %v", elapsed) 2081 return false 2082 } 2083 2084 // Start up a few readers and wait for them to acquire views. Each 2085 // reader waits for a signal from the writer to be finished to ensure 2086 // that the data written by the writer is not seen by the view since it 2087 // was started before the data was set. 2088 concurrentKey := []byte("notthere") 2089 concurrentVal := []byte("someval") 2090 started := make(chan struct{}) 2091 writeComplete := make(chan struct{}) 2092 reader = func(blockNum int) { 2093 err := tc.db.View(func(tx database.Tx) error { 2094 started <- struct{}{} 2095 2096 // Wait for the writer to complete. 2097 <-writeComplete 2098 2099 // Since this reader was created before the write took 2100 // place, the data it added should not be visible. 2101 val := tx.Metadata().Get(concurrentKey) 2102 if val != nil { 2103 return fmt.Errorf("%s should not be visible", 2104 concurrentKey) 2105 } 2106 return nil 2107 }) 2108 if err != nil { 2109 tc.t.Errorf("Unexpected error in concurrent view: %v", 2110 err) 2111 resultChan <- false 2112 } 2113 resultChan <- true 2114 } 2115 for i := 0; i < numReaders; i++ { 2116 go reader(0) 2117 } 2118 for i := 0; i < numReaders; i++ { 2119 <-started 2120 } 2121 2122 // All readers are started and waiting for completion of the writer. 2123 // Set some data the readers are expecting to not find and signal the 2124 // readers the write is done by closing the writeComplete channel. 2125 err = tc.db.Update(func(tx database.Tx) error { 2126 return tx.Metadata().Put(concurrentKey, concurrentVal) 2127 }) 2128 if err != nil { 2129 tc.t.Errorf("Unexpected error in update: %v", err) 2130 return false 2131 } 2132 close(writeComplete) 2133 2134 // Wait for reader results. 2135 for i := 0; i < numReaders; i++ { 2136 if result := <-resultChan; !result { 2137 return false 2138 } 2139 } 2140 2141 // Start a few writers and ensure the total time is at least the 2142 // writeSleepTime * numWriters. This ensures only one write transaction 2143 // can be active at a time. 2144 writeSleepTime := time.Millisecond * 250 2145 writer := func() { 2146 err := tc.db.Update(func(tx database.Tx) error { 2147 time.Sleep(writeSleepTime) 2148 return nil 2149 }) 2150 if err != nil { 2151 tc.t.Errorf("Unexpected error in concurrent view: %v", 2152 err) 2153 resultChan <- false 2154 } 2155 resultChan <- true 2156 } 2157 numWriters := 3 2158 startTime = time.Now() 2159 for i := 0; i < numWriters; i++ { 2160 go writer() 2161 } 2162 for i := 0; i < numWriters; i++ { 2163 if result := <-resultChan; !result { 2164 return false 2165 } 2166 } 2167 elapsed = time.Since(startTime) 2168 tc.t.Logf("%d concurrent writers elapsed using sleep time %v: %v", 2169 numWriters, writeSleepTime, elapsed) 2170 2171 // The total time must have been at least the sum of all sleeps if the 2172 // writes blocked properly. 2173 if elapsed < writeSleepTime*time.Duration(numWriters) { 2174 tc.t.Errorf("Concurrent writes appeared to run simultaneously: "+ 2175 "elapsed %v", elapsed) 2176 return false 2177 } 2178 2179 return true 2180 } 2181 2182 // testConcurrentClose ensures that closing the database with open transactions 2183 // blocks until the transactions are finished. 2184 // 2185 // The database will be closed upon returning from this function. 2186 func testConcurrentClose(tc *testContext) bool { 2187 // Start up a few readers and wait for them to acquire views. Each 2188 // reader waits for a signal to complete to ensure the transactions stay 2189 // open until they are explicitly signalled to be closed. 2190 var activeReaders int32 2191 numReaders := 3 2192 started := make(chan struct{}) 2193 finishReaders := make(chan struct{}) 2194 resultChan := make(chan bool, numReaders+1) 2195 reader := func() { 2196 err := tc.db.View(func(tx database.Tx) error { 2197 atomic.AddInt32(&activeReaders, 1) 2198 started <- struct{}{} 2199 <-finishReaders 2200 atomic.AddInt32(&activeReaders, -1) 2201 return nil 2202 }) 2203 if err != nil { 2204 tc.t.Errorf("Unexpected error in concurrent view: %v", 2205 err) 2206 resultChan <- false 2207 } 2208 resultChan <- true 2209 } 2210 for i := 0; i < numReaders; i++ { 2211 go reader() 2212 } 2213 for i := 0; i < numReaders; i++ { 2214 <-started 2215 } 2216 2217 // Close the database in a separate goroutine. This should block until 2218 // the transactions are finished. Once the close has taken place, the 2219 // dbClosed channel is closed to signal the main goroutine below. 2220 dbClosed := make(chan struct{}) 2221 go func() { 2222 started <- struct{}{} 2223 err := tc.db.Close() 2224 if err != nil { 2225 tc.t.Errorf("Unexpected error in concurrent view: %v", 2226 err) 2227 resultChan <- false 2228 } 2229 close(dbClosed) 2230 resultChan <- true 2231 }() 2232 <-started 2233 2234 // Wait a short period and then signal the reader transactions to 2235 // finish. When the db closed channel is received, ensure there are no 2236 // active readers open. 2237 time.AfterFunc(time.Millisecond*250, func() { close(finishReaders) }) 2238 <-dbClosed 2239 if nr := atomic.LoadInt32(&activeReaders); nr != 0 { 2240 tc.t.Errorf("Close did not appear to block with active "+ 2241 "readers: %d active", nr) 2242 return false 2243 } 2244 2245 // Wait for all results. 2246 for i := 0; i < numReaders+1; i++ { 2247 if result := <-resultChan; !result { 2248 return false 2249 } 2250 } 2251 2252 return true 2253 } 2254 2255 // testInterface tests performs tests for the various interfaces of the database 2256 // package which require state in the database for the given database type. 2257 func testInterface(t *testing.T, db database.DB) { 2258 // Create a test context to pass around. 2259 context := testContext{t: t, db: db} 2260 2261 // Load the test blocks and store in the test context for use throughout 2262 // the tests. 2263 blocks, err := loadBlocks(t, blockDataFile, blockDataNet) 2264 if err != nil { 2265 t.Errorf("loadBlocks: Unexpected error: %v", err) 2266 return 2267 } 2268 context.blocks = blocks 2269 2270 // Test the transaction metadata interface including managed and manual 2271 // transactions as well as buckets. 2272 if !testMetadataTxInterface(&context) { 2273 return 2274 } 2275 2276 // Test the transaction block IO interface using managed and manual 2277 // transactions. This function leaves all of the stored blocks in the 2278 // database since they're used later. 2279 if !testBlockIOTxInterface(&context) { 2280 return 2281 } 2282 2283 // Test all of the transaction interface functions against a closed 2284 // transaction work as expected. 2285 if !testTxClosed(&context) { 2286 return 2287 } 2288 2289 // Test the database properly supports concurrency. 2290 if !testConcurrecy(&context) { 2291 return 2292 } 2293 2294 // Test that closing the database with open transactions blocks until 2295 // the transactions are finished. 2296 // 2297 // The database will be closed upon returning from this function, so it 2298 // must be the last thing called. 2299 testConcurrentClose(&context) 2300 }