diff --git a/bucket_test.go b/bucket_test.go index 359a98f..5f54b17 100644 --- a/bucket_test.go +++ b/bucket_test.go @@ -16,7 +16,7 @@ import ( // Ensure that a bucket that gets a non-existent key returns nil. func TestBucketGetNonExistent(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") value := tx.Bucket("widgets").Get([]byte("foo")) assert.Nil(t, value) @@ -28,7 +28,7 @@ func TestBucketGetNonExistent(t *testing.T) { // Ensure that a bucket can read a value that is not flushed yet. func TestBucketGetFromNode(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") b := tx.Bucket("widgets") b.Put([]byte("foo"), []byte("bar")) @@ -42,7 +42,7 @@ func TestBucketGetFromNode(t *testing.T) { // Ensure that a bucket can write a key/value. func TestBucketPut(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") err := tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) assert.NoError(t, err) @@ -56,11 +56,11 @@ func TestBucketPut(t *testing.T) { // Ensure that setting a value on a read-only bucket returns an error. func TestBucketPutReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") err := b.Put([]byte("foo"), []byte("bar")) assert.Equal(t, err, ErrBucketNotWritable) @@ -72,7 +72,7 @@ func TestBucketPutReadOnly(t *testing.T) { // Ensure that a bucket can delete an existing key. func TestBucketDelete(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) err := tx.Bucket("widgets").Delete([]byte("foo")) @@ -87,11 +87,11 @@ func TestBucketDelete(t *testing.T) { // Ensure that deleting a key on a read-only bucket returns an error. func TestBucketDeleteReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") err := b.Delete([]byte("foo")) assert.Equal(t, err, ErrBucketNotWritable) @@ -103,7 +103,7 @@ func TestBucketDeleteReadOnly(t *testing.T) { // Ensure that a bucket can return an autoincrementing sequence. func TestBucketNextSequence(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.CreateBucket("woojits") @@ -127,11 +127,11 @@ func TestBucketNextSequence(t *testing.T) { // Ensure that retrieving the next sequence on a read-only bucket returns an error. func TestBucketNextSequenceReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") i, err := b.NextSequence() assert.Equal(t, i, 0) @@ -144,11 +144,11 @@ func TestBucketNextSequenceReadOnly(t *testing.T) { // Ensure that incrementing past the maximum sequence number will return an error. func TestBucketNextSequenceOverflow(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") return nil }) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { b := tx.Bucket("widgets") b.bucket.sequence = uint64(maxInt) seq, err := b.NextSequence() @@ -162,7 +162,7 @@ func TestBucketNextSequenceOverflow(t *testing.T) { // Ensure a database can loop over all key/value pairs in a bucket. func TestBucketForEach(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("0000")) tx.Bucket("widgets").Put([]byte("baz"), []byte("0001")) @@ -194,7 +194,7 @@ func TestBucketForEach(t *testing.T) { // Ensure a database can stop iteration early. func TestBucketForEachShortCircuit(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("bar"), []byte("0000")) tx.Bucket("widgets").Put([]byte("baz"), []byte("0000")) @@ -218,7 +218,7 @@ func TestBucketForEachShortCircuit(t *testing.T) { // Ensure that an error is returned when inserting with an empty key. func TestBucketPutEmptyKey(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") err := tx.Bucket("widgets").Put([]byte(""), []byte("bar")) assert.Equal(t, err, ErrKeyRequired) @@ -232,7 +232,7 @@ func TestBucketPutEmptyKey(t *testing.T) { // Ensure that an error is returned when inserting with a key that's too large. func TestBucketPutKeyTooLarge(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") err := tx.Bucket("widgets").Put(make([]byte, 32769), []byte("bar")) assert.Equal(t, err, ErrKeyTooLarge) @@ -248,7 +248,7 @@ func TestBucketStat(t *testing.T) { } withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { // Add bucket with lots of keys. tx.CreateBucket("widgets") b := tx.Bucket("widgets") @@ -271,7 +271,7 @@ func TestBucketStat(t *testing.T) { return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") stat := b.Stat() assert.Equal(t, stat.BranchPageCount, 15) @@ -312,11 +312,11 @@ func TestBucketPutSingle(t *testing.T) { withOpenDB(func(db *DB, path string) { m := make(map[string][]byte) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) for _, item := range items { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { if err := tx.Bucket("widgets").Put(item.Key, item.Value); err != nil { panic("put error: " + err.Error()) } @@ -325,7 +325,7 @@ func TestBucketPutSingle(t *testing.T) { }) // Verify all key/values so far. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { i := 0 for k, v := range m { value := tx.Bucket("widgets").Get([]byte(k)) @@ -359,10 +359,10 @@ func TestBucketPutMultiple(t *testing.T) { f := func(items testdata) bool { withOpenDB(func(db *DB, path string) { // Bulk insert all values. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { b := tx.Bucket("widgets") for _, item := range items { assert.NoError(t, b.Put(item.Key, item.Value)) @@ -372,7 +372,7 @@ func TestBucketPutMultiple(t *testing.T) { assert.NoError(t, err) // Verify all items exist. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") for _, item := range items { value := b.Get(item.Key) @@ -402,10 +402,10 @@ func TestBucketDeleteQuick(t *testing.T) { f := func(items testdata) bool { withOpenDB(func(db *DB, path string) { // Bulk insert all values. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { b := tx.Bucket("widgets") for _, item := range items { assert.NoError(t, b.Put(item.Key, item.Value)) @@ -416,13 +416,13 @@ func TestBucketDeleteQuick(t *testing.T) { // Remove items one at a time and check consistency. for i, item := range items { - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Delete(item.Key) }) assert.NoError(t, err) // Anything before our deletion index should be nil. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") for j, exp := range items { if j > i { diff --git a/cmd/bolt/main.go b/cmd/bolt/main.go index 647c1c2..9a51ec5 100644 --- a/cmd/bolt/main.go +++ b/cmd/bolt/main.go @@ -66,7 +66,7 @@ func GetCommand(c *cli.Context) { } defer db.Close() - err = db.With(func(tx *bolt.Tx) error { + err = db.View(func(tx *bolt.Tx) error { // Find bucket. b := tx.Bucket(name) if b == nil { @@ -105,7 +105,7 @@ func SetCommand(c *cli.Context) { } defer db.Close() - err = db.Do(func(tx *bolt.Tx) error { + err = db.Update(func(tx *bolt.Tx) error { // Find bucket. b := tx.Bucket(name) if b == nil { @@ -137,7 +137,7 @@ func KeysCommand(c *cli.Context) { } defer db.Close() - err = db.With(func(tx *bolt.Tx) error { + err = db.View(func(tx *bolt.Tx) error { // Find bucket. b := tx.Bucket(name) if b == nil { @@ -172,7 +172,7 @@ func BucketsCommand(c *cli.Context) { } defer db.Close() - err = db.With(func(tx *bolt.Tx) error { + err = db.View(func(tx *bolt.Tx) error { for _, b := range tx.Buckets() { println(b.Name()) } @@ -202,7 +202,7 @@ func PagesCommand(c *cli.Context) { println("ID TYPE ITEMS OVRFLW") println("======== ========== ====== ======") - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { var id int for { p, err := tx.Page(id) diff --git a/cmd/bolt/main_test.go b/cmd/bolt/main_test.go index b755ccd..b203d2c 100644 --- a/cmd/bolt/main_test.go +++ b/cmd/bolt/main_test.go @@ -15,7 +15,7 @@ import ( func TestGet(t *testing.T) { SetTestMode(true) open(func(db *bolt.DB) { - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) return nil @@ -45,7 +45,7 @@ func TestGetBucketNotFound(t *testing.T) { func TestGetKeyNotFound(t *testing.T) { SetTestMode(true) open(func(db *bolt.DB) { - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { return tx.CreateBucket("widgets") }) output := run("get", db.Path(), "widgets", "foo") @@ -57,7 +57,7 @@ func TestGetKeyNotFound(t *testing.T) { func TestSet(t *testing.T) { SetTestMode(true) open(func(db *bolt.DB) { - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { tx.CreateBucket("widgets") return nil }) @@ -86,7 +86,7 @@ func TestSetBucketNotFound(t *testing.T) { func TestKeys(t *testing.T) { SetTestMode(true) open(func(db *bolt.DB) { - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("0002"), []byte("")) tx.Bucket("widgets").Put([]byte("0001"), []byte("")) @@ -118,7 +118,7 @@ func TestKeysBucketNotFound(t *testing.T) { func TestBuckets(t *testing.T) { SetTestMode(true) open(func(db *bolt.DB) { - db.Do(func(tx *bolt.Tx) error { + db.Update(func(tx *bolt.Tx) error { tx.CreateBucket("woojits") tx.CreateBucket("widgets") tx.CreateBucket("whatchits") diff --git a/db.go b/db.go index 0d9117f..1acef14 100644 --- a/db.go +++ b/db.go @@ -293,11 +293,22 @@ func (db *DB) close() error { return nil } -// Tx creates a read-only transaction. -// Multiple read-only transactions can be used concurrently. +// Begin starts a new transaction. +// Multiple read-only transactions can be used concurrently but only one +// write transaction can be used at a time. Starting multiple write transactions +// will cause the calls to block and be serialized until the current write +// transaction finishes. // -// IMPORTANT: You must close the transaction after you are finished or else the database will not reclaim old pages. -func (db *DB) Tx() (*Tx, error) { +// IMPORTANT: You must close read-only transactions after you are finished or +// else the database will not reclaim old pages. +func (db *DB) Begin(writable bool) (*Tx, error) { + if writable { + return db.beginRWTx() + } + return db.beginTx() +} + +func (db *DB) beginTx() (*Tx, error) { db.metalock.Lock() defer db.metalock.Unlock() @@ -321,10 +332,7 @@ func (db *DB) Tx() (*Tx, error) { return t, nil } -// RWTx creates a read/write transaction. -// Only one read/write transaction is allowed at a time. -// You must call Commit() or Rollback() on the transaction to close it. -func (db *DB) RWTx() (*Tx, error) { +func (db *DB) beginRWTx() (*Tx, error) { db.metalock.Lock() defer db.metalock.Unlock() @@ -373,15 +381,15 @@ func (db *DB) removeTx(t *Tx) { } } -// Do executes a function within the context of a read-write managed transaction. +// Update executes a function within the context of a read-write managed transaction. // If no error is returned from the function then the transaction is committed. // If an error is returned then the entire transaction is rolled back. // Any error that is returned from the function or returned from the commit is -// returned from the Do() method. +// returned from the Update() method. // // Attempting to manually commit or rollback within the function will cause a panic. -func (db *DB) Do(fn func(*Tx) error) error { - t, err := db.RWTx() +func (db *DB) Update(fn func(*Tx) error) error { + t, err := db.Begin(true) if err != nil { return err } @@ -400,12 +408,12 @@ func (db *DB) Do(fn func(*Tx) error) error { return t.Commit() } -// With executes a function within the context of a managed transaction. -// Any error that is returned from the function is returned from the With() method. +// View executes a function within the context of a managed read-only transaction. +// Any error that is returned from the function is returned from the View() method. // // Attempting to manually rollback within the function will cause a panic. -func (db *DB) With(fn func(*Tx) error) error { - t, err := db.Tx() +func (db *DB) View(fn func(*Tx) error) error { + t, err := db.Begin(false) if err != nil { return err } @@ -433,7 +441,7 @@ func (db *DB) With(fn func(*Tx) error) error { // using the database while a copy is in progress. func (db *DB) Copy(w io.Writer) error { // Maintain a reader transaction so pages don't get reclaimed. - t, err := db.Tx() + t, err := db.Begin(false) if err != nil { return err } @@ -492,7 +500,7 @@ func (db *DB) Stat() (*Stat, error) { db.mmaplock.RUnlock() db.metalock.Unlock() - err := db.Do(func(t *Tx) error { + err := db.Update(func(t *Tx) error { s.PageCount = int(t.meta.pgid) s.FreePageCount = len(db.freelist.all()) s.PageSize = db.pageSize diff --git a/db_test.go b/db_test.go index 462821f..df17f94 100644 --- a/db_test.go +++ b/db_test.go @@ -116,16 +116,16 @@ func TestDBCorruptMeta0(t *testing.T) { // Ensure that a database cannot open a transaction when it's not open. func TestDBTxErrDatabaseNotOpen(t *testing.T) { withDB(func(db *DB, path string) { - tx, err := db.Tx() + tx, err := db.Begin(false) assert.Nil(t, tx) assert.Equal(t, err, ErrDatabaseNotOpen) }) } // Ensure that a read-write transaction can be retrieved. -func TestDBRWTx(t *testing.T) { +func TestDBBeginRW(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, err := db.RWTx() + tx, err := db.Begin(true) assert.NotNil(t, tx) assert.NoError(t, err) assert.Equal(t, tx.DB(), db) @@ -136,7 +136,7 @@ func TestDBRWTx(t *testing.T) { // Ensure that opening a transaction while the DB is closed returns an error. func TestDBRWTxOpenWithClosedDB(t *testing.T) { withDB(func(db *DB, path string) { - tx, err := db.RWTx() + tx, err := db.Begin(true) assert.Equal(t, err, ErrDatabaseNotOpen) assert.Nil(t, tx) }) @@ -145,7 +145,7 @@ func TestDBRWTxOpenWithClosedDB(t *testing.T) { // Ensure a database can provide a transactional block. func TestDBTxBlock(t *testing.T) { withOpenDB(func(db *DB, path string) { - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") b := tx.Bucket("widgets") b.Put([]byte("foo"), []byte("bar")) @@ -154,7 +154,7 @@ func TestDBTxBlock(t *testing.T) { return nil }) assert.NoError(t, err) - err = db.With(func(tx *Tx) error { + err = db.View(func(tx *Tx) error { assert.Nil(t, tx.Bucket("widgets").Get([]byte("foo"))) assert.Equal(t, []byte("bat"), tx.Bucket("widgets").Get([]byte("baz"))) return nil @@ -166,7 +166,7 @@ func TestDBTxBlock(t *testing.T) { // Ensure a closed database returns an error while running a transaction block func TestDBTxBlockWhileClosed(t *testing.T) { withDB(func(db *DB, path string) { - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") return nil }) @@ -177,13 +177,13 @@ func TestDBTxBlockWhileClosed(t *testing.T) { // Ensure a panic occurs while trying to commit a managed transaction. func TestDBTxBlockWithManualCommitAndRollback(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") assert.Panics(t, func() { tx.Commit() }) assert.Panics(t, func() { tx.Rollback() }) return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { assert.Panics(t, func() { tx.Commit() }) assert.Panics(t, func() { tx.Rollback() }) return nil @@ -194,7 +194,7 @@ func TestDBTxBlockWithManualCommitAndRollback(t *testing.T) { // Ensure that the database can be copied to a file path. func TestDBCopyFile(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) tx.Bucket("widgets").Put([]byte("baz"), []byte("bat")) @@ -207,7 +207,7 @@ func TestDBCopyFile(t *testing.T) { assert.NoError(t, db2.Open("/tmp/bolt.copyfile.db", 0666)) defer db2.Close() - db2.With(func(tx *Tx) error { + db2.View(func(tx *Tx) error { assert.Equal(t, []byte("bar"), tx.Bucket("widgets").Get([]byte("foo"))) assert.Equal(t, []byte("bat"), tx.Bucket("widgets").Get([]byte("baz"))) return nil @@ -218,7 +218,7 @@ func TestDBCopyFile(t *testing.T) { // Ensure the database can return stats about itself. func TestDBStat(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") b := tx.Bucket("widgets") for i := 0; i < 10000; i++ { @@ -228,17 +228,17 @@ func TestDBStat(t *testing.T) { }) // Delete some keys. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Delete([]byte("10")) }) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Delete([]byte("1000")) }) // Open some readers. - t0, _ := db.Tx() - t1, _ := db.Tx() - t2, _ := db.Tx() + t0, _ := db.Begin(false) + t1, _ := db.Begin(false) + t2, _ := db.Begin(false) t2.Rollback() // Obtain stats. @@ -293,11 +293,11 @@ func TestDBString(t *testing.T) { func BenchmarkDBPutSequential(b *testing.B) { value := []byte(strings.Repeat("0", 64)) withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) for i := 0; i < b.N; i++ { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Put([]byte(strconv.Itoa(i)), value) }) } @@ -309,11 +309,11 @@ func BenchmarkDBPutRandom(b *testing.B) { indexes := rand.Perm(b.N) value := []byte(strings.Repeat("0", 64)) withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) for i := 0; i < b.N; i++ { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Put([]byte(strconv.Itoa(indexes[i])), value) }) } diff --git a/example_test.go b/example_test.go index 6c6e6f5..c09b86f 100644 --- a/example_test.go +++ b/example_test.go @@ -10,14 +10,14 @@ func init() { os.MkdirAll("/tmp/bolt", 0777) } -func ExampleDB_Do() { +func ExampleDB_Update() { // Open the database. var db DB db.Open("/tmp/bolt/db_do.db", 0666) defer db.Close() // Execute several commands within a write transaction. - err := db.Do(func(tx *Tx) error { + err := db.Update(func(tx *Tx) error { if err := tx.CreateBucket("widgets"); err != nil { return err } @@ -30,7 +30,7 @@ func ExampleDB_Do() { // If our transactional block didn't return an error then our data is saved. if err == nil { - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { value := tx.Bucket("widgets").Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", string(value)) return nil @@ -41,14 +41,14 @@ func ExampleDB_Do() { // The value of 'foo' is: bar } -func ExampleDB_With() { +func ExampleDB_View() { // Open the database. var db DB db.Open("/tmp/bolt/db_with.db", 0666) defer db.Close() // Insert data into a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("people") tx.Bucket("people").Put([]byte("john"), []byte("doe")) tx.Bucket("people").Put([]byte("susy"), []byte("que")) @@ -56,7 +56,7 @@ func ExampleDB_With() { }) // Access data from within a read-only transactional block. - db.With(func(t *Tx) error { + db.View(func(t *Tx) error { v := t.Bucket("people").Get([]byte("john")) fmt.Printf("John's last name is %s.\n", string(v)) return nil @@ -73,7 +73,7 @@ func ExampleTx_Put() { defer db.Close() // Start a write transaction. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { // Create a bucket. tx.CreateBucket("widgets") @@ -83,7 +83,7 @@ func ExampleTx_Put() { }) // Read value back in a different read-only transaction. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { value := tx.Bucket("widgets").Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", string(value)) return nil @@ -100,7 +100,7 @@ func ExampleTx_Delete() { defer db.Close() // Start a write transaction. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { // Create a bucket. tx.CreateBucket("widgets") b := tx.Bucket("widgets") @@ -115,12 +115,12 @@ func ExampleTx_Delete() { }) // Delete the key in a different write transaction. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Delete([]byte("foo")) }) // Retrieve the key again. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { value := tx.Bucket("widgets").Get([]byte("foo")) if value == nil { fmt.Printf("The value of 'foo' is now: nil\n") @@ -140,7 +140,7 @@ func ExampleTx_ForEach() { defer db.Close() // Insert data into a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("animals") b := tx.Bucket("animals") b.Put([]byte("dog"), []byte("fun")) @@ -161,19 +161,19 @@ func ExampleTx_ForEach() { // A liger is awesome. } -func ExampleTx() { +func ExampleBegin_ReadOnly() { // Open the database. var db DB db.Open("/tmp/bolt/tx.db", 0666) defer db.Close() // Create a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) // Create several keys in a transaction. - tx, _ := db.RWTx() + tx, _ := db.Begin(true) b := tx.Bucket("widgets") b.Put([]byte("john"), []byte("blue")) b.Put([]byte("abby"), []byte("red")) @@ -181,7 +181,7 @@ func ExampleTx() { tx.Commit() // Iterate over the values in sorted key order. - tx, _ = db.Tx() + tx, _ = db.Begin(false) c := tx.Bucket("widgets").Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { fmt.Printf("%s likes %s\n", string(k), string(v)) @@ -201,23 +201,23 @@ func ExampleTx_rollback() { defer db.Close() // Create a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) // Set a value for a key. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) }) // Update the key but rollback the transaction so it never saves. - tx, _ := db.RWTx() + tx, _ := db.Begin(true) b := tx.Bucket("widgets") b.Put([]byte("foo"), []byte("baz")) tx.Rollback() // Ensure that our original value is still set. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { value := tx.Bucket("widgets").Get([]byte("foo")) fmt.Printf("The value for 'foo' is still: %s\n", string(value)) return nil @@ -234,7 +234,7 @@ func ExampleDB_CopyFile() { defer db.Close() // Create a bucket and a key. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) return nil @@ -249,7 +249,7 @@ func ExampleDB_CopyFile() { defer db2.Close() // Ensure that the key exists in the copy. - db2.With(func(tx *Tx) error { + db2.View(func(tx *Tx) error { value := tx.Bucket("widgets").Get([]byte("foo")) fmt.Printf("The value for 'foo' in the clone is: %s\n", string(value)) return nil diff --git a/functional_test.go b/functional_test.go index 866ec95..6ca75b8 100644 --- a/functional_test.go +++ b/functional_test.go @@ -28,7 +28,7 @@ func TestParallelTxs(t *testing.T) { var current testdata withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) @@ -51,7 +51,7 @@ func TestParallelTxs(t *testing.T) { go func() { mutex.RLock() local := current - tx, err := db.Tx() + tx, err := db.Begin(false) mutex.RUnlock() if err == ErrDatabaseNotOpen { wg.Done() @@ -89,7 +89,7 @@ func TestParallelTxs(t *testing.T) { pending = pending[currentBatchSize:] // Start write transaction. - tx, err := db.RWTx() + tx, err := db.Begin(true) if !assert.NoError(t, err) { t.FailNow() } diff --git a/tx_test.go b/tx_test.go index fe33869..58cfc95 100644 --- a/tx_test.go +++ b/tx_test.go @@ -16,7 +16,7 @@ import ( // Ensure that committing a closed transaction returns an error. func TestTxCommitClosed(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, _ := db.RWTx() + tx, _ := db.Begin(true) tx.CreateBucket("foo") assert.NoError(t, tx.Commit()) assert.Equal(t, tx.Commit(), ErrTxClosed) @@ -26,7 +26,7 @@ func TestTxCommitClosed(t *testing.T) { // Ensure that rolling back a closed transaction returns an error. func TestTxRollbackClosed(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, _ := db.RWTx() + tx, _ := db.Begin(true) assert.NoError(t, tx.Rollback()) assert.Equal(t, tx.Rollback(), ErrTxClosed) }) @@ -35,7 +35,7 @@ func TestTxRollbackClosed(t *testing.T) { // Ensure that committing a read-only transaction returns an error. func TestTxCommitReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, _ := db.Tx() + tx, _ := db.Begin(false) assert.Equal(t, tx.Commit(), ErrTxNotWritable) }) } @@ -43,7 +43,7 @@ func TestTxCommitReadOnly(t *testing.T) { // Ensure that the database can retrieve a list of buckets. func TestTxBuckets(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("foo") tx.CreateBucket("bar") tx.CreateBucket("baz") @@ -61,7 +61,7 @@ func TestTxBuckets(t *testing.T) { // Ensure that creating a bucket with a read-only transaction returns an error. func TestTxCreateBucketReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { assert.Equal(t, tx.CreateBucket("foo"), ErrTxNotWritable) return nil }) @@ -71,7 +71,7 @@ func TestTxCreateBucketReadOnly(t *testing.T) { // Ensure that creating a bucket on a closed transaction returns an error. func TestTxCreateBucketClosed(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, _ := db.RWTx() + tx, _ := db.Begin(true) tx.Commit() assert.Equal(t, tx.CreateBucket("foo"), ErrTxClosed) }) @@ -80,7 +80,7 @@ func TestTxCreateBucketClosed(t *testing.T) { // Ensure that a Tx can retrieve a bucket. func TestTxBucket(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") b := tx.Bucket("widgets") if assert.NotNil(t, b) { @@ -94,7 +94,7 @@ func TestTxBucket(t *testing.T) { // Ensure that a Tx retrieving a non-existent key returns nil. func TestTxGetMissing(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) value := tx.Bucket("widgets").Get([]byte("no_such_key")) @@ -107,12 +107,12 @@ func TestTxGetMissing(t *testing.T) { // Ensure that retrieving all buckets returns writable buckets. func TestTxWritableBuckets(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.CreateBucket("woojits") return nil }) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { buckets := tx.Buckets() assert.Equal(t, len(buckets), 2) assert.Equal(t, buckets[0].Name(), "widgets") @@ -121,7 +121,7 @@ func TestTxWritableBuckets(t *testing.T) { buckets[1].Put([]byte("bar"), []byte("0001")) return nil }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { assert.Equal(t, []byte("0000"), tx.Bucket("widgets").Get([]byte("foo"))) assert.Equal(t, []byte("0001"), tx.Bucket("woojits").Get([]byte("bar"))) return nil @@ -133,13 +133,13 @@ func TestTxWritableBuckets(t *testing.T) { func TestTxCreateBucket(t *testing.T) { withOpenDB(func(db *DB, path string) { // Create a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.NoError(t, tx.CreateBucket("widgets")) return nil }) // Read the bucket through a separate transaction. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") assert.NotNil(t, b) return nil @@ -150,7 +150,7 @@ func TestTxCreateBucket(t *testing.T) { // Ensure that a bucket can be created if it doesn't already exist. func TestTxCreateBucketIfNotExists(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.NoError(t, tx.CreateBucketIfNotExists("widgets")) assert.NoError(t, tx.CreateBucketIfNotExists("widgets")) assert.Equal(t, tx.CreateBucketIfNotExists(""), ErrBucketNameRequired) @@ -158,7 +158,7 @@ func TestTxCreateBucketIfNotExists(t *testing.T) { }) // Read the bucket through a separate transaction. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { b := tx.Bucket("widgets") assert.NotNil(t, b) return nil @@ -170,13 +170,13 @@ func TestTxCreateBucketIfNotExists(t *testing.T) { func TestTxRecreateBucket(t *testing.T) { withOpenDB(func(db *DB, path string) { // Create a bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.NoError(t, tx.CreateBucket("widgets")) return nil }) // Create the same bucket again. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.Equal(t, ErrBucketExists, tx.CreateBucket("widgets")) return nil }) @@ -186,7 +186,7 @@ func TestTxRecreateBucket(t *testing.T) { // Ensure that a bucket is created with a non-blank name. func TestTxCreateBucketWithoutName(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.Equal(t, ErrBucketNameRequired, tx.CreateBucket("")) return nil }) @@ -196,7 +196,7 @@ func TestTxCreateBucketWithoutName(t *testing.T) { // Ensure that a bucket name is not too long. func TestTxCreateBucketWithLongName(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.NoError(t, tx.CreateBucket(strings.Repeat("X", 255))) assert.Equal(t, ErrBucketNameTooLarge, tx.CreateBucket(strings.Repeat("X", 256))) return nil @@ -208,7 +208,7 @@ func TestTxCreateBucketWithLongName(t *testing.T) { func TestTxDeleteBucket(t *testing.T) { withOpenDB(func(db *DB, path string) { // Create a bucket and add a value. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("foo"), []byte("bar")) return nil @@ -216,19 +216,19 @@ func TestTxDeleteBucket(t *testing.T) { // Save root page id. var root pgid - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { root = tx.Bucket("widgets").root return nil }) // Delete the bucket and make sure we can't get the value. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.NoError(t, tx.DeleteBucket("widgets")) assert.Nil(t, tx.Bucket("widgets")) return nil }) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { // Verify that the bucket's page is free. assert.Equal(t, []pgid{root}, db.freelist.all()) @@ -243,7 +243,7 @@ func TestTxDeleteBucket(t *testing.T) { // Ensure that deleting a bucket on a closed transaction returns an error. func TestTxDeleteBucketClosed(t *testing.T) { withOpenDB(func(db *DB, path string) { - tx, _ := db.RWTx() + tx, _ := db.Begin(true) tx.Commit() assert.Equal(t, tx.DeleteBucket("foo"), ErrTxClosed) }) @@ -252,7 +252,7 @@ func TestTxDeleteBucketClosed(t *testing.T) { // Ensure that deleting a bucket with a read-only transaction returns an error. func TestTxDeleteBucketReadOnly(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { assert.Equal(t, tx.DeleteBucket("foo"), ErrTxNotWritable) return nil }) @@ -262,7 +262,7 @@ func TestTxDeleteBucketReadOnly(t *testing.T) { // Ensure that an error is returned when deleting from a bucket that doesn't exist. func TestTxDeleteBucketNotFound(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { assert.Equal(t, ErrBucketNotFound, tx.DeleteBucket("widgets")) return nil }) @@ -272,10 +272,10 @@ func TestTxDeleteBucketNotFound(t *testing.T) { // Ensure that a Tx cursor can iterate over an empty bucket without error. func TestTxCursorEmptyBucket(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { c := tx.Bucket("widgets").Cursor() k, v := c.First() assert.Nil(t, k) @@ -288,10 +288,10 @@ func TestTxCursorEmptyBucket(t *testing.T) { // Ensure that a Tx cursor can reverse iterate over an empty bucket without error. func TestCursorEmptyBucketReverse(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { c := tx.Bucket("widgets").Cursor() k, v := c.Last() assert.Nil(t, k) @@ -304,14 +304,14 @@ func TestCursorEmptyBucketReverse(t *testing.T) { // Ensure that a Tx cursor can iterate over a single root with a couple elements. func TestTxCursorLeafRoot(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("baz"), []byte{}) tx.Bucket("widgets").Put([]byte("foo"), []byte{0}) tx.Bucket("widgets").Put([]byte("bar"), []byte{1}) return nil }) - tx, _ := db.Tx() + tx, _ := db.Begin(false) c := tx.Bucket("widgets").Cursor() k, v := c.First() @@ -341,14 +341,14 @@ func TestTxCursorLeafRoot(t *testing.T) { // Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements. func TestTxCursorLeafRootReverse(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("baz"), []byte{}) tx.Bucket("widgets").Put([]byte("foo"), []byte{0}) tx.Bucket("widgets").Put([]byte("bar"), []byte{1}) return nil }) - tx, _ := db.Tx() + tx, _ := db.Begin(false) c := tx.Bucket("widgets").Cursor() k, v := c.Last() @@ -378,14 +378,14 @@ func TestTxCursorLeafRootReverse(t *testing.T) { // Ensure that a Tx cursor can restart from the beginning. func TestTxCursorRestart(t *testing.T) { withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") tx.Bucket("widgets").Put([]byte("bar"), []byte{}) tx.Bucket("widgets").Put([]byte("foo"), []byte{}) return nil }) - tx, _ := db.Tx() + tx, _ := db.Begin(false) c := tx.Bucket("widgets").Cursor() k, _ := c.First() @@ -413,7 +413,7 @@ func TestTxCursorIterate(t *testing.T) { f := func(items testdata) bool { withOpenDB(func(db *DB, path string) { // Bulk insert all values. - tx, _ := db.RWTx() + tx, _ := db.Begin(true) tx.CreateBucket("widgets") b := tx.Bucket("widgets") for _, item := range items { @@ -426,7 +426,7 @@ func TestTxCursorIterate(t *testing.T) { // Iterate over all items and check consistency. var index = 0 - tx, _ = db.Tx() + tx, _ = db.Begin(false) c := tx.Bucket("widgets").Cursor() for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() { assert.Equal(t, k, items[index].Key) @@ -454,7 +454,7 @@ func TestTxCursorIterateReverse(t *testing.T) { f := func(items testdata) bool { withOpenDB(func(db *DB, path string) { // Bulk insert all values. - tx, _ := db.RWTx() + tx, _ := db.Begin(true) tx.CreateBucket("widgets") b := tx.Bucket("widgets") for _, item := range items { @@ -467,7 +467,7 @@ func TestTxCursorIterateReverse(t *testing.T) { // Iterate over all items and check consistency. var index = 0 - tx, _ = db.Tx() + tx, _ = db.Begin(false) c := tx.Bucket("widgets").Cursor() for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() { assert.Equal(t, k, items[index].Key) @@ -493,7 +493,7 @@ func BenchmarkTxCursor(b *testing.B) { withOpenDB(func(db *DB, path string) { // Write data to bucket. - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { tx.CreateBucket("widgets") bucket := tx.Bucket("widgets") for i := 0; i < b.N; i++ { @@ -504,7 +504,7 @@ func BenchmarkTxCursor(b *testing.B) { b.ResetTimer() // Iterate over bucket using cursor. - db.With(func(tx *Tx) error { + db.View(func(tx *Tx) error { count := 0 c := tx.Bucket("widgets").Cursor() for k, _ := c.First(); k != nil; k, _ = c.Next() { @@ -523,7 +523,7 @@ func BenchmarkTxPutRandom(b *testing.B) { indexes := rand.Perm(b.N) value := []byte(strings.Repeat("0", 64)) withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) var tx *Tx @@ -533,7 +533,7 @@ func BenchmarkTxPutRandom(b *testing.B) { if tx != nil { tx.Commit() } - tx, _ = db.RWTx() + tx, _ = db.Begin(true) bucket = tx.Bucket("widgets") } bucket.Put([]byte(strconv.Itoa(indexes[i])), value) @@ -546,10 +546,10 @@ func BenchmarkTxPutRandom(b *testing.B) { func BenchmarkTxPutSequential(b *testing.B) { value := []byte(strings.Repeat("0", 64)) withOpenDB(func(db *DB, path string) { - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { return tx.CreateBucket("widgets") }) - db.Do(func(tx *Tx) error { + db.Update(func(tx *Tx) error { bucket := tx.Bucket("widgets") for i := 0; i < b.N; i++ { bucket.Put([]byte(strconv.Itoa(i)), value)