Skip to content

Commit

Permalink
Improve test coverage for releaseRange rollbacks.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpbetz committed Sep 7, 2017
1 parent a148de8 commit d065aa7
Showing 1 changed file with 93 additions and 0 deletions.
93 changes: 93 additions & 0 deletions tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,99 @@ func TestTx_CopyFile_Error_Normal(t *testing.T) {
}
}

// Ensure that a update that releases pages in ranges between read transactions
// is properly applied and rolled back.
func TestTx_releaseRange(t *testing.T) {
// Set initial mmap size well beyond the limit we will hit in this
// test, since we are testing with long running read transactions
// and will deadlock if db.grow is triggered.
db := MustOpenWithOption(&bolt.Options{InitialMmapSize: os.Getpagesize() * 100})
defer db.MustClose()

bucket := "bucket"

put := func(key, value string) {
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(bucket))
if err != nil {
t.Fatal(err)
}
return b.Put([]byte(key), []byte(value))
}); err != nil {
t.Fatal(err)
}
}

del := func(key string) {
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(bucket))
if err != nil {
t.Fatal(err)
}
return b.Delete([]byte(key))
}); err != nil {
t.Fatal(err)
}
}

getWithTxn := func(txn *bolt.Tx, key string) []byte {
return txn.Bucket([]byte(bucket)).Get([]byte(key))
}

openReadTxn := func() *bolt.Tx {
readTx, err := db.Begin(false)
if err != nil {
t.Fatal(err)
}
return readTx
}

checkWithReadTxn := func(txn *bolt.Tx, key string, wantValue []byte) {
value := getWithTxn(txn, key)
if !bytes.Equal(value, wantValue) {
t.Errorf("Wanted value to be %s for key %s, but got %s", wantValue, key, string(value))
}
}

rollback := func(txn *bolt.Tx) {
if err := txn.Rollback(); err != nil {
t.Fatal(err)
}
}

put("k1", "v1")
rtx1 := openReadTxn()
put("k2", "v2")
put("k3", "v3")
del("k3")
rtx2 := openReadTxn()
del("k1")
del("k2")
put("k4", "v4")

// Execute a write transaction to trigger a releaseRange operation in the db
// that will free multiple ranges between the remaining open read transactions.
updateTxn, err := db.Begin(true)
if err != nil {
t.Fatal(err)
}
rollback(updateTxn)

// Check that all long running reads still read correct values.
checkWithReadTxn(rtx1, "k1", []byte("v1"))
checkWithReadTxn(rtx2, "k2", []byte("v2"))
rollback(rtx1)
rollback(rtx2)

// Check that the final state is correct.
rtx7 := openReadTxn()
checkWithReadTxn(rtx7, "k1", nil)
checkWithReadTxn(rtx7, "k2", nil)
checkWithReadTxn(rtx7, "k3", nil)
checkWithReadTxn(rtx7, "k4", []byte("v4"))
rollback(rtx7)
}

func ExampleTx_Rollback() {
// Open the database.
db, err := bolt.Open(tempfile(), 0666, nil)
Expand Down

0 comments on commit d065aa7

Please sign in to comment.