package bolt_test import ( "testing" "time" "github.com/boltdb/bolt" ) // Ensure two functions can perform updates in a single batch. func TestDB_Batch(t *testing.T) { db := NewTestDB() defer db.Close() db.MustCreateBucket([]byte("widgets")) // Iterate over multiple updates in separate goroutines. n := 2 ch := make(chan error) for i := 0; i < n; i++ { go func(i int) { ch <- db.Batch(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Put(u64tob(uint64(i)), []byte{}) }) }(i) } // Check all responses to make sure there's no error. for i := 0; i < n; i++ { if err := <-ch; err != nil { t.Fatal(err) } } // Ensure data is correct. db.MustView(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("widgets")) for i := 0; i < n; i++ { if v := b.Get(u64tob(uint64(i))); v == nil { t.Errorf("key not found: %d", i) } } return nil }) } func TestDB_Batch_Panic(t *testing.T) { db := NewTestDB() defer db.Close() var sentinel int var bork = &sentinel var problem interface{} var err error // Execute a function inside a batch that panics. func() { defer func() { if p := recover(); p != nil { problem = p } }() err = db.Batch(func(tx *bolt.Tx) error { panic(bork) }) }() // Verify there is no error. if g, e := err, error(nil); g != e { t.Fatalf("wrong error: %v != %v", g, e) } // Verify the panic was captured. if g, e := problem, bork; g != e { t.Fatalf("wrong error: %v != %v", g, e) } } func TestDB_BatchFull(t *testing.T) { db := NewTestDB() defer db.Close() db.MustCreateBucket([]byte("widgets")) const size = 3 // buffered so we never leak goroutines ch := make(chan error, size) put := func(i int) { ch <- db.Batch(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Put(u64tob(uint64(i)), []byte{}) }) } db.MaxBatchSize = size // high enough to never trigger here db.MaxBatchDelay = 1 * time.Hour go put(1) go put(2) // Give the batch a chance to exhibit bugs. time.Sleep(10 * time.Millisecond) // not triggered yet select { case <-ch: t.Fatalf("batch triggered too early") default: } go put(3) // Check all responses to make sure there's no error. for i := 0; i < size; i++ { if err := <-ch; err != nil { t.Fatal(err) } } // Ensure data is correct. db.MustView(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("widgets")) for i := 1; i <= size; i++ { if v := b.Get(u64tob(uint64(i))); v == nil { t.Errorf("key not found: %d", i) } } return nil }) } func TestDB_BatchTime(t *testing.T) { db := NewTestDB() defer db.Close() db.MustCreateBucket([]byte("widgets")) const size = 1 // buffered so we never leak goroutines ch := make(chan error, size) put := func(i int) { ch <- db.Batch(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Put(u64tob(uint64(i)), []byte{}) }) } db.MaxBatchSize = 1000 db.MaxBatchDelay = 0 go put(1) // Batch must trigger by time alone. // Check all responses to make sure there's no error. for i := 0; i < size; i++ { if err := <-ch; err != nil { t.Fatal(err) } } // Ensure data is correct. db.MustView(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("widgets")) for i := 1; i <= size; i++ { if v := b.Get(u64tob(uint64(i))); v == nil { t.Errorf("key not found: %d", i) } } return nil }) }