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