1
1
package bbolt_test
2
2
3
3
import (
4
- "bytes"
5
- "encoding/binary"
6
4
"fmt"
7
5
"math/rand"
8
6
"testing"
9
- "unsafe"
10
7
11
8
"github.com/stretchr/testify/require"
12
9
@@ -17,26 +14,19 @@ import (
17
14
)
18
15
19
16
func TestTx_Check_CorruptPage (t * testing.T ) {
20
- bucketKey := "testBucket"
21
-
22
17
t .Log ("Creating db file." )
23
- db := btesting .MustCreateDBWithOption (t , & bbolt.Options {PageSize : pageSize })
24
- defer func () {
25
- require .NoError (t , db .Close ())
26
- }()
27
-
28
- uErr := db .Update (func (tx * bbolt.Tx ) error {
29
- t .Logf ("Creating bucket '%v'." , bucketKey )
30
- b , bErr := tx .CreateBucketIfNotExists ([]byte (bucketKey ))
31
- require .NoError (t , bErr )
32
- t .Logf ("Generating random data in bucket '%v'." , bucketKey )
33
- generateSampleDataInBucket (t , b , pageSize , 3 )
34
- return nil
35
- })
36
- require .NoError (t , uErr )
18
+ db := btesting .MustCreateDBWithOption (t , & bbolt.Options {PageSize : 4096 })
19
+
20
+ // Each page can hold roughly 20 key/values pair, so 100 such
21
+ // key/value pairs will consume about 5 leaf pages.
22
+ err := db .Fill ([]byte ("data" ), 1 , 100 ,
23
+ func (tx int , k int ) []byte { return []byte (fmt .Sprintf ("%04d" , k )) },
24
+ func (tx int , k int ) []byte { return make ([]byte , 100 ) },
25
+ )
26
+ require .NoError (t , err )
37
27
38
- t .Logf ("Corrupting random leaf page in bucket '%v'." , bucketKey )
39
- victimPageId , validPageIds := corruptLeafPage (t , db .DB )
28
+ t .Log ("Corrupting random leaf page." )
29
+ victimPageId , validPageIds := corruptRandomLeafPage (t , db .DB )
40
30
41
31
t .Log ("Running consistency check." )
42
32
vErr := db .View (func (tx * bbolt.Tx ) error {
@@ -61,45 +51,53 @@ func TestTx_Check_CorruptPage(t *testing.T) {
61
51
return nil
62
52
})
63
53
require .NoError (t , vErr )
54
+ t .Log ("All check passed" )
55
+
56
+ // Manually close the db, otherwise the PostTestCleanup will
57
+ // check the db again and accordingly fail the test.
58
+ db .MustClose ()
64
59
}
65
60
66
- // corruptLeafPage write an invalid leafPageElement into the victim page.
67
- func corruptLeafPage (t testing.TB , db * bbolt.DB ) (victimPageId common.Pgid , validPageIds []common.Pgid ) {
68
- t .Helper ()
69
- victimPageId , validPageIds = findVictimPageId (t , db )
61
+ // corruptRandomLeafPage corrupts one random leaf page.
62
+ func corruptRandomLeafPage (t testing.TB , db * bbolt.DB ) (victimPageId common.Pgid , validPageIds []common.Pgid ) {
63
+ victimPageId , validPageIds = pickupRandomLeafPage (t , db )
70
64
victimPage , victimBuf , err := guts_cli .ReadPage (db .Path (), uint64 (victimPageId ))
71
65
require .NoError (t , err )
72
66
require .True (t , victimPage .IsLeafPage ())
73
- require .True (t , victimPage .Count () > 0 )
74
- // Dumping random bytes in victim page for corruption.
75
- copy (victimBuf [32 :], generateCorruptionBytes (t ))
67
+ require .True (t , victimPage .Count () > 1 )
68
+
69
+ // intentionally make the second key < the first key.
70
+ element := victimPage .LeafPageElement (1 )
71
+ key := element .Key ()
72
+ key [0 ] = 0
73
+
76
74
// Write the corrupt page to db file.
77
75
err = guts_cli .WritePage (db .Path (), victimBuf )
78
76
require .NoError (t , err )
79
77
return victimPageId , validPageIds
80
78
}
81
79
82
- // findVictimPageId finds all the leaf pages of a bucket and picks a random leaf page as a victim to be corrupted.
83
- func findVictimPageId (t testing.TB , db * bbolt.DB ) (victimPageId common.Pgid , validPageIds []common.Pgid ) {
84
- t .Helper ()
85
- // Read DB's RootPage.
80
+ // pickupRandomLeafPage picks up a random leaf page.
81
+ func pickupRandomLeafPage (t testing.TB , db * bbolt.DB ) (victimPageId common.Pgid , validPageIds []common.Pgid ) {
82
+ // Read DB's RootPage, which should be a leaf page.
86
83
rootPageId , _ , err := guts_cli .GetRootPage (db .Path ())
87
84
require .NoError (t , err )
88
85
rootPage , _ , err := guts_cli .ReadPage (db .Path (), uint64 (rootPageId ))
89
86
require .NoError (t , err )
90
87
require .True (t , rootPage .IsLeafPage ())
91
- require .Equal (t , 1 , len (rootPage .LeafPageElements ()))
92
- // Find Bucket's RootPage.
88
+
89
+ // The leaf page contains only one item, namely the bucket
90
+ require .Equal (t , uint16 (1 ), rootPage .Count ())
93
91
lpe := rootPage .LeafPageElement (uint16 (0 ))
94
- require .Equal (t , uint32 ( common . BranchPageFlag ), lpe .Flags ())
95
- k := lpe . Key ()
96
- require . Equal ( t , "testBucket" , string ( k ))
92
+ require .True (t , lpe .IsBucketEntry ())
93
+
94
+ // The bucket should be pointing to a branch page
97
95
bucketRootPageId := lpe .Bucket ().RootPage ()
98
- // Read Bucket's RootPage.
99
96
bucketRootPage , _ , err := guts_cli .ReadPage (db .Path (), uint64 (bucketRootPageId ))
100
97
require .NoError (t , err )
101
- require .Equal (t , uint16 (common .BranchPageFlag ), bucketRootPage .Flags ())
102
- // Retrieve Bucket's PageIds
98
+ require .True (t , bucketRootPage .IsBranchPage ())
99
+
100
+ // Retrieve all the leaf pages included in the branch page, and pick up random one from them.
103
101
var bucketPageIds []common.Pgid
104
102
for _ , bpe := range bucketRootPage .BranchPageElements () {
105
103
bucketPageIds = append (bucketPageIds , bpe .Pgid ())
@@ -109,36 +107,3 @@ func findVictimPageId(t testing.TB, db *bbolt.DB) (victimPageId common.Pgid, val
109
107
validPageIds = append (bucketPageIds [:randomIdx ], bucketPageIds [randomIdx + 1 :]... )
110
108
return victimPageId , validPageIds
111
109
}
112
-
113
- // generateSampleDataInBucket fill in sample data into given bucket to create the given
114
- // number of leafPages. To control the number of leafPages, sample data are generated in order.
115
- func generateSampleDataInBucket (t testing.TB , bk * bbolt.Bucket , pageSize int , lPages int ) {
116
- t .Helper ()
117
- maxBytesInPage := int (bk .FillPercent * float64 (pageSize ))
118
- currentKey := 1
119
- currentVal := 100
120
- for i := 0 ; i < lPages ; i ++ {
121
- currentSize := common .PageHeaderSize
122
- for {
123
- err := bk .Put ([]byte (fmt .Sprintf ("key_%d" , currentKey )), []byte (fmt .Sprintf ("val_%d" , currentVal )))
124
- require .NoError (t , err )
125
- currentSize += common .LeafPageElementSize + unsafe .Sizeof (currentKey ) + unsafe .Sizeof (currentVal )
126
- if int (currentSize ) >= maxBytesInPage {
127
- break
128
- }
129
- currentKey ++
130
- currentVal ++
131
- }
132
- }
133
- }
134
-
135
- // generateCorruptionBytes returns random bytes to corrupt a page.
136
- // It inserts a page element which violates the btree key order if no panic is expected.
137
- func generateCorruptionBytes (t testing.TB ) []byte {
138
- t .Helper ()
139
- invalidLPE := common .NewLeafPageElement (0 , 0 , 0 , 0 )
140
- var buf bytes.Buffer
141
- err := binary .Write (& buf , binary .BigEndian , invalidLPE )
142
- require .NoError (t , err )
143
- return buf .Bytes ()
144
- }
0 commit comments