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 cffac96
Showing 1 changed file with 103 additions and 1 deletion.
104 changes: 103 additions & 1 deletion tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"os"
"testing"

"github.com/coreos/bbolt"
"github.com/boltdb/bolt"
)

// Ensure that committing a closed transaction returns an error.
Expand Down Expand Up @@ -602,6 +602,108 @@ 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")
hold1 := openReadTxn()
put("k3", "v3")
hold2 := openReadTxn()
del("k3")
rtx2 := openReadTxn()
del("k1")
hold3 := openReadTxn()
del("k2")
hold4 := openReadTxn()
put("k4", "v4")
hold5 := openReadTxn()

// Close the read transactions we established to hold a portion of the pages in pending state.
rollback(hold1)
rollback(hold2)
rollback(hold3)
rollback(hold4)
rollback(hold5)

// Execute a write transaction to trigger a releaseRange operation in the db
// that will free multiple ranges between the remaining open read transactions, now that the
// holds have been rolled back.
put("k4", "v4")

// 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 cffac96

Please sign in to comment.