mirror of https://github.com/hak5/bolt.git
Add Tx.OnCommit() handler.
This commit adds the ability to execute a function after a transaction has successfully committed.master
parent
d667ae0fe1
commit
394e42e3eb
24
tx.go
24
tx.go
|
@ -38,6 +38,7 @@ type Tx struct {
|
||||||
pages map[pgid]*page
|
pages map[pgid]*page
|
||||||
pending []*node
|
pending []*node
|
||||||
stats TxStats
|
stats TxStats
|
||||||
|
commitHandlers []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// init initializes the transaction.
|
// init initializes the transaction.
|
||||||
|
@ -175,6 +176,11 @@ func (t *Tx) DeleteBucket(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnCommit adds a handler function to be executed after the transaction successfully commits.
|
||||||
|
func (t *Tx) OnCommit(fn func()) {
|
||||||
|
t.commitHandlers = append(t.commitHandlers, fn)
|
||||||
|
}
|
||||||
|
|
||||||
// Commit writes all changes to disk and updates the meta page.
|
// Commit writes all changes to disk and updates the meta page.
|
||||||
// Returns an error if a disk write error occurs.
|
// Returns an error if a disk write error occurs.
|
||||||
func (t *Tx) Commit() error {
|
func (t *Tx) Commit() error {
|
||||||
|
@ -185,7 +191,6 @@ func (t *Tx) Commit() error {
|
||||||
} else if !t.writable {
|
} else if !t.writable {
|
||||||
return ErrTxNotWritable
|
return ErrTxNotWritable
|
||||||
}
|
}
|
||||||
defer t.close()
|
|
||||||
|
|
||||||
// TODO(benbjohnson): Use vectorized I/O to write out dirty pages.
|
// TODO(benbjohnson): Use vectorized I/O to write out dirty pages.
|
||||||
|
|
||||||
|
@ -197,6 +202,7 @@ func (t *Tx) Commit() error {
|
||||||
// spill data onto dirty pages.
|
// spill data onto dirty pages.
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
if err := t.spill(); err != nil {
|
if err := t.spill(); err != nil {
|
||||||
|
t.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.stats.SpillTime += time.Since(startTime)
|
t.stats.SpillTime += time.Since(startTime)
|
||||||
|
@ -204,6 +210,7 @@ func (t *Tx) Commit() error {
|
||||||
// Spill buckets page.
|
// Spill buckets page.
|
||||||
p, err := t.allocate((t.buckets.size() / t.db.pageSize) + 1)
|
p, err := t.allocate((t.buckets.size() / t.db.pageSize) + 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.buckets.write(p)
|
t.buckets.write(p)
|
||||||
|
@ -217,6 +224,7 @@ func (t *Tx) Commit() error {
|
||||||
t.db.freelist.free(t.id(), t.page(t.meta.freelist))
|
t.db.freelist.free(t.id(), t.page(t.meta.freelist))
|
||||||
p, err = t.allocate((t.db.freelist.size() / t.db.pageSize) + 1)
|
p, err = t.allocate((t.db.freelist.size() / t.db.pageSize) + 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.db.freelist.write(p)
|
t.db.freelist.write(p)
|
||||||
|
@ -225,15 +233,25 @@ func (t *Tx) Commit() error {
|
||||||
// Write dirty pages to disk.
|
// Write dirty pages to disk.
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
if err := t.write(); err != nil {
|
if err := t.write(); err != nil {
|
||||||
|
t.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write meta to disk.
|
// Write meta to disk.
|
||||||
if err := t.writeMeta(); err != nil {
|
if err := t.writeMeta(); err != nil {
|
||||||
|
t.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.stats.WriteTime += time.Since(startTime)
|
t.stats.WriteTime += time.Since(startTime)
|
||||||
|
|
||||||
|
// Finalize the transaction.
|
||||||
|
t.close()
|
||||||
|
|
||||||
|
// Execute commit handlers now that the locks have been removed.
|
||||||
|
for _, fn := range t.commitHandlers {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,13 +268,13 @@ func (t *Tx) Rollback() error {
|
||||||
|
|
||||||
func (t *Tx) close() {
|
func (t *Tx) close() {
|
||||||
if t.writable {
|
if t.writable {
|
||||||
t.db.rwlock.Unlock()
|
|
||||||
|
|
||||||
// Merge statistics.
|
// Merge statistics.
|
||||||
t.db.metalock.Lock()
|
t.db.metalock.Lock()
|
||||||
t.db.stats.TxStats.add(&t.stats)
|
t.db.stats.TxStats.add(&t.stats)
|
||||||
t.db.metalock.Unlock()
|
t.db.metalock.Unlock()
|
||||||
|
|
||||||
|
// Remove writer lock.
|
||||||
|
t.db.rwlock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
t.db.removeTx(t)
|
t.db.removeTx(t)
|
||||||
}
|
}
|
||||||
|
|
28
tx_test.go
28
tx_test.go
|
@ -1,6 +1,7 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
@ -484,6 +485,33 @@ func TestTxCursorIterateReverse(t *testing.T) {
|
||||||
fmt.Fprint(os.Stderr, "\n")
|
fmt.Fprint(os.Stderr, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that Tx commit handlers are called after a transaction successfully commits.
|
||||||
|
func TestTx_OnCommit(t *testing.T) {
|
||||||
|
var x int
|
||||||
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
db.Update(func(tx *Tx) error {
|
||||||
|
tx.OnCommit(func() { x += 1 })
|
||||||
|
tx.OnCommit(func() { x += 2 })
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
assert.Equal(t, 3, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that Tx commit handlers are NOT called after a transaction rolls back.
|
||||||
|
func TestTx_OnCommit_Rollback(t *testing.T) {
|
||||||
|
var x int
|
||||||
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
db.Update(func(tx *Tx) error {
|
||||||
|
tx.OnCommit(func() { x += 1 })
|
||||||
|
tx.OnCommit(func() { x += 2 })
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
return errors.New("rollback this commit")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
assert.Equal(t, 0, x)
|
||||||
|
}
|
||||||
|
|
||||||
// Benchmark the performance iterating over a cursor.
|
// Benchmark the performance iterating over a cursor.
|
||||||
func BenchmarkTxCursor(b *testing.B) {
|
func BenchmarkTxCursor(b *testing.B) {
|
||||||
indexes := rand.Perm(b.N)
|
indexes := rand.Perm(b.N)
|
||||||
|
|
Loading…
Reference in New Issue