Codebase list golang-github-boltdb-bolt / 57376f0
Rename Transaction to Tx. I changed the Transaction/RWTransaction types to Tx/RWTx, respectively. This makes the naming more consistent with other packages such as database/sql. The txnid is changed to txid as well. Ben Johnson 10 years ago
17 changed file(s) with 269 addition(s) and 283 deletion(s). Raw diff Collapse all Expand all
+0
-13
TODO less more
0 TODO
1 ====
2 X Open DB.
3 X Initialize transaction.
4 - Cursor First, Get(key), Next
5 - RWTransaction.insert()
6 - rebalance
7 - adjust cursors
8 - RWTransaction Commmit
9
10
11
12
66 // Bucket represents a collection of key/value pairs inside the database.
77 type Bucket struct {
88 *bucket
9 name string
10 transaction *Transaction
11 rwtransaction *RWTransaction
9 name string
10 tx *Tx
11 rwtx *RWTx
1212 }
1313
1414 // bucket represents the on-file representation of a bucket.
2424
2525 // Writable returns whether the bucket is writable.
2626 func (b *Bucket) Writable() bool {
27 return (b.rwtransaction != nil)
27 return (b.rwtx != nil)
2828 }
2929
3030 // Cursor creates a cursor associated with the bucket.
31 // The cursor is only valid as long as the Transaction is open.
31 // The cursor is only valid as long as the transaction is open.
3232 // Do not use a cursor after the transaction is closed.
3333 func (b *Bucket) Cursor() *Cursor {
3434 return &Cursor{
35 transaction: b.transaction,
36 root: b.root,
37 stack: make([]elemRef, 0),
35 tx: b.tx,
36 root: b.root,
37 stack: make([]elemRef, 0),
3838 }
3939 }
4040
7373 c.Seek(key)
7474
7575 // Insert the key/value.
76 c.node(b.rwtransaction).put(key, key, value, 0)
76 c.node(b.rwtx).put(key, key, value, 0)
7777
7878 return nil
7979 }
9191 c.Seek(key)
9292
9393 // Delete the node if we have a matching key.
94 c.node(b.rwtransaction).del(key)
94 c.node(b.rwtx).del(key)
9595
9696 return nil
9797 }
129129 // Stat returns stats on a bucket.
130130 func (b *Bucket) Stat() *BucketStat {
131131 s := &BucketStat{}
132 b.transaction.forEachPage(b.root, 0, func(p *page, depth int) {
132 b.tx.forEachPage(b.root, 0, func(p *page, depth int) {
133133 if (p.flags & leafPageFlag) != 0 {
134134 s.LeafPageCount++
135135 s.KeyCount += int(p.count)
2626 func TestBucketGetFromNode(t *testing.T) {
2727 withOpenDB(func(db *DB, path string) {
2828 db.CreateBucket("widgets")
29 db.Do(func(txn *RWTransaction) error {
29 db.Do(func(txn *RWTx) error {
3030 b := txn.Bucket("widgets")
3131 b.Put([]byte("foo"), []byte("bar"))
3232 value := b.Get([]byte("foo"))
5353 func TestBucketPutReadOnly(t *testing.T) {
5454 withOpenDB(func(db *DB, path string) {
5555 db.CreateBucket("widgets")
56 db.With(func(txn *Transaction) error {
56 db.With(func(txn *Tx) error {
5757 b := txn.Bucket("widgets")
5858 err := b.Put([]byte("foo"), []byte("bar"))
5959 assert.Equal(t, err, ErrBucketNotWritable)
8080 func TestBucketDeleteReadOnly(t *testing.T) {
8181 withOpenDB(func(db *DB, path string) {
8282 db.CreateBucket("widgets")
83 db.With(func(txn *Transaction) error {
83 db.With(func(txn *Tx) error {
8484 b := txn.Bucket("widgets")
8585 err := b.Delete([]byte("foo"))
8686 assert.Equal(t, err, ErrBucketNotWritable)
119119 func TestBucketNextSequenceReadOnly(t *testing.T) {
120120 withOpenDB(func(db *DB, path string) {
121121 db.CreateBucket("widgets")
122 db.With(func(txn *Transaction) error {
122 db.With(func(txn *Tx) error {
123123 b := txn.Bucket("widgets")
124124 i, err := b.NextSequence()
125125 assert.Equal(t, i, 0)
133133 func TestBucketNextSequenceOverflow(t *testing.T) {
134134 withOpenDB(func(db *DB, path string) {
135135 db.CreateBucket("widgets")
136 db.Do(func(txn *RWTransaction) error {
136 db.Do(func(txn *RWTx) error {
137137 b := txn.Bucket("widgets")
138138 b.bucket.sequence = uint64(maxInt)
139139 seq, err := b.NextSequence()
217217 // Ensure a bucket can calculate stats.
218218 func TestBucketStat(t *testing.T) {
219219 withOpenDB(func(db *DB, path string) {
220 db.Do(func(txn *RWTransaction) error {
220 db.Do(func(txn *RWTx) error {
221221 // Add bucket with lots of keys.
222222 txn.CreateBucket("widgets")
223223 b := txn.Bucket("widgets")
240240
241241 return nil
242242 })
243 db.With(func(txn *Transaction) error {
243 db.With(func(txn *Tx) error {
244244 b := txn.Bucket("widgets")
245245 stat := b.Stat()
246246 assert.Equal(t, stat.BranchPageCount, 15)
316316 withOpenDB(func(db *DB, path string) {
317317 // Bulk insert all values.
318318 db.CreateBucket("widgets")
319 rwtxn, _ := db.RWTransaction()
319 rwtxn, _ := db.RWTx()
320320 b := rwtxn.Bucket("widgets")
321321 for _, item := range items {
322322 assert.NoError(t, b.Put(item.Key, item.Value))
324324 assert.NoError(t, rwtxn.Commit())
325325
326326 // Verify all items exist.
327 txn, _ := db.Transaction()
327 txn, _ := db.Tx()
328328 b = txn.Bucket("widgets")
329329 for _, item := range items {
330330 value := b.Get(item.Key)
350350 withOpenDB(func(db *DB, path string) {
351351 // Bulk insert all values.
352352 db.CreateBucket("widgets")
353 rwtxn, _ := db.RWTransaction()
353 rwtxn, _ := db.RWTx()
354354 b := rwtxn.Bucket("widgets")
355355 for _, item := range items {
356356 assert.NoError(t, b.Put(item.Key, item.Value))
362362 assert.NoError(t, db.Delete("widgets", item.Key))
363363
364364 // Anything before our deletion index should be nil.
365 txn, _ := db.Transaction()
365 txn, _ := db.Tx()
366366 b := txn.Bucket("widgets")
367367 for j, exp := range items {
368368 if j > i {
55 )
66
77 // Cursor represents an iterator that can traverse over all key/value pairs in a bucket in sorted order.
8 // Cursors can be obtained from a Transaction and are valid as long as the Transaction is open.
8 // Cursors can be obtained from a transaction and are valid as long as the transaction is open.
99 type Cursor struct {
10 transaction *Transaction
11 root pgid
12 stack []elemRef
10 tx *Tx
11 root pgid
12 stack []elemRef
1313 }
1414
1515 // First moves the cursor to the first item in the bucket and returns its key and value.
1616 // If the bucket is empty then a nil key and value are returned.
1717 func (c *Cursor) First() (key []byte, value []byte) {
1818 c.stack = c.stack[:0]
19 p, n := c.transaction.pageNode(c.root)
19 p, n := c.tx.pageNode(c.root)
2020 c.stack = append(c.stack, elemRef{page: p, node: n, index: 0})
2121 c.first()
2222 return c.keyValue()
2626 // If the bucket is empty then a nil key and value are returned.
2727 func (c *Cursor) Last() (key []byte, value []byte) {
2828 c.stack = c.stack[:0]
29 p, n := c.transaction.pageNode(c.root)
29 p, n := c.tx.pageNode(c.root)
3030 ref := elemRef{page: p, node: n}
3131 ref.index = ref.count() - 1
3232 c.stack = append(c.stack, ref)
115115 } else {
116116 pgid = ref.page.branchPageElement(uint16(ref.index)).pgid
117117 }
118 p, n := c.transaction.pageNode(pgid)
118 p, n := c.tx.pageNode(pgid)
119119 c.stack = append(c.stack, elemRef{page: p, node: n, index: 0})
120120 }
121121 }
136136 } else {
137137 pgid = ref.page.branchPageElement(uint16(ref.index)).pgid
138138 }
139 p, n := c.transaction.pageNode(pgid)
139 p, n := c.tx.pageNode(pgid)
140140
141141 var nextRef = elemRef{page: p, node: n}
142142 nextRef.index = nextRef.count() - 1
146146
147147 // search recursively performs a binary search against a given page/node until it finds a given key.
148148 func (c *Cursor) search(key []byte, pgid pgid) {
149 p, n := c.transaction.pageNode(pgid)
149 p, n := c.tx.pageNode(pgid)
150150 if p != nil {
151151 _assert((p.flags&(branchPageFlag|leafPageFlag)) != 0, "invalid page type: "+p.typ())
152152 }
250250 }
251251
252252 // node returns the node that the cursor is currently positioned on.
253 func (c *Cursor) node(t *RWTransaction) *node {
253 func (c *Cursor) node(t *RWTx) *node {
254254 _assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack")
255255
256256 // If the top of the stack is a leaf node then just return it.
+58
-58
db.go less more
1818 // All data access is performed through transactions which can be obtained through the DB.
1919 // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called.
2020 type DB struct {
21 os _os
22 syscall _syscall
23 path string
24 file file
25 metafile file
26 data []byte
27 meta0 *meta
28 meta1 *meta
29 pageSize int
30 opened bool
31 rwtransaction *RWTransaction
32 transactions []*Transaction
33 freelist *freelist
21 os _os
22 syscall _syscall
23 path string
24 file file
25 metafile file
26 data []byte
27 meta0 *meta
28 meta1 *meta
29 pageSize int
30 opened bool
31 rwtx *RWTx
32 txs []*Tx
33 freelist *freelist
3434
3535 rwlock sync.Mutex // Allows only one writer at a time.
3636 metalock sync.Mutex // Protects meta page access.
111111 }
112112
113113 // Read in the freelist.
114 db.freelist = &freelist{pending: make(map[txnid][]pgid)}
114 db.freelist = &freelist{pending: make(map[txid][]pgid)}
115115 db.freelist.read(db.page(db.meta().freelist))
116116
117117 // Mark the database as opened and return.
126126 defer db.mmaplock.Unlock()
127127
128128 // Dereference all mmap references before unmapping.
129 if db.rwtransaction != nil {
130 db.rwtransaction.dereference()
129 if db.rwtx != nil {
130 db.rwtx.dereference()
131131 }
132132
133133 // Unmap existing data before continuing.
217217 m.freelist = 2
218218 m.buckets = 3
219219 m.pgid = 4
220 m.txnid = txnid(i)
220 m.txid = txid(i)
221221 }
222222
223223 // Write an empty freelist at page 3.
258258 db.munmap()
259259 }
260260
261 // Transaction creates a read-only transaction.
261 // Tx creates a read-only transaction.
262262 // Multiple read-only transactions can be used concurrently.
263263 //
264264 // IMPORTANT: You must close the transaction after you are finished or else the database will not reclaim old pages.
265 func (db *DB) Transaction() (*Transaction, error) {
265 func (db *DB) Tx() (*Tx, error) {
266266 db.metalock.Lock()
267267 defer db.metalock.Unlock()
268268
277277 }
278278
279279 // Create a transaction associated with the database.
280 t := &Transaction{}
280 t := &Tx{}
281281 t.init(db)
282282
283283 // Keep track of transaction until it closes.
284 db.transactions = append(db.transactions, t)
284 db.txs = append(db.txs, t)
285285
286286 return t, nil
287287 }
288288
289 // RWTransaction creates a read/write transaction.
289 // RWTx creates a read/write transaction.
290290 // Only one read/write transaction is allowed at a time.
291291 // You must call Commit() or Rollback() on the transaction to close it.
292 func (db *DB) RWTransaction() (*RWTransaction, error) {
292 func (db *DB) RWTx() (*RWTx, error) {
293293 db.metalock.Lock()
294294 defer db.metalock.Unlock()
295295
296 // Obtain writer lock. This is released by the RWTransaction when it closes.
296 // Obtain writer lock. This is released by the RWTx when it closes.
297297 db.rwlock.Lock()
298298
299299 // Exit if the database is not open yet.
303303 }
304304
305305 // Create a transaction associated with the database.
306 t := &RWTransaction{}
306 t := &RWTx{}
307307 t.init(db)
308 db.rwtransaction = t
308 db.rwtx = t
309309
310310 // Free any pages associated with closed read-only transactions.
311 var minid txnid = 0xFFFFFFFFFFFFFFFF
312 for _, t := range db.transactions {
311 var minid txid = 0xFFFFFFFFFFFFFFFF
312 for _, t := range db.txs {
313313 if t.id() < minid {
314314 minid = t.id()
315315 }
321321 return t, nil
322322 }
323323
324 // removeTransaction removes a transaction from the database.
325 func (db *DB) removeTransaction(t *Transaction) {
324 // removeTx removes a transaction from the database.
325 func (db *DB) removeTx(t *Tx) {
326326 db.metalock.Lock()
327327 defer db.metalock.Unlock()
328328
330330 db.mmaplock.RUnlock()
331331
332332 // Remove the transaction.
333 for i, txn := range db.transactions {
333 for i, txn := range db.txs {
334334 if txn == t {
335 db.transactions = append(db.transactions[:i], db.transactions[i+1:]...)
335 db.txs = append(db.txs[:i], db.txs[i+1:]...)
336336 break
337337 }
338338 }
339339 }
340340
341 // Do executes a function within the context of a RWTransaction.
341 // Do executes a function within the context of a RWTx.
342342 // If no error is returned from the function then the transaction is committed.
343343 // If an error is returned then the entire transaction is rolled back.
344344 // Any error that is returned from the function or returned from the commit is
345345 // returned from the Do() method.
346 func (db *DB) Do(fn func(*RWTransaction) error) error {
347 t, err := db.RWTransaction()
346 func (db *DB) Do(fn func(*RWTx) error) error {
347 t, err := db.RWTx()
348348 if err != nil {
349349 return err
350350 }
358358 return t.Commit()
359359 }
360360
361 // With executes a function within the context of a Transaction.
361 // With executes a function within the context of a transaction.
362362 // Any error that is returned from the function is returned from the With() method.
363 func (db *DB) With(fn func(*Transaction) error) error {
364 t, err := db.Transaction()
363 func (db *DB) With(fn func(*Tx) error) error {
364 t, err := db.Tx()
365365 if err != nil {
366366 return err
367367 }
374374 // ForEach executes a function for each key/value pair in a bucket.
375375 // An error is returned if the bucket cannot be found.
376376 func (db *DB) ForEach(name string, fn func(k, v []byte) error) error {
377 return db.With(func(t *Transaction) error {
377 return db.With(func(t *Tx) error {
378378 b := t.Bucket(name)
379379 if b == nil {
380380 return ErrBucketNotFound
386386 // Bucket retrieves a reference to a bucket.
387387 // This is typically useful for checking the existence of a bucket.
388388 func (db *DB) Bucket(name string) (*Bucket, error) {
389 t, err := db.Transaction()
389 t, err := db.Tx()
390390 if err != nil {
391391 return nil, err
392392 }
396396
397397 // Buckets retrieves a list of all buckets in the database.
398398 func (db *DB) Buckets() ([]*Bucket, error) {
399 t, err := db.Transaction()
399 t, err := db.Tx()
400400 if err != nil {
401401 return nil, err
402402 }
408408 // This function can return an error if the bucket already exists, if the name
409409 // is blank, or the bucket name is too long.
410410 func (db *DB) CreateBucket(name string) error {
411 return db.Do(func(t *RWTransaction) error {
411 return db.Do(func(t *RWTx) error {
412412 return t.CreateBucket(name)
413413 })
414414 }
416416 // CreateBucketIfNotExists creates a new bucket with the given name if it doesn't already exist.
417417 // This function can return an error if the name is blank, or the bucket name is too long.
418418 func (db *DB) CreateBucketIfNotExists(name string) error {
419 return db.Do(func(t *RWTransaction) error {
419 return db.Do(func(t *RWTx) error {
420420 return t.CreateBucketIfNotExists(name)
421421 })
422422 }
424424 // DeleteBucket removes a bucket from the database.
425425 // Returns an error if the bucket does not exist.
426426 func (db *DB) DeleteBucket(name string) error {
427 return db.Do(func(t *RWTransaction) error {
427 return db.Do(func(t *RWTx) error {
428428 return t.DeleteBucket(name)
429429 })
430430 }
433433 // This function can return an error if the bucket does not exist.
434434 func (db *DB) NextSequence(name string) (int, error) {
435435 var seq int
436 err := db.Do(func(t *RWTransaction) error {
436 err := db.Do(func(t *RWTx) error {
437437 b := t.Bucket(name)
438438 if b == nil {
439439 return ErrBucketNotFound
452452 // Get retrieves the value for a key in a bucket.
453453 // Returns an error if the key does not exist.
454454 func (db *DB) Get(name string, key []byte) ([]byte, error) {
455 t, err := db.Transaction()
455 t, err := db.Tx()
456456 if err != nil {
457457 return nil, err
458458 }
481481 // Put sets the value for a key in a bucket.
482482 // Returns an error if the bucket is not found, if key is blank, if the key is too large, or if the value is too large.
483483 func (db *DB) Put(name string, key []byte, value []byte) error {
484 return db.Do(func(t *RWTransaction) error {
484 return db.Do(func(t *RWTx) error {
485485 b := t.Bucket(name)
486486 if b == nil {
487487 return ErrBucketNotFound
493493 // Delete removes a key from a bucket.
494494 // Returns an error if the bucket cannot be found.
495495 func (db *DB) Delete(name string, key []byte) error {
496 return db.Do(func(t *RWTransaction) error {
496 return db.Do(func(t *RWTx) error {
497497 b := t.Bucket(name)
498498 if b == nil {
499499 return ErrBucketNotFound
507507 // using the database while a copy is in progress.
508508 func (db *DB) Copy(w io.Writer) error {
509509 // Maintain a reader transaction so pages don't get reclaimed.
510 t, err := db.Transaction()
510 t, err := db.Tx()
511511 if err != nil {
512512 return err
513513 }
548548 db.mmaplock.RLock()
549549
550550 var s = &Stat{
551 MmapSize: len(db.data),
552 TransactionCount: len(db.transactions),
551 MmapSize: len(db.data),
552 TxCount: len(db.txs),
553553 }
554554
555555 // Release locks.
556556 db.mmaplock.RUnlock()
557557 db.metalock.Unlock()
558558
559 err := db.Do(func(t *RWTransaction) error {
559 err := db.Do(func(t *RWTx) error {
560560 s.PageCount = int(t.meta.pgid)
561561 s.FreePageCount = len(db.freelist.all())
562562 s.PageSize = db.pageSize
581581
582582 // meta retrieves the current meta page reference.
583583 func (db *DB) meta() *meta {
584 if db.meta0.txnid > db.meta1.txnid {
584 if db.meta0.txid > db.meta1.txid {
585585 return db.meta0
586586 }
587587 return db.meta1
600600 }
601601
602602 // Resize mmap() if we're at the end.
603 p.id = db.rwtransaction.meta.pgid
603 p.id = db.rwtx.meta.pgid
604604 var minsz = int((p.id+pgid(count))+1) * db.pageSize
605605 if minsz >= len(db.data) {
606606 if err := db.mmap(minsz); err != nil {
609609 }
610610
611611 // Move the page id high water mark.
612 db.rwtransaction.meta.pgid += pgid(count)
612 db.rwtx.meta.pgid += pgid(count)
613613
614614 return p, nil
615615 }
633633 // resize it.
634634 MmapSize int
635635
636 // TransactionCount is the total number of reader transactions.
637 TransactionCount int
638 }
636 // TxCount is the total number of reader transactions.
637 TxCount int
638 }
155155 }
156156
157157 // Ensure that a database cannot open a transaction when it's not open.
158 func TestDBTransactionErrDatabaseNotOpen(t *testing.T) {
159 withDB(func(db *DB, path string) {
160 txn, err := db.Transaction()
158 func TestDBTxErrDatabaseNotOpen(t *testing.T) {
159 withDB(func(db *DB, path string) {
160 txn, err := db.Tx()
161161 assert.Nil(t, txn)
162162 assert.Equal(t, err, ErrDatabaseNotOpen)
163163 })
172172 }
173173
174174 // Ensure a database can provide a transactional block.
175 func TestDBTransactionBlock(t *testing.T) {
176 withOpenDB(func(db *DB, path string) {
177 err := db.Do(func(txn *RWTransaction) error {
175 func TestDBTxBlock(t *testing.T) {
176 withOpenDB(func(db *DB, path string) {
177 err := db.Do(func(txn *RWTx) error {
178178 txn.CreateBucket("widgets")
179179 b := txn.Bucket("widgets")
180180 b.Put([]byte("foo"), []byte("bar"))
191191 }
192192
193193 // Ensure a closed database returns an error while running a transaction block
194 func TestDBTransactionBlockWhileClosed(t *testing.T) {
195 withDB(func(db *DB, path string) {
196 err := db.Do(func(txn *RWTransaction) error {
194 func TestDBTxBlockWhileClosed(t *testing.T) {
195 withDB(func(db *DB, path string) {
196 err := db.Do(func(txn *RWTx) error {
197197 txn.CreateBucket("widgets")
198198 return nil
199199 })
275275 // Ensure the database can return stats about itself.
276276 func TestDBStat(t *testing.T) {
277277 withOpenDB(func(db *DB, path string) {
278 db.Do(func(txn *RWTransaction) error {
278 db.Do(func(txn *RWTx) error {
279279 txn.CreateBucket("widgets")
280280 b := txn.Bucket("widgets")
281281 for i := 0; i < 10000; i++ {
289289 db.Delete("widgets", []byte("1000"))
290290
291291 // Open some readers.
292 t0, _ := db.Transaction()
293 t1, _ := db.Transaction()
294 t2, _ := db.Transaction()
292 t0, _ := db.Tx()
293 t1, _ := db.Tx()
294 t2, _ := db.Tx()
295295 t2.Close()
296296
297297 // Obtain stats.
301301 assert.Equal(t, stat.FreePageCount, 2)
302302 assert.Equal(t, stat.PageSize, 4096)
303303 assert.Equal(t, stat.MmapSize, 4194304)
304 assert.Equal(t, stat.TransactionCount, 2)
304 assert.Equal(t, stat.TxCount, 2)
305305
306306 // Close readers.
307307 t0.Close()
11 Package bolt implements a low-level key/value store in pure Go. It supports
22 fully serializable transactions, ACID semantics, and lock-free MVCC with
33 multiple readers and a single writer. Bolt can be used for projects that
4 want a simple data store without the need to add large dependencies such as
4 want a simple data store without the need to add large dependencies such as
55 Postgres or MySQL.
66
77 Bolt is a single-level, zero-copy, B+tree data store. This means that Bolt is
1313
1414 Basics
1515
16 There are only a few types in Bolt: DB, Bucket, Transaction, RWTransaction, and
17 Cursor. The DB is a collection of buckets and is represented by a single file
18 on disk. A bucket is a collection of unique keys that are associated with values.
16 There are only a few types in Bolt: DB, Bucket, Tx, RWTx, and Cursor. The DB is
17 a collection of buckets and is represented by a single file on disk. A bucket is
18 a collection of unique keys that are associated with values.
1919
20 Transactions provide read-only access to data inside the database. They can
21 retrieve key/value pairs and can use Cursors to iterate over the entire dataset.
22 RWTransactions provide read-write access to the database. They can create and
23 delete buckets and they can insert and remove keys. Only one RWTransaction is
24 allowed at a time.
20 Txs provide read-only access to data inside the database. They can retrieve
21 key/value pairs and can use Cursors to iterate over the entire dataset. RWTxs
22 provide read-write access to the database. They can create and delete buckets
23 and they can insert and remove keys. Only one RWTx is allowed at a time.
2524
2625
2726 Caveats
6666 defer db.Close()
6767
6868 // Execute several commands within a write transaction.
69 err := db.Do(func(t *RWTransaction) error {
69 err := db.Do(func(t *RWTx) error {
7070 if err := t.CreateBucket("widgets"); err != nil {
7171 return err
7272 }
9999 db.Put("people", []byte("susy"), []byte("que"))
100100
101101 // Access data from within a read-only transactional block.
102 db.With(func(t *Transaction) error {
102 db.With(func(t *Tx) error {
103103 v := t.Bucket("people").Get([]byte("john"))
104104 fmt.Printf("John's last name is %s.\n", string(v))
105105 return nil
133133 // A liger is awesome.
134134 }
135135
136 func ExampleRWTransaction() {
137 // Open the database.
138 var db DB
139 db.Open("/tmp/bolt/rwtransaction.db", 0666)
136 func ExampleRWTx() {
137 // Open the database.
138 var db DB
139 db.Open("/tmp/bolt/rwtx.db", 0666)
140140 defer db.Close()
141141
142142 // Create a bucket.
143143 db.CreateBucket("widgets")
144144
145145 // Create several keys in a transaction.
146 rwtxn, _ := db.RWTransaction()
146 rwtxn, _ := db.RWTx()
147147 b := rwtxn.Bucket("widgets")
148148 b.Put([]byte("john"), []byte("blue"))
149149 b.Put([]byte("abby"), []byte("red"))
151151 rwtxn.Commit()
152152
153153 // Iterate over the values in sorted key order.
154 txn, _ := db.Transaction()
154 txn, _ := db.Tx()
155155 c := txn.Bucket("widgets").Cursor()
156156 for k, v := c.First(); k != nil; k, v = c.Next() {
157157 fmt.Printf("%s likes %s\n", string(k), string(v))
164164 // zephyr likes purple
165165 }
166166
167 func ExampleRWTransaction_rollback() {
168 // Open the database.
169 var db DB
170 db.Open("/tmp/bolt/rwtransaction_rollback.db", 0666)
167 func ExampleRWTx_rollback() {
168 // Open the database.
169 var db DB
170 db.Open("/tmp/bolt/rwtx_rollback.db", 0666)
171171 defer db.Close()
172172
173173 // Create a bucket.
177177 db.Put("widgets", []byte("foo"), []byte("bar"))
178178
179179 // Update the key but rollback the transaction so it never saves.
180 rwtxn, _ := db.RWTransaction()
180 rwtxn, _ := db.RWTx()
181181 b := rwtxn.Bucket("widgets")
182182 b.Put([]byte("foo"), []byte("baz"))
183183 rwtxn.Rollback()
88 // It also tracks pages that have been freed but are still in use by open transactions.
99 type freelist struct {
1010 ids []pgid
11 pending map[txnid][]pgid
11 pending map[txid][]pgid
1212 }
1313
1414 // all returns a list of all free ids and all pending ids in one sorted list.
4949 }
5050
5151 // free releases a page and its overflow for a given transaction id.
52 func (f *freelist) free(txnid txnid, p *page) {
53 var ids = f.pending[txnid]
52 func (f *freelist) free(txid txid, p *page) {
53 var ids = f.pending[txid]
5454 _assert(p.id > 1, "cannot free page 0 or 1: %d", p.id)
5555 for i := 0; i < int(p.overflow+1); i++ {
5656 ids = append(ids, p.id+pgid(i))
5757 }
58 f.pending[txnid] = ids
58 f.pending[txid] = ids
5959 }
6060
6161 // release moves all page ids for a transaction id (or older) to the freelist.
62 func (f *freelist) release(txnid txnid) {
62 func (f *freelist) release(txid txid) {
6363 for tid, ids := range f.pending {
64 if tid <= txnid {
64 if tid <= txid {
6565 f.ids = append(f.ids, ids...)
6666 delete(f.pending, tid)
6767 }
88
99 // Ensure that a page is added to a transaction's freelist.
1010 func TestFreelistFree(t *testing.T) {
11 f := &freelist{pending: make(map[txnid][]pgid)}
11 f := &freelist{pending: make(map[txid][]pgid)}
1212 f.free(100, &page{id: 12})
1313 assert.Equal(t, f.pending[100], []pgid{12})
1414 }
1515
1616 // Ensure that a page and its overflow is added to a transaction's freelist.
1717 func TestFreelistFreeOverflow(t *testing.T) {
18 f := &freelist{pending: make(map[txnid][]pgid)}
18 f := &freelist{pending: make(map[txid][]pgid)}
1919 f.free(100, &page{id: 12, overflow: 3})
2020 assert.Equal(t, f.pending[100], []pgid{12, 13, 14, 15})
2121 }
2222
2323 // Ensure that a transaction's free pages can be released.
2424 func TestFreelistRelease(t *testing.T) {
25 f := &freelist{pending: make(map[txnid][]pgid)}
25 f := &freelist{pending: make(map[txid][]pgid)}
2626 f.free(100, &page{id: 12, overflow: 1})
2727 f.free(100, &page{id: 9})
2828 f.free(102, &page{id: 39})
6060 ids[1] = 50
6161
6262 // Deserialize page into a freelist.
63 f := &freelist{pending: make(map[txnid][]pgid)}
63 f := &freelist{pending: make(map[txid][]pgid)}
6464 f.read(page)
6565
6666 // Ensure that there are two page ids in the freelist.
7373 func TestFreelistWrite(t *testing.T) {
7474 // Create a freelist and write it to a page.
7575 var buf [4096]byte
76 f := &freelist{ids: []pgid{12, 39}, pending: make(map[txnid][]pgid)}
76 f := &freelist{ids: []pgid{12, 39}, pending: make(map[txid][]pgid)}
7777 f.pending[100] = []pgid{28, 11}
7878 f.pending[101] = []pgid{3}
7979 p := (*page)(unsafe.Pointer(&buf[0]))
8080 f.write(p)
8181
8282 // Read the page back out.
83 f2 := &freelist{pending: make(map[txnid][]pgid)}
83 f2 := &freelist{pending: make(map[txid][]pgid)}
8484 f2.read(p)
8585
8686 // Ensure that the freelist is correct.
1111 )
1212
1313 // Ensure that multiple threads can use the DB without race detector errors.
14 func TestParallelTransactions(t *testing.T) {
14 func TestParallelTxs(t *testing.T) {
1515 var mutex sync.RWMutex
1616
1717 err := quick.Check(func(numReaders, batchSize uint, items testdata) bool {
4444 go func() {
4545 mutex.RLock()
4646 local := current
47 txn, err := db.Transaction()
47 txn, err := db.Tx()
4848 mutex.RUnlock()
4949 if err == ErrDatabaseNotOpen {
5050 wg.Done()
8282 pending = pending[currentBatchSize:]
8383
8484 // Start write transaction.
85 txn, err := db.RWTransaction()
85 txn, err := db.RWTx()
8686 if !assert.NoError(t, err) {
8787 t.FailNow()
8888 }
99 buckets pgid
1010 freelist pgid
1111 pgid pgid
12 txnid txnid
12 txid txid
1313 }
1414
1515 // validate checks the marker bytes and version of the meta page to ensure it matches this binary.
3030 dest.buckets = m.buckets
3131 dest.freelist = m.freelist
3232 dest.pgid = m.pgid
33 dest.txnid = m.txnid
33 dest.txid = m.txid
3434 }
3535
3636 // write writes the meta onto a page.
3737 func (m *meta) write(p *page) {
3838 // Page id is either going to be 0 or 1 which we can determine by the Txn ID.
39 p.id = pgid(m.txnid % 2)
39 p.id = pgid(m.txid % 2)
4040 p.flags |= metaPageFlag
4141
4242 m.copy(p.meta())
77
88 // node represents an in-memory, deserialized page.
99 type node struct {
10 transaction *RWTransaction
11 isLeaf bool
12 unbalanced bool
13 key []byte
14 depth int
15 pgid pgid
16 parent *node
17 inodes inodes
10 tx *RWTx
11 isLeaf bool
12 unbalanced bool
13 key []byte
14 depth int
15 pgid pgid
16 parent *node
17 inodes inodes
1818 }
1919
2020 // minKeys returns the minimum number of inodes this node should have.
5555 // childAt returns the child node at a given index.
5656 func (n *node) childAt(index int) *node {
5757 _assert(!n.isLeaf, "invalid childAt(%d) on a leaf node", index)
58 return n.transaction.node(n.inodes[index].pgid, n)
58 return n.tx.node(n.inodes[index].pgid, n)
5959 }
6060
6161 // childIndex returns the index of a given child node.
213213 if len(current.inodes) >= minKeysPerPage && i < len(inodes)-minKeysPerPage && size+elemSize > threshold {
214214 size = pageHeaderSize
215215 nodes = append(nodes, current)
216 current = &node{transaction: n.transaction, isLeaf: n.isLeaf}
216 current = &node{tx: n.tx, isLeaf: n.isLeaf}
217217 }
218218
219219 size += elemSize
233233 n.unbalanced = false
234234
235235 // Ignore if node is above threshold (25%) and has enough keys.
236 var threshold = n.transaction.db.pageSize / 4
236 var threshold = n.tx.db.pageSize / 4
237237 if n.size() > threshold && len(n.inodes) > n.minKeys() {
238238 return
239239 }
243243 // If root node is a branch and only has one node then collapse it.
244244 if !n.isLeaf && len(n.inodes) == 1 {
245245 // Move child's children up.
246 child := n.transaction.nodes[n.inodes[0].pgid]
246 child := n.tx.nodes[n.inodes[0].pgid]
247247 n.isLeaf = child.isLeaf
248248 n.inodes = child.inodes[:]
249249
250250 // Reparent all child nodes being moved.
251251 for _, inode := range n.inodes {
252 if child, ok := n.transaction.nodes[inode.pgid]; ok {
252 if child, ok := n.tx.nodes[inode.pgid]; ok {
253253 child.parent = n
254254 }
255255 }
256256
257257 // Remove old child.
258258 child.parent = nil
259 delete(n.transaction.nodes, child.pgid)
259 delete(n.tx.nodes, child.pgid)
260260 }
261261
262262 return
277277 if target.numChildren() > target.minKeys() {
278278 if useNextSibling {
279279 // Reparent and move node.
280 if child, ok := n.transaction.nodes[target.inodes[0].pgid]; ok {
280 if child, ok := n.tx.nodes[target.inodes[0].pgid]; ok {
281281 child.parent = n
282282 }
283283 n.inodes = append(n.inodes, target.inodes[0])
288288 target.key = target.inodes[0].key
289289 } else {
290290 // Reparent and move node.
291 if child, ok := n.transaction.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
291 if child, ok := n.tx.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
292292 child.parent = n
293293 }
294294 n.inodes = append(n.inodes, inode{})
308308 if useNextSibling {
309309 // Reparent all child nodes being moved.
310310 for _, inode := range target.inodes {
311 if child, ok := n.transaction.nodes[inode.pgid]; ok {
311 if child, ok := n.tx.nodes[inode.pgid]; ok {
312312 child.parent = n
313313 }
314314 }
316316 // Copy over inodes from target and remove target.
317317 n.inodes = append(n.inodes, target.inodes...)
318318 n.parent.del(target.key)
319 delete(n.transaction.nodes, target.pgid)
319 delete(n.tx.nodes, target.pgid)
320320 } else {
321321 // Reparent all child nodes being moved.
322322 for _, inode := range n.inodes {
323 if child, ok := n.transaction.nodes[inode.pgid]; ok {
323 if child, ok := n.tx.nodes[inode.pgid]; ok {
324324 child.parent = target
325325 }
326326 }
329329 target.inodes = append(target.inodes, n.inodes...)
330330 n.parent.del(n.key)
331331 n.parent.put(target.key, target.inodes[0].key, nil, target.pgid)
332 delete(n.transaction.nodes, n.pgid)
332 delete(n.tx.nodes, n.pgid)
333333 }
334334
335335 // Either this node or the target node was deleted from the parent so rebalance it.
44 "unsafe"
55 )
66
7 // RWTransaction represents a transaction that can read and write data.
7 // RWTx represents a transaction that can read and write data.
88 // Only one read/write transaction can be active for a database at a time.
9 // RWTransaction is composed of a read-only Transaction so it can also use
10 // functions provided by Transaction.
11 type RWTransaction struct {
12 Transaction
9 // RWTx is composed of a read-only transaction so it can also use
10 // functions provided by Tx.
11 type RWTx struct {
12 Tx
1313 pending []*node
1414 }
1515
1616 // init initializes the transaction.
17 func (t *RWTransaction) init(db *DB) {
18 t.Transaction.init(db)
19 t.Transaction.rwtransaction = t
17 func (t *RWTx) init(db *DB) {
18 t.Tx.init(db)
19 t.Tx.rwtx = t
2020 t.pages = make(map[pgid]*page)
2121 t.nodes = make(map[pgid]*node)
2222
2323 // Increment the transaction id.
24 t.meta.txnid += txnid(1)
24 t.meta.txid += txid(1)
2525 }
2626
2727 // CreateBucket creates a new bucket.
2828 // Returns an error if the bucket already exists, if the bucket name is blank, or if the bucket name is too long.
29 func (t *RWTransaction) CreateBucket(name string) error {
29 func (t *RWTx) CreateBucket(name string) error {
3030 // Check if bucket already exists.
3131 if b := t.Bucket(name); b != nil {
3232 return ErrBucketExists
5151
5252 // CreateBucketIfNotExists creates a new bucket if it doesn't already exist.
5353 // Returns an error if the bucket name is blank, or if the bucket name is too long.
54 func (t *RWTransaction) CreateBucketIfNotExists(name string) error {
54 func (t *RWTx) CreateBucketIfNotExists(name string) error {
5555 err := t.CreateBucket(name)
5656 if err != nil && err != ErrBucketExists {
5757 return err
6161
6262 // DeleteBucket deletes a bucket.
6363 // Returns an error if the bucket cannot be found.
64 func (t *RWTransaction) DeleteBucket(name string) error {
64 func (t *RWTx) DeleteBucket(name string) error {
6565 b := t.Bucket(name)
6666 if b == nil {
6767 return ErrBucketNotFound
8080
8181 // Commit writes all changes to disk and updates the meta page.
8282 // Returns an error if a disk write error occurs.
83 func (t *RWTransaction) Commit() error {
83 func (t *RWTx) Commit() error {
8484 if t.db == nil {
8585 return nil
8686 }
117117 }
118118
119119 // Rollback closes the transaction and ignores all previous updates.
120 func (t *RWTransaction) Rollback() {
120 func (t *RWTx) Rollback() {
121121 t.close()
122122 }
123123
124 func (t *RWTransaction) close() {
124 func (t *RWTx) close() {
125125 if t.db != nil {
126126 t.db.rwlock.Unlock()
127127 t.db = nil
129129 }
130130
131131 // allocate returns a contiguous block of memory starting at a given page.
132 func (t *RWTransaction) allocate(count int) (*page, error) {
132 func (t *RWTx) allocate(count int) (*page, error) {
133133 p, err := t.db.allocate(count)
134134 if err != nil {
135135 return nil, err
142142 }
143143
144144 // rebalance attempts to balance all nodes.
145 func (t *RWTransaction) rebalance() {
145 func (t *RWTx) rebalance() {
146146 for _, n := range t.nodes {
147147 n.rebalance()
148148 }
149149 }
150150
151151 // spill writes all the nodes to dirty pages.
152 func (t *RWTransaction) spill() error {
152 func (t *RWTx) spill() error {
153153 // Keep track of the current root nodes.
154154 // We will update this at the end once all nodes are created.
155155 type root struct {
181181
182182 // If this is a root node that split then create a parent node.
183183 if n.parent == nil && len(newNodes) > 1 {
184 n.parent = &node{transaction: t, isLeaf: false}
184 n.parent = &node{tx: t, isLeaf: false}
185185 nodes = append(nodes, n.parent)
186186 }
187187
232232 }
233233
234234 // write writes any dirty pages to disk.
235 func (t *RWTransaction) write() error {
235 func (t *RWTx) write() error {
236236 // Sort pages by id.
237237 pages := make(pages, 0, len(t.pages))
238238 for _, p := range t.pages {
257257 }
258258
259259 // writeMeta writes the meta to the disk.
260 func (t *RWTransaction) writeMeta() error {
260 func (t *RWTx) writeMeta() error {
261261 // Create a temporary buffer for the meta page.
262262 buf := make([]byte, t.db.pageSize)
263263 p := t.db.pageInBuffer(buf, 0)
270270 }
271271
272272 // node creates a node from a page and associates it with a given parent.
273 func (t *RWTransaction) node(pgid pgid, parent *node) *node {
273 func (t *RWTx) node(pgid pgid, parent *node) *node {
274274 // Retrieve node if it has already been fetched.
275 if n := t.Transaction.node(pgid); n != nil {
275 if n := t.Tx.node(pgid); n != nil {
276276 return n
277277 }
278278
279279 // Otherwise create a branch and cache it.
280 n := &node{transaction: t, parent: parent}
280 n := &node{tx: t, parent: parent}
281281 if n.parent != nil {
282282 n.depth = n.parent.depth + 1
283283 }
288288 }
289289
290290 // dereference removes all references to the old mmap.
291 func (t *RWTransaction) dereference() {
291 func (t *RWTx) dereference() {
292292 for _, n := range t.nodes {
293293 n.dereference()
294294 }
88 "github.com/stretchr/testify/assert"
99 )
1010
11 // Ensure that a RWTransaction can be retrieved.
12 func TestRWTransaction(t *testing.T) {
11 // Ensure that a RWTx can be retrieved.
12 func TestRWTx(t *testing.T) {
1313 withOpenDB(func(db *DB, path string) {
14 txn, err := db.RWTransaction()
14 txn, err := db.RWTx()
1515 assert.NotNil(t, txn)
1616 assert.NoError(t, err)
1717 assert.Equal(t, txn.DB(), db)
1818 })
1919 }
2020
21 // Ensure that opening a RWTransaction while the DB is closed returns an error.
22 func TestRWTransactionOpenWithClosedDB(t *testing.T) {
21 // Ensure that opening a RWTx while the DB is closed returns an error.
22 func TestRWTxOpenWithClosedDB(t *testing.T) {
2323 withDB(func(db *DB, path string) {
24 txn, err := db.RWTransaction()
24 txn, err := db.RWTx()
2525 assert.Equal(t, err, ErrDatabaseNotOpen)
2626 assert.Nil(t, txn)
2727 })
2828 }
2929
3030 // Ensure that retrieving all buckets returns writable buckets.
31 func TestRWTransactionBuckets(t *testing.T) {
31 func TestRWTxBuckets(t *testing.T) {
3232 withOpenDB(func(db *DB, path string) {
3333 db.CreateBucket("widgets")
3434 db.CreateBucket("woojits")
35 db.Do(func(txn *RWTransaction) error {
35 db.Do(func(txn *RWTx) error {
3636 buckets := txn.Buckets()
3737 assert.Equal(t, len(buckets), 2)
3838 assert.Equal(t, buckets[0].Name(), "widgets")
4949 }
5050
5151 // Ensure that a bucket can be created and retrieved.
52 func TestRWTransactionCreateBucket(t *testing.T) {
52 func TestRWTxCreateBucket(t *testing.T) {
5353 withOpenDB(func(db *DB, path string) {
5454 // Create a bucket.
5555 err := db.CreateBucket("widgets")
6363 }
6464
6565 // Ensure that a bucket can be created if it doesn't already exist.
66 func TestRWTransactionCreateBucketIfNotExists(t *testing.T) {
66 func TestRWTxCreateBucketIfNotExists(t *testing.T) {
6767 withOpenDB(func(db *DB, path string) {
6868 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
6969 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
7777 }
7878
7979 // Ensure that a bucket cannot be created twice.
80 func TestRWTransactionRecreateBucket(t *testing.T) {
80 func TestRWTxRecreateBucket(t *testing.T) {
8181 withOpenDB(func(db *DB, path string) {
8282 // Create a bucket.
8383 err := db.CreateBucket("widgets")
9090 }
9191
9292 // Ensure that a bucket is created with a non-blank name.
93 func TestRWTransactionCreateBucketWithoutName(t *testing.T) {
93 func TestRWTxCreateBucketWithoutName(t *testing.T) {
9494 withOpenDB(func(db *DB, path string) {
9595 err := db.CreateBucket("")
9696 assert.Equal(t, err, ErrBucketNameRequired)
9898 }
9999
100100 // Ensure that a bucket name is not too long.
101 func TestRWTransactionCreateBucketWithLongName(t *testing.T) {
101 func TestRWTxCreateBucketWithLongName(t *testing.T) {
102102 withOpenDB(func(db *DB, path string) {
103103 err := db.CreateBucket(strings.Repeat("X", 255))
104104 assert.NoError(t, err)
109109 }
110110
111111 // Ensure that a bucket can be deleted.
112 func TestRWTransactionDeleteBucket(t *testing.T) {
112 func TestRWTxDeleteBucket(t *testing.T) {
113113 withOpenDB(func(db *DB, path string) {
114114 // Create a bucket and add a value.
115115 db.CreateBucket("widgets")
135135 }
136136
137137 // Ensure that an error is returned when deleting from a bucket that doesn't exist.
138 func TestRWTransactionDeleteBucketNotFound(t *testing.T) {
138 func TestRWTxDeleteBucketNotFound(t *testing.T) {
139139 withOpenDB(func(db *DB, path string) {
140140 err := db.DeleteBucket("widgets")
141141 assert.Equal(t, err, ErrBucketNotFound)
143143 }
144144
145145 // Benchmark the performance of bulk put transactions in random order.
146 func BenchmarkRWTransactionPutRandom(b *testing.B) {
146 func BenchmarkRWTxPutRandom(b *testing.B) {
147147 indexes := rand.Perm(b.N)
148148 value := []byte(strings.Repeat("0", 64))
149149 withOpenDB(func(db *DB, path string) {
150150 db.CreateBucket("widgets")
151 var txn *RWTransaction
151 var txn *RWTx
152152 var bucket *Bucket
153153 for i := 0; i < b.N; i++ {
154154 if i%1000 == 0 {
155155 if txn != nil {
156156 txn.Commit()
157157 }
158 txn, _ = db.RWTransaction()
158 txn, _ = db.RWTx()
159159 bucket = txn.Bucket("widgets")
160160 }
161161 bucket.Put([]byte(strconv.Itoa(indexes[i])), value)
165165 }
166166
167167 // Benchmark the performance of bulk put transactions in sequential order.
168 func BenchmarkRWTransactionPutSequential(b *testing.B) {
168 func BenchmarkRWTxPutSequential(b *testing.B) {
169169 value := []byte(strings.Repeat("0", 64))
170170 withOpenDB(func(db *DB, path string) {
171171 db.CreateBucket("widgets")
172 db.Do(func(txn *RWTransaction) error {
172 db.Do(func(txn *RWTx) error {
173173 bucket := txn.Bucket("widgets")
174174 for i := 0; i < b.N; i++ {
175175 bucket.Put([]byte(strconv.Itoa(i)), value)
00 package bolt
11
2 // Transaction represents a read-only transaction on the database.
2 // Tx represents a read-only transaction on the database.
33 // It can be used for retrieving values for keys as well as creating cursors for
44 // iterating over the data.
55 //
66 // IMPORTANT: You must close transactions when you are done with them. Pages
77 // can not be reclaimed by the writer until no more transactions are using them.
88 // A long running read transaction can cause the database to quickly grow.
9 type Transaction struct {
10 db *DB
11 rwtransaction *RWTransaction
12 meta *meta
13 buckets *buckets
14 nodes map[pgid]*node
15 pages map[pgid]*page
9 type Tx struct {
10 db *DB
11 rwtx *RWTx
12 meta *meta
13 buckets *buckets
14 nodes map[pgid]*node
15 pages map[pgid]*page
1616 }
1717
18 // txnid represents the internal transaction identifier.
19 type txnid uint64
18 // txid represents the internal transaction identifier.
19 type txid uint64
2020
2121 // init initializes the transaction and associates it with a database.
22 func (t *Transaction) init(db *DB) {
22 func (t *Tx) init(db *DB) {
2323 t.db = db
2424 t.pages = nil
2525
3333 }
3434
3535 // id returns the transaction id.
36 func (t *Transaction) id() txnid {
37 return t.meta.txnid
36 func (t *Tx) id() txid {
37 return t.meta.txid
3838 }
3939
4040 // Close closes the transaction and releases any pages it is using.
41 func (t *Transaction) Close() {
41 func (t *Tx) Close() {
4242 if t.db != nil {
43 if t.rwtransaction != nil {
44 t.rwtransaction.Rollback()
43 if t.rwtx != nil {
44 t.rwtx.Rollback()
4545 } else {
46 t.db.removeTransaction(t)
46 t.db.removeTx(t)
4747 t.db = nil
4848 }
4949 }
5050 }
5151
5252 // DB returns a reference to the database that created the transaction.
53 func (t *Transaction) DB() *DB {
53 func (t *Tx) DB() *DB {
5454 return t.db
5555 }
5656
5757 // Bucket retrieves a bucket by name.
5858 // Returns nil if the bucket does not exist.
59 func (t *Transaction) Bucket(name string) *Bucket {
59 func (t *Tx) Bucket(name string) *Bucket {
6060 b := t.buckets.get(name)
6161 if b == nil {
6262 return nil
6363 }
6464
6565 return &Bucket{
66 bucket: b,
67 name: name,
68 transaction: t,
69 rwtransaction: t.rwtransaction,
66 bucket: b,
67 name: name,
68 tx: t,
69 rwtx: t.rwtx,
7070 }
7171 }
7272
7373 // Buckets retrieves a list of all buckets.
74 func (t *Transaction) Buckets() []*Bucket {
74 func (t *Tx) Buckets() []*Bucket {
7575 buckets := make([]*Bucket, 0, len(t.buckets.items))
7676 for name, b := range t.buckets.items {
7777 bucket := &Bucket{
78 bucket: b,
79 name: name,
80 transaction: t,
81 rwtransaction: t.rwtransaction,
78 bucket: b,
79 name: name,
80 tx: t,
81 rwtx: t.rwtx,
8282 }
8383 buckets = append(buckets, bucket)
8484 }
8787
8888 // page returns a reference to the page with a given id.
8989 // If page has been written to then a temporary bufferred page is returned.
90 func (t *Transaction) page(id pgid) *page {
90 func (t *Tx) page(id pgid) *page {
9191 // Check the dirty pages first.
9292 if t.pages != nil {
9393 if p, ok := t.pages[id]; ok {
100100 }
101101
102102 // node returns a reference to the in-memory node for a given page, if it exists.
103 func (t *Transaction) node(id pgid) *node {
103 func (t *Tx) node(id pgid) *node {
104104 if t.nodes == nil {
105105 return nil
106106 }
109109
110110 // pageNode returns the in-memory node, if it exists.
111111 // Otherwise returns the underlying page.
112 func (t *Transaction) pageNode(id pgid) (*page, *node) {
112 func (t *Tx) pageNode(id pgid) (*page, *node) {
113113 if n := t.node(id); n != nil {
114114 return nil, n
115115 }
117117 }
118118
119119 // forEachPage iterates over every page within a given page and executes a function.
120 func (t *Transaction) forEachPage(pgid pgid, depth int, fn func(*page, int)) {
120 func (t *Tx) forEachPage(pgid pgid, depth int, fn func(*page, int)) {
121121 p := t.page(pgid)
122122
123123 // Execute function.
1313 )
1414
1515 // Ensure that the database can retrieve a list of buckets.
16 func TestTransactionBuckets(t *testing.T) {
16 func TestTxBuckets(t *testing.T) {
1717 withOpenDB(func(db *DB, path string) {
1818 db.CreateBucket("foo")
1919 db.CreateBucket("bar")
2727 })
2828 }
2929
30 // Ensure that a Transaction can retrieve a bucket.
31 func TestTransactionBucketMissing(t *testing.T) {
30 // Ensure that a Tx can retrieve a bucket.
31 func TestTxBucketMissing(t *testing.T) {
3232 withOpenDB(func(db *DB, path string) {
3333 db.CreateBucket("widgets")
3434 b, err := db.Bucket("widgets")
3939 })
4040 }
4141
42 // Ensure that a Transaction retrieving a non-existent key returns nil.
43 func TestTransactionGetMissing(t *testing.T) {
42 // Ensure that a Tx retrieving a non-existent key returns nil.
43 func TestTxGetMissing(t *testing.T) {
4444 withOpenDB(func(db *DB, path string) {
4545 db.CreateBucket("widgets")
4646 db.Put("widgets", []byte("foo"), []byte("bar"))
5050 })
5151 }
5252
53 // Ensure that a Transaction cursor can iterate over an empty bucket without error.
54 func TestTransactionCursorEmptyBucket(t *testing.T) {
55 withOpenDB(func(db *DB, path string) {
56 db.CreateBucket("widgets")
57 txn, _ := db.Transaction()
53 // Ensure that a Tx cursor can iterate over an empty bucket without error.
54 func TestTxCursorEmptyBucket(t *testing.T) {
55 withOpenDB(func(db *DB, path string) {
56 db.CreateBucket("widgets")
57 txn, _ := db.Tx()
5858 c := txn.Bucket("widgets").Cursor()
5959 k, v := c.First()
6060 assert.Nil(t, k)
6363 })
6464 }
6565
66 // Ensure that a Transaction cursor can iterate over a single root with a couple elements.
67 func TestTransactionCursorLeafRoot(t *testing.T) {
66 // Ensure that a Tx cursor can iterate over a single root with a couple elements.
67 func TestTxCursorLeafRoot(t *testing.T) {
6868 withOpenDB(func(db *DB, path string) {
6969 db.CreateBucket("widgets")
7070 db.Put("widgets", []byte("baz"), []byte{})
7171 db.Put("widgets", []byte("foo"), []byte{0})
7272 db.Put("widgets", []byte("bar"), []byte{1})
73 txn, _ := db.Transaction()
73 txn, _ := db.Tx()
7474 c := txn.Bucket("widgets").Cursor()
7575
7676 k, v := c.First()
9797 })
9898 }
9999
100 // Ensure that a Transaction cursor can iterate in reverse over a single root with a couple elements.
101 func TestTransactionCursorLeafRootReverse(t *testing.T) {
100 // Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
101 func TestTxCursorLeafRootReverse(t *testing.T) {
102102 withOpenDB(func(db *DB, path string) {
103103 db.CreateBucket("widgets")
104104 db.Put("widgets", []byte("baz"), []byte{})
105105 db.Put("widgets", []byte("foo"), []byte{0})
106106 db.Put("widgets", []byte("bar"), []byte{1})
107 txn, _ := db.Transaction()
107 txn, _ := db.Tx()
108108 c := txn.Bucket("widgets").Cursor()
109109
110110 k, v := c.Last()
131131 })
132132 }
133133
134 // Ensure that a Transaction cursor can restart from the beginning.
135 func TestTransactionCursorRestart(t *testing.T) {
134 // Ensure that a Tx cursor can restart from the beginning.
135 func TestTxCursorRestart(t *testing.T) {
136136 withOpenDB(func(db *DB, path string) {
137137 db.CreateBucket("widgets")
138138 db.Put("widgets", []byte("bar"), []byte{})
139139 db.Put("widgets", []byte("foo"), []byte{})
140140
141 txn, _ := db.Transaction()
141 txn, _ := db.Tx()
142142 c := txn.Bucket("widgets").Cursor()
143143
144144 k, _ := c.First()
157157 })
158158 }
159159
160 // Ensure that a transaction can iterate over all elements in a bucket.
161 func TestTransactionCursorIterate(t *testing.T) {
160 // Ensure that a Tx can iterate over all elements in a bucket.
161 func TestTxCursorIterate(t *testing.T) {
162162 f := func(items testdata) bool {
163163 withOpenDB(func(db *DB, path string) {
164164 // Bulk insert all values.
165165 db.CreateBucket("widgets")
166 rwtxn, _ := db.RWTransaction()
166 rwtxn, _ := db.RWTx()
167167 b := rwtxn.Bucket("widgets")
168168 for _, item := range items {
169169 assert.NoError(t, b.Put(item.Key, item.Value))
175175
176176 // Iterate over all items and check consistency.
177177 var index = 0
178 txn, _ := db.Transaction()
178 txn, _ := db.Tx()
179179 c := txn.Bucket("widgets").Cursor()
180180 for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
181181 assert.Equal(t, k, items[index].Key)
195195 }
196196
197197 // Ensure that a transaction can iterate over all elements in a bucket in reverse.
198 func TestTransactionCursorIterateReverse(t *testing.T) {
198 func TestTxCursorIterateReverse(t *testing.T) {
199199 f := func(items testdata) bool {
200200 withOpenDB(func(db *DB, path string) {
201201 // Bulk insert all values.
202202 db.CreateBucket("widgets")
203 rwtxn, _ := db.RWTransaction()
203 rwtxn, _ := db.RWTx()
204204 b := rwtxn.Bucket("widgets")
205205 for _, item := range items {
206206 assert.NoError(t, b.Put(item.Key, item.Value))
212212
213213 // Iterate over all items and check consistency.
214214 var index = 0
215 txn, _ := db.Transaction()
215 txn, _ := db.Tx()
216216 c := txn.Bucket("widgets").Cursor()
217217 for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() {
218218 assert.Equal(t, k, items[index].Key)
232232 }
233233
234234 // Benchmark the performance iterating over a cursor.
235 func BenchmarkTransactionCursor(b *testing.B) {
235 func BenchmarkTxCursor(b *testing.B) {
236236 indexes := rand.Perm(b.N)
237237 value := []byte(strings.Repeat("0", 64))
238238
239239 withOpenDB(func(db *DB, path string) {
240240 // Write data to bucket.
241241 db.CreateBucket("widgets")
242 db.Do(func(txn *RWTransaction) error {
242 db.Do(func(txn *RWTx) error {
243243 bucket := txn.Bucket("widgets")
244244 for i := 0; i < b.N; i++ {
245245 bucket.Put([]byte(strconv.Itoa(indexes[i])), value)
249249 b.ResetTimer()
250250
251251 // Iterate over bucket using cursor.
252 db.With(func(txn *Transaction) error {
252 db.With(func(txn *Tx) error {
253253 count := 0
254254 c := txn.Bucket("widgets").Cursor()
255255 for k, _ := c.First(); k != nil; k, _ = c.Next() {