Codebase list golang-github-boltdb-bolt / 3b2fd8f
Revert "Refactor Transaction/Bucket API." This reverts commit 1ad2b99f281d587b767b36f886401e81d17915a9. Ben Johnson 10 years ago
15 changed file(s) with 867 addition(s) and 864 deletion(s). Raw diff Collapse all Expand all
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
00 package bolt
11
2 import (
3 "bytes"
4 )
5
62 // Bucket represents a collection of key/value pairs inside the database.
7 // All keys inside the bucket are unique.
8 //
9 // Accessing or changing data from a Bucket whose Transaction has closed will cause a panic.
3 // All keys inside the bucket are unique. The Bucket type is not typically used
4 // directly. Instead the bucket name is typically passed into the Get(), Put(),
5 // or Delete() functions.
106 type Bucket struct {
117 *bucket
128 name string
2420 return b.name
2521 }
2622
27 // Cursor creates a new cursor for this bucket.
28 func (b *Bucket) Cursor() *Cursor {
29 _assert(b.transaction.isOpen(), "transaction not open")
23 // cursor creates a new cursor for this bucket.
24 func (b *Bucket) cursor() *Cursor {
3025 return &Cursor{
3126 transaction: b.transaction,
3227 root: b.root,
3429 }
3530 }
3631
37 // Get retrieves the value for a key in a named bucket.
38 // Returns a nil value if the key does not exist.
39 func (b *Bucket) Get(key []byte) []byte {
40 _assert(b.transaction.isOpen(), "transaction not open")
41 c := b.Cursor()
42 k, v := c.Seek(key)
43
44 // If our target node isn't the same key as what's passed in then return nil.
45 if !bytes.Equal(key, k) {
46 return nil
47 }
48
49 return v
50 }
51
52 // Put sets the value for a key inside of the bucket.
53 // If the key exist then its previous value will be overwritten.
54 // Returns an error if bucket was created from a read-only transaction, if the
55 // key is blank, if the key is too large, or if the value is too large.
56 func (b *Bucket) Put(key []byte, value []byte) error {
57 _assert(b.transaction.isOpen(), "transaction not open")
58 if !b.transaction.writable {
59 return ErrTransactionNotWritable
60 } else if len(key) == 0 {
61 return ErrKeyRequired
62 } else if len(key) > MaxKeySize {
63 return ErrKeyTooLarge
64 } else if len(value) > MaxValueSize {
65 return ErrValueTooLarge
66 }
67
68 // Move cursor to correct position.
69 c := b.Cursor()
70 c.Seek(key)
71
72 // Insert the key/value.
73 c.node(b.transaction).put(key, key, value, 0)
74
75 return nil
76 }
77
78 // Delete removes a key from the bucket.
79 // If the key does not exist then nothing is done and a nil error is returned.
80 // Returns an error if the bucket was created from a read-only transaction.
81 func (b *Bucket) Delete(key []byte) error {
82 _assert(b.transaction.isOpen(), "transaction not open")
83 if !b.transaction.writable {
84 return ErrTransactionNotWritable
85 }
86
87 // Move cursor to correct position.
88 c := b.Cursor()
89 c.Seek(key)
90
91 // Delete the node if we have a matching key.
92 c.node(c.transaction).del(key)
93
94 return nil
95 }
96
97 // NextSequence returns an autoincrementing integer for the bucket.
98 // Returns an error if the bucket was created from a read-only transaction or
99 // if the next sequence will overflow the int type.
100 func (b *Bucket) NextSequence() (int, error) {
101 _assert(b.transaction.isOpen(), "transaction not open")
102 if !b.transaction.writable {
103 return 0, ErrTransactionNotWritable
104 } else if b.bucket.sequence == uint64(maxInt) {
105 return 0, ErrSequenceOverflow
106 }
107
108 // Increment and return the sequence.
109 b.bucket.sequence++
110
111 return int(b.bucket.sequence), nil
112 }
113
114 // ForEach executes a function for each key/value pair in a bucket.
115 func (b *Bucket) ForEach(fn func(k, v []byte) error) error {
116 _assert(b.transaction.isOpen(), "transaction not open")
117 c := b.Cursor()
118 for k, v := c.First(); k != nil; k, v = c.Next() {
119 if err := fn(k, v); err != nil {
120 return err
121 }
122 }
123 return nil
124 }
125
12632 // Stat returns stats on a bucket.
12733 func (b *Bucket) Stat() *BucketStat {
128 _assert(b.transaction.isOpen(), "transaction not open")
12934 s := &BucketStat{}
13035 b.transaction.forEachPage(b.root, 0, func(p *page, depth int) {
13136 if (p.flags & leafPageFlag) != 0 {
1010 // Ensure a bucket can calculate stats.
1111 func TestBucketStat(t *testing.T) {
1212 withOpenDB(func(db *DB, path string) {
13 db.Do(func(txn *Transaction) error {
13 db.Do(func(txn *RWTransaction) error {
1414 // Add bucket with lots of keys.
1515 txn.CreateBucket("widgets")
16 b := txn.Bucket("widgets")
1716 for i := 0; i < 100000; i++ {
18 b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
17 txn.Put("widgets", []byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
1918 }
2019
2120 // Add bucket with fewer keys but one big value.
2221 txn.CreateBucket("woojits")
23 b = txn.Bucket("woojits")
2422 for i := 0; i < 500; i++ {
25 b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
23 txn.Put("woojits", []byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
2624 }
27 b.Put([]byte("really-big-value"), []byte(strings.Repeat("*", 10000)))
25 txn.Put("woojits", []byte("really-big-value"), []byte(strings.Repeat("*", 10000)))
2826
2927 // Add a bucket that fits on a single root leaf.
3028 txn.CreateBucket("whozawhats")
31 b = txn.Bucket("whozawhats")
32 b.Put([]byte("foo"), []byte("bar"))
29 txn.Put("whozawhats", []byte("foo"), []byte("bar"))
3330
3431 return nil
3532 })
200200 }
201201
202202 // node returns the node that the cursor is currently positioned on.
203 func (c *Cursor) node(t *Transaction) *node {
203 func (c *Cursor) node(t *RWTransaction) *node {
204204 _assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack")
205205
206206 // Start from root and traverse down the hierarchy.
+40
-74
db.go less more
1515 const maxMmapStep = 1 << 30 // 1GB
1616
1717 // DB represents a collection of buckets persisted to a file on disk.
18 // All data access is performed through transactions which can be obtained from
19 // the DB. There are a number of functions duplicated from the Transction type
20 // which provide ease-of-use, single transaction access to the data.
21 //
22 // All the functions on DB will return a ErrDatabaseNotOpen if accessed before
23 // Open() is called or after Close is called.
18 // All data access is performed through transactions which can be obtained through the DB.
19 // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called.
2420 type DB struct {
2521 os _os
2622 syscall _syscall
3228 meta1 *meta
3329 pageSize int
3430 opened bool
35 rwtransaction *Transaction
31 rwtransaction *RWTransaction
3632 transactions []*Transaction
3733 freelist *freelist
3834
4440 // Path returns the path to currently open database file.
4541 func (db *DB) Path() string {
4642 return db.path
43 }
44
45 // GoString returns the Go string representation of the database.
46 func (db *DB) GoString() string {
47 return fmt.Sprintf("bolt.DB{path:%q}", db.path)
48 }
49
50 // String returns the string representation of the database.
51 func (db *DB) String() string {
52 return fmt.Sprintf("DB<%q>", db.path)
4753 }
4854
4955 // Open opens a data file at the given path and initializes the database.
255261 // Transaction creates a read-only transaction.
256262 // Multiple read-only transactions can be used concurrently.
257263 //
258 // IMPORTANT: You must close the transaction after you are finished or else the
259 // database will not reclaim old pages.
264 // IMPORTANT: You must close the transaction after you are finished or else the database will not reclaim old pages.
260265 func (db *DB) Transaction() (*Transaction, error) {
261266 db.metalock.Lock()
262267 defer db.metalock.Unlock()
283288
284289 // RWTransaction creates a read/write transaction.
285290 // Only one read/write transaction is allowed at a time.
286 // You must call Commit() or Close() on the transaction to close it.
287 func (db *DB) RWTransaction() (*Transaction, error) {
291 // You must call Commit() or Rollback() on the transaction to close it.
292 func (db *DB) RWTransaction() (*RWTransaction, error) {
288293 db.metalock.Lock()
289294 defer db.metalock.Unlock()
290295
291 // Obtain writer lock. This is released by the writer transaction when it closes.
296 // Obtain writer lock. This is released by the RWTransaction when it closes.
292297 db.rwlock.Lock()
293298
294299 // Exit if the database is not open yet.
297302 return nil, ErrDatabaseNotOpen
298303 }
299304
300 // Create a writable transaction associated with the database.
301 t := &Transaction{writable: true, nodes: make(map[pgid]*node)}
305 // Create a transaction associated with the database.
306 t := &RWTransaction{nodes: make(map[pgid]*node)}
302307 t.init(db)
303308 db.rwtransaction = t
304309
333338 }
334339 }
335340
336 // Do executes a function within the context of a writable Transaction.
341 // Do executes a function within the context of a RWTransaction.
337342 // If no error is returned from the function then the transaction is committed.
338343 // If an error is returned then the entire transaction is rolled back.
339344 // Any error that is returned from the function or returned from the commit is
340345 // returned from the Do() method.
341 func (db *DB) Do(fn func(*Transaction) error) error {
346 func (db *DB) Do(fn func(*RWTransaction) error) error {
342347 t, err := db.RWTransaction()
343348 if err != nil {
344349 return err
360365 if err != nil {
361366 return err
362367 }
363 defer t.Rollback()
368 defer t.Close()
364369
365370 // If an error is returned from the function then pass it through.
366371 return fn(t)
370375 // An error is returned if the bucket cannot be found.
371376 func (db *DB) ForEach(name string, fn func(k, v []byte) error) error {
372377 return db.With(func(t *Transaction) error {
373 b := t.Bucket(name)
374 if b == nil {
375 return ErrBucketNotFound
376 }
377 return b.ForEach(fn)
378 return t.ForEach(name, fn)
378379 })
379380 }
380381
381382 // Bucket retrieves a reference to a bucket.
382383 // This is typically useful for checking the existence of a bucket.
383 //
384 // Do not use the returned bucket for accessing or changing data.
385384 func (db *DB) Bucket(name string) (*Bucket, error) {
386385 t, err := db.Transaction()
387386 if err != nil {
388387 return nil, err
389388 }
390 defer t.Rollback()
389 defer t.Close()
391390 return t.Bucket(name), nil
392391 }
393392
394393 // Buckets retrieves a list of all buckets in the database.
395 //
396 // Do not use any of the returned buckets for accessing or changing data.
397394 func (db *DB) Buckets() ([]*Bucket, error) {
398395 t, err := db.Transaction()
399396 if err != nil {
400397 return nil, err
401398 }
402 defer t.Rollback()
399 defer t.Close()
403400 return t.Buckets(), nil
404401 }
405402
407404 // This function can return an error if the bucket already exists, if the name
408405 // is blank, or the bucket name is too long.
409406 func (db *DB) CreateBucket(name string) error {
410 return db.Do(func(t *Transaction) error {
407 return db.Do(func(t *RWTransaction) error {
411408 return t.CreateBucket(name)
412409 })
413410 }
415412 // CreateBucketIfNotExists creates a new bucket with the given name if it doesn't already exist.
416413 // This function can return an error if the name is blank, or the bucket name is too long.
417414 func (db *DB) CreateBucketIfNotExists(name string) error {
418 return db.Do(func(t *Transaction) error {
415 return db.Do(func(t *RWTransaction) error {
419416 return t.CreateBucketIfNotExists(name)
420417 })
421418 }
423420 // DeleteBucket removes a bucket from the database.
424421 // Returns an error if the bucket does not exist.
425422 func (db *DB) DeleteBucket(name string) error {
426 return db.Do(func(t *Transaction) error {
423 return db.Do(func(t *RWTransaction) error {
427424 return t.DeleteBucket(name)
428425 })
429426 }
432429 // This function can return an error if the bucket does not exist.
433430 func (db *DB) NextSequence(name string) (int, error) {
434431 var seq int
435 err := db.Do(func(t *Transaction) error {
436 b := t.Bucket(name)
437 if b == nil {
438 return ErrBucketNotFound
439 }
440
432 err := db.Do(func(t *RWTransaction) error {
441433 var err error
442 if seq, err = b.NextSequence(); err != nil {
443 return err
444 }
445 return nil
434 seq, err = t.NextSequence(name)
435 return err
446436 })
447437 if err != nil {
448438 return 0, err
451441 }
452442
453443 // Get retrieves the value for a key in a bucket.
454 // Returns an error if the bucket does not exist.
444 // Returns an error if the key does not exist.
455445 func (db *DB) Get(name string, key []byte) ([]byte, error) {
456446 t, err := db.Transaction()
457447 if err != nil {
458448 return nil, err
459449 }
460 defer t.Rollback()
461
462 b := t.Bucket(name)
463 if b == nil {
464 return nil, ErrBucketNotFound
465 }
466
467 return b.Get(key), nil
450 defer t.Close()
451 return t.Get(name, key)
468452 }
469453
470454 // Put sets the value for a key in a bucket.
471455 // 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.
472456 func (db *DB) Put(name string, key []byte, value []byte) error {
473 return db.Do(func(t *Transaction) error {
474 b := t.Bucket(name)
475 if b == nil {
476 return ErrBucketNotFound
477 }
478 return b.Put(key, value)
457 return db.Do(func(t *RWTransaction) error {
458 return t.Put(name, key, value)
479459 })
480460 }
481461
482462 // Delete removes a key from a bucket.
483463 // Returns an error if the bucket cannot be found.
484464 func (db *DB) Delete(name string, key []byte) error {
485 return db.Do(func(t *Transaction) error {
486 b := t.Bucket(name)
487 if b == nil {
488 return ErrBucketNotFound
489 }
490 return b.Delete(key)
465 return db.Do(func(t *RWTransaction) error {
466 return t.Delete(name, key)
491467 })
492468 }
493469
500476 if err != nil {
501477 return err
502478 }
503 defer t.Commit()
479 defer t.Close()
504480
505481 // Open reader on the database.
506482 f, err := os.Open(db.path)
545521 db.mmaplock.RUnlock()
546522 db.metalock.Unlock()
547523
548 err := db.Do(func(t *Transaction) error {
524 err := db.Do(func(t *RWTransaction) error {
549525 s.PageCount = int(t.meta.pgid)
550526 s.FreePageCount = len(db.freelist.all())
551527 s.PageSize = db.pageSize
555531 return nil, err
556532 }
557533 return s, nil
558 }
559
560 // GoString returns the Go string representation of the database.
561 func (db *DB) GoString() string {
562 return fmt.Sprintf("bolt.DB{path:%q}", db.path)
563 }
564
565 // String returns the string representation of the database.
566 func (db *DB) String() string {
567 return fmt.Sprintf("DB<%q>", db.path)
568534 }
569535
570536 // page retrieves a page reference from the mmap based on the current page size.
187187 })
188188 }
189189
190 // Ensure that a Transaction can be retrieved.
191 func TestDBRWTransaction(t *testing.T) {
192 withOpenDB(func(db *DB, path string) {
193 txn, err := db.RWTransaction()
194 assert.NotNil(t, txn)
195 assert.NoError(t, err)
196 assert.Equal(t, txn.DB(), db)
197 })
198 }
199
200 // Ensure that opening a Transaction while the DB is closed returns an error.
201 func TestRWTransactionOpenWithClosedDB(t *testing.T) {
202 withDB(func(db *DB, path string) {
203 txn, err := db.RWTransaction()
204 assert.Equal(t, err, ErrDatabaseNotOpen)
205 assert.Nil(t, txn)
206 })
207 }
208
209190 // Ensure a database can provide a transactional block.
210191 func TestDBTransactionBlock(t *testing.T) {
211192 withOpenDB(func(db *DB, path string) {
212 err := db.Do(func(txn *Transaction) error {
193 err := db.Do(func(txn *RWTransaction) error {
213194 txn.CreateBucket("widgets")
214 b := txn.Bucket("widgets")
215 b.Put([]byte("foo"), []byte("bar"))
216 b.Put([]byte("baz"), []byte("bat"))
217 b.Delete([]byte("foo"))
195 txn.Put("widgets", []byte("foo"), []byte("bar"))
196 txn.Put("widgets", []byte("baz"), []byte("bat"))
197 txn.Delete("widgets", []byte("foo"))
218198 return nil
219199 })
220200 assert.NoError(t, err)
228208 // Ensure a closed database returns an error while running a transaction block
229209 func TestDBTransactionBlockWhileClosed(t *testing.T) {
230210 withDB(func(db *DB, path string) {
231 err := db.Do(func(txn *Transaction) error {
211 err := db.Do(func(txn *RWTransaction) error {
232212 txn.CreateBucket("widgets")
233213 return nil
234214 })
352332 // Ensure the database can return stats about itself.
353333 func TestDBStat(t *testing.T) {
354334 withOpenDB(func(db *DB, path string) {
355 db.Do(func(txn *Transaction) error {
335 db.Do(func(txn *RWTransaction) error {
356336 txn.CreateBucket("widgets")
357 b := txn.Bucket("widgets")
358337 for i := 0; i < 10000; i++ {
359 b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
338 txn.Put("widgets", []byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
360339 }
361340 return nil
362341 })
369348 t0, _ := db.Transaction()
370349 t1, _ := db.Transaction()
371350 t2, _ := db.Transaction()
372 t2.Rollback()
351 t2.Close()
373352
374353 // Obtain stats.
375354 stat, err := db.Stat()
381360 assert.Equal(t, stat.TransactionCount, 2)
382361
383362 // Close readers.
384 t0.Rollback()
385 t1.Rollback()
363 t0.Close()
364 t1.Close()
386365 })
387366 }
388367
99 system crash. Transactions which have not finished committing will simply be
1010 rolled back in the event of a crash.
1111
12 The design of Bolt is based on Howard Chu's LMDB project.
12 The design of Bolt is based on Howard Chu's LMDB database project.
1313
1414 Basics
1515
16 There are only a few types in Bolt: DB, Bucket, Transaction, and Cursor. The DB
17 is a collection of buckets and is represented by a single file on disk. A
18 bucket is a collection of unique keys that are associated with values.
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.
1919
20 Transactions provide a consistent view of the database. They can be used for
21 retrieving, setting, and deleting properties. They can also be used to iterate
22 over all the values in a bucket. Only one writer Transaction can be in use at
23 a time.
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.
2425
2526
2627 Caveats
2829 The database uses a read-only, memory-mapped data file to ensure that
2930 applications cannot corrupt the database, however, this means that keys and
3031 values returned from Bolt cannot be changed. Writing to a read-only byte slice
31 will cause Go to panic. If you need to alter data returned from a Transaction
32 you need to first copy it to a new byte slice.
32 will cause Go to panic. If you need to work with data returned from a Get() you
33 need to first copy it to a new byte slice.
3334
3435 Bolt currently works on Mac OS and Linux. Windows support is coming soon.
3536
1414 // ErrDatabaseOpen is returned when opening a database that is
1515 // already open.
1616 ErrDatabaseOpen = &Error{"database already open", nil}
17
18 // ErrTransactionNotWritable is returned changing data using a read-only
19 // transaction.
20 ErrTransactionNotWritable = &Error{"transaction not writable", nil}
2117
2218 // ErrBucketNotFound is returned when trying to access a bucket that has
2319 // not been created yet.
6666 defer db.Close()
6767
6868 // Execute several commands within a write transaction.
69 err := db.Do(func(t *Transaction) error {
69 err := db.Do(func(t *RWTransaction) error {
7070 if err := t.CreateBucket("widgets"); err != nil {
7171 return err
7272 }
73
74 b := t.Bucket("widgets")
75 if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
73 if err := t.Put("widgets", []byte("foo"), []byte("bar")); err != nil {
7674 return err
7775 }
7876 return nil
10199
102100 // Access data from within a read-only transactional block.
103101 db.With(func(t *Transaction) error {
104 v := t.Bucket("people").Get([]byte("john"))
102 v, _ := t.Get("people", []byte("john"))
105103 fmt.Printf("John's last name is %s.\n", string(v))
106104 return nil
107105 })
134132 // A liger is awesome.
135133 }
136134
137 func ExampleTransaction_Commit() {
138 // Open the database.
139 var db DB
140 db.Open("/tmp/bolt/db_rwtransaction.db", 0666)
135 func ExampleRWTransaction() {
136 // Open the database.
137 var db DB
138 db.Open("/tmp/bolt/rwtransaction.db", 0666)
141139 defer db.Close()
142140
143141 // Create a bucket.
144142 db.CreateBucket("widgets")
145143
146144 // Create several keys in a transaction.
147 txn, _ := db.RWTransaction()
148 b := txn.Bucket("widgets")
149 b.Put([]byte("john"), []byte("blue"))
150 b.Put([]byte("abby"), []byte("red"))
151 b.Put([]byte("zephyr"), []byte("purple"))
152 txn.Commit()
145 rwtxn, _ := db.RWTransaction()
146 rwtxn.Put("widgets", []byte("john"), []byte("blue"))
147 rwtxn.Put("widgets", []byte("abby"), []byte("red"))
148 rwtxn.Put("widgets", []byte("zephyr"), []byte("purple"))
149 rwtxn.Commit()
153150
154151 // Iterate over the values in sorted key order.
155 txn, _ = db.Transaction()
156 c := txn.Bucket("widgets").Cursor()
152 txn, _ := db.Transaction()
153 c, _ := txn.Cursor("widgets")
157154 for k, v := c.First(); k != nil; k, v = c.Next() {
158155 fmt.Printf("%s likes %s\n", string(k), string(v))
159156 }
160 txn.Rollback()
157 txn.Close()
161158
162159 // Output:
163160 // abby likes red
165162 // zephyr likes purple
166163 }
167164
168 func ExampleTransaction_Rollback() {
169 // Open the database.
170 var db DB
171 db.Open("/tmp/bolt/transaction_close.db", 0666)
165 func ExampleRWTransaction_rollback() {
166 // Open the database.
167 var db DB
168 db.Open("/tmp/bolt/rwtransaction_rollback.db", 0666)
172169 defer db.Close()
173170
174171 // Create a bucket.
178175 db.Put("widgets", []byte("foo"), []byte("bar"))
179176
180177 // Update the key but rollback the transaction so it never saves.
181 txn, _ := db.RWTransaction()
182 txn.Bucket("widgets").Put([]byte("foo"), []byte("baz"))
183 txn.Rollback()
178 rwtxn, _ := db.RWTransaction()
179 rwtxn.Put("widgets", []byte("foo"), []byte("baz"))
180 rwtxn.Rollback()
184181
185182 // Ensure that our original value is still set.
186183 value, _ := db.Get("widgets", []byte("foo"))
5555
5656 // Verify all data is in for local data list.
5757 for _, item := range local {
58 value := txn.Bucket("widgets").Get(item.Key)
58 value, err := txn.Get("widgets", item.Key)
5959 if !assert.NoError(t, err) || !assert.Equal(t, value, item.Value) {
60 txn.Rollback()
60 txn.Close()
6161 wg.Done()
6262 t.FailNow()
6363 }
6464 }
6565
66 txn.Rollback()
66 txn.Close()
6767 wg.Done()
6868 <-readers
6969 }()
8888 }
8989
9090 // Insert whole batch.
91 b := txn.Bucket("widgets")
9291 for _, item := range batchItems {
93 err := b.Put(item.Key, item.Value)
92 err := txn.Put("widgets", item.Key, item.Value)
9493 if !assert.NoError(t, err) {
9594 t.FailNow()
9695 }
77
88 // node represents an in-memory, deserialized page.
99 type node struct {
10 transaction *Transaction
10 transaction *RWTransaction
1111 isLeaf bool
1212 unbalanced bool
1313 key []byte
0 package bolt
1
2 import (
3 "sort"
4 "unsafe"
5 )
6
7 // RWTransaction represents a transaction that can read and write data.
8 // 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
13 nodes map[pgid]*node
14 pending []*node
15 }
16
17 // init initializes the transaction.
18 func (t *RWTransaction) init(db *DB) {
19 t.Transaction.init(db)
20 t.pages = make(map[pgid]*page)
21
22 // Increment the transaction id.
23 t.meta.txnid += txnid(1)
24 }
25
26 // CreateBucket creates a new bucket.
27 // Returns an error if the bucket already exists, if the bucket name is blank, or if the bucket name is too long.
28 func (t *RWTransaction) CreateBucket(name string) error {
29 // Check if bucket already exists.
30 if b := t.Bucket(name); b != nil {
31 return ErrBucketExists
32 } else if len(name) == 0 {
33 return ErrBucketNameRequired
34 } else if len(name) > MaxBucketNameSize {
35 return ErrBucketNameTooLarge
36 }
37
38 // Create a blank root leaf page.
39 p, err := t.allocate(1)
40 if err != nil {
41 return err
42 }
43 p.flags = leafPageFlag
44
45 // Add bucket to buckets page.
46 t.buckets.put(name, &bucket{root: p.id})
47
48 return nil
49 }
50
51 // CreateBucketIfNotExists creates a new bucket if it doesn't already exist.
52 // Returns an error if the bucket name is blank, or if the bucket name is too long.
53 func (t *RWTransaction) CreateBucketIfNotExists(name string) error {
54 err := t.CreateBucket(name)
55 if err != nil && err != ErrBucketExists {
56 return err
57 }
58 return nil
59 }
60
61 // DeleteBucket deletes a bucket.
62 // Returns an error if the bucket cannot be found.
63 func (t *RWTransaction) DeleteBucket(name string) error {
64 if b := t.Bucket(name); b == nil {
65 return ErrBucketNotFound
66 }
67
68 // Remove from buckets page.
69 t.buckets.del(name)
70
71 // TODO(benbjohnson): Free all pages.
72
73 return nil
74 }
75
76 // NextSequence returns an autoincrementing integer for the bucket.
77 func (t *RWTransaction) NextSequence(name string) (int, error) {
78 // Check if bucket already exists.
79 b := t.Bucket(name)
80 if b == nil {
81 return 0, ErrBucketNotFound
82 }
83
84 // Make sure next sequence number will not be larger than the maximum
85 // integer size of the system.
86 if b.bucket.sequence == uint64(maxInt) {
87 return 0, ErrSequenceOverflow
88 }
89
90 // Increment and return the sequence.
91 b.bucket.sequence++
92
93 return int(b.bucket.sequence), nil
94 }
95
96 // Put sets the value for a key inside of the named bucket.
97 // If the key exist then its previous value will be overwritten.
98 // Returns an error if the bucket is not found, if the key is blank, if the key is too large, or if the value is too large.
99 func (t *RWTransaction) Put(name string, key []byte, value []byte) error {
100 b := t.Bucket(name)
101 if b == nil {
102 return ErrBucketNotFound
103 }
104
105 // Validate the key and data size.
106 if len(key) == 0 {
107 return ErrKeyRequired
108 } else if len(key) > MaxKeySize {
109 return ErrKeyTooLarge
110 } else if len(value) > MaxValueSize {
111 return ErrValueTooLarge
112 }
113
114 // Move cursor to correct position.
115 c := b.cursor()
116 c.Seek(key)
117
118 // Insert the key/value.
119 c.node(t).put(key, key, value, 0)
120
121 return nil
122 }
123
124 // Delete removes a key from the named bucket.
125 // If the key does not exist then nothing is done and a nil error is returned.
126 // Returns an error if the bucket cannot be found.
127 func (t *RWTransaction) Delete(name string, key []byte) error {
128 b := t.Bucket(name)
129 if b == nil {
130 return ErrBucketNotFound
131 }
132
133 // Move cursor to correct position.
134 c := b.cursor()
135 c.Seek(key)
136
137 // Delete the node if we have a matching key.
138 c.node(t).del(key)
139
140 return nil
141 }
142
143 // Commit writes all changes to disk and updates the meta page.
144 // Returns an error if a disk write error occurs.
145 func (t *RWTransaction) Commit() error {
146 defer t.close()
147
148 // TODO(benbjohnson): Use vectorized I/O to write out dirty pages.
149
150 // Rebalance and spill data onto dirty pages.
151 t.rebalance()
152 t.spill()
153
154 // Spill buckets page.
155 p, err := t.allocate((t.buckets.size() / t.db.pageSize) + 1)
156 if err != nil {
157 return err
158 }
159 t.buckets.write(p)
160
161 // Write dirty pages to disk.
162 if err := t.write(); err != nil {
163 return err
164 }
165
166 // Update the meta.
167 t.meta.buckets = p.id
168
169 // Write meta to disk.
170 if err := t.writeMeta(); err != nil {
171 return err
172 }
173
174 return nil
175 }
176
177 // Rollback closes the transaction and ignores all previous updates.
178 func (t *RWTransaction) Rollback() {
179 t.close()
180 }
181
182 func (t *RWTransaction) close() {
183 t.db.rwlock.Unlock()
184 }
185
186 // allocate returns a contiguous block of memory starting at a given page.
187 func (t *RWTransaction) allocate(count int) (*page, error) {
188 p, err := t.db.allocate(count)
189 if err != nil {
190 return nil, err
191 }
192
193 // Save to our page cache.
194 t.pages[p.id] = p
195
196 return p, nil
197 }
198
199 // rebalance attempts to balance all nodes.
200 func (t *RWTransaction) rebalance() {
201 for _, n := range t.nodes {
202 n.rebalance()
203 }
204 }
205
206 // spill writes all the nodes to dirty pages.
207 func (t *RWTransaction) spill() error {
208 // Keep track of the current root nodes.
209 // We will update this at the end once all nodes are created.
210 type root struct {
211 node *node
212 pgid pgid
213 }
214 var roots []root
215
216 // Sort nodes by highest depth first.
217 nodes := make(nodesByDepth, 0, len(t.nodes))
218 for _, n := range t.nodes {
219 nodes = append(nodes, n)
220 }
221 sort.Sort(nodes)
222
223 // Spill nodes by deepest first.
224 for i := 0; i < len(nodes); i++ {
225 n := nodes[i]
226
227 // Save existing root buckets for later.
228 if n.parent == nil && n.pgid != 0 {
229 roots = append(roots, root{n, n.pgid})
230 }
231
232 // Split nodes into appropriate sized nodes.
233 // The first node in this list will be a reference to n to preserve ancestry.
234 newNodes := n.split(t.db.pageSize)
235 t.pending = newNodes
236
237 // If this is a root node that split then create a parent node.
238 if n.parent == nil && len(newNodes) > 1 {
239 n.parent = &node{transaction: t, isLeaf: false}
240 nodes = append(nodes, n.parent)
241 }
242
243 // Add node's page to the freelist.
244 if n.pgid > 0 {
245 t.db.freelist.free(t.id(), t.page(n.pgid))
246 }
247
248 // Write nodes to dirty pages.
249 for i, newNode := range newNodes {
250 // Allocate contiguous space for the node.
251 p, err := t.allocate((newNode.size() / t.db.pageSize) + 1)
252 if err != nil {
253 return err
254 }
255
256 // Write the node to the page.
257 newNode.write(p)
258 newNode.pgid = p.id
259 newNode.parent = n.parent
260
261 // The first node should use the existing entry, other nodes are inserts.
262 var oldKey []byte
263 if i == 0 {
264 oldKey = n.key
265 } else {
266 oldKey = newNode.inodes[0].key
267 }
268
269 // Update the parent entry.
270 if newNode.parent != nil {
271 newNode.parent.put(oldKey, newNode.inodes[0].key, nil, newNode.pgid)
272 }
273 }
274
275 t.pending = nil
276 }
277
278 // Update roots with new roots.
279 for _, root := range roots {
280 t.buckets.updateRoot(root.pgid, root.node.root().pgid)
281 }
282
283 // Clear out nodes now that they are all spilled.
284 t.nodes = make(map[pgid]*node)
285
286 return nil
287 }
288
289 // write writes any dirty pages to disk.
290 func (t *RWTransaction) write() error {
291 // Sort pages by id.
292 pages := make(pages, 0, len(t.pages))
293 for _, p := range t.pages {
294 pages = append(pages, p)
295 }
296 sort.Sort(pages)
297
298 // Write pages to disk in order.
299 for _, p := range pages {
300 size := (int(p.overflow) + 1) * t.db.pageSize
301 buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:size]
302 offset := int64(p.id) * int64(t.db.pageSize)
303 if _, err := t.db.file.WriteAt(buf, offset); err != nil {
304 return err
305 }
306 }
307
308 // Clear out page cache.
309 t.pages = make(map[pgid]*page)
310
311 return nil
312 }
313
314 // writeMeta writes the meta to the disk.
315 func (t *RWTransaction) writeMeta() error {
316 // Create a temporary buffer for the meta page.
317 buf := make([]byte, t.db.pageSize)
318 p := t.db.pageInBuffer(buf, 0)
319 t.meta.write(p)
320
321 // Write the meta page to file.
322 t.db.metafile.WriteAt(buf, int64(p.id)*int64(t.db.pageSize))
323
324 return nil
325 }
326
327 // node creates a node from a page and associates it with a given parent.
328 func (t *RWTransaction) node(pgid pgid, parent *node) *node {
329 // Retrieve node if it has already been fetched.
330 if n := t.nodes[pgid]; n != nil {
331 return n
332 }
333
334 // Otherwise create a branch and cache it.
335 n := &node{transaction: t, parent: parent}
336 if n.parent != nil {
337 n.depth = n.parent.depth + 1
338 }
339 n.read(t.page(pgid))
340 t.nodes[pgid] = n
341
342 return n
343 }
344
345 // dereference removes all references to the old mmap.
346 func (t *RWTransaction) dereference() {
347 for _, n := range t.nodes {
348 n.dereference()
349 }
350
351 for _, n := range t.pending {
352 n.dereference()
353 }
354 }
0 package bolt
1
2 import (
3 "bytes"
4 "fmt"
5 "os"
6 "strings"
7 "testing"
8 "testing/quick"
9
10 "github.com/stretchr/testify/assert"
11 )
12
13 // Ensure that a RWTransaction can be retrieved.
14 func TestRWTransaction(t *testing.T) {
15 withOpenDB(func(db *DB, path string) {
16 txn, err := db.RWTransaction()
17 assert.NotNil(t, txn)
18 assert.NoError(t, err)
19 assert.Equal(t, txn.DB(), db)
20 })
21 }
22
23 // Ensure that opening a RWTransaction while the DB is closed returns an error.
24 func TestRWTransactionOpenWithClosedDB(t *testing.T) {
25 withDB(func(db *DB, path string) {
26 txn, err := db.RWTransaction()
27 assert.Equal(t, err, ErrDatabaseNotOpen)
28 assert.Nil(t, txn)
29 })
30 }
31
32 // Ensure that a bucket can be created and retrieved.
33 func TestRWTransactionCreateBucket(t *testing.T) {
34 withOpenDB(func(db *DB, path string) {
35 // Create a bucket.
36 err := db.CreateBucket("widgets")
37 assert.NoError(t, err)
38
39 // Read the bucket through a separate transaction.
40 b, err := db.Bucket("widgets")
41 assert.NotNil(t, b)
42 assert.NoError(t, err)
43 })
44 }
45
46 // Ensure that a bucket can be created if it doesn't already exist.
47 func TestRWTransactionCreateBucketIfNotExists(t *testing.T) {
48 withOpenDB(func(db *DB, path string) {
49 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
50 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
51
52 // Read the bucket through a separate transaction.
53 b, err := db.Bucket("widgets")
54 assert.NotNil(t, b)
55 assert.NoError(t, err)
56 })
57 }
58
59 // Ensure that a bucket cannot be created twice.
60 func TestRWTransactionRecreateBucket(t *testing.T) {
61 withOpenDB(func(db *DB, path string) {
62 // Create a bucket.
63 err := db.CreateBucket("widgets")
64 assert.NoError(t, err)
65
66 // Create the same bucket again.
67 err = db.CreateBucket("widgets")
68 assert.Equal(t, err, ErrBucketExists)
69 })
70 }
71
72 // Ensure that a bucket is created with a non-blank name.
73 func TestRWTransactionCreateBucketWithoutName(t *testing.T) {
74 withOpenDB(func(db *DB, path string) {
75 err := db.CreateBucket("")
76 assert.Equal(t, err, ErrBucketNameRequired)
77 })
78 }
79
80 // Ensure that a bucket name is not too long.
81 func TestRWTransactionCreateBucketWithLongName(t *testing.T) {
82 withOpenDB(func(db *DB, path string) {
83 err := db.CreateBucket(strings.Repeat("X", 255))
84 assert.NoError(t, err)
85
86 err = db.CreateBucket(strings.Repeat("X", 256))
87 assert.Equal(t, err, ErrBucketNameTooLarge)
88 })
89 }
90
91 // Ensure that a bucket can be deleted.
92 func TestRWTransactionDeleteBucket(t *testing.T) {
93 withOpenDB(func(db *DB, path string) {
94 // Create a bucket and add a value.
95 db.CreateBucket("widgets")
96 db.Put("widgets", []byte("foo"), []byte("bar"))
97
98 // Delete the bucket and make sure we can't get the value.
99 assert.NoError(t, db.DeleteBucket("widgets"))
100 value, err := db.Get("widgets", []byte("foo"))
101 assert.Equal(t, err, ErrBucketNotFound)
102 assert.Nil(t, value)
103
104 // Create the bucket again and make sure there's not a phantom value.
105 assert.NoError(t, db.CreateBucket("widgets"))
106 value, err = db.Get("widgets", []byte("foo"))
107 assert.NoError(t, err)
108 assert.Nil(t, value)
109 })
110 }
111
112 // Ensure that a bucket can return an autoincrementing sequence.
113 func TestRWTransactionNextSequence(t *testing.T) {
114 withOpenDB(func(db *DB, path string) {
115 db.CreateBucket("widgets")
116 db.CreateBucket("woojits")
117
118 // Make sure sequence increments.
119 seq, err := db.NextSequence("widgets")
120 assert.NoError(t, err)
121 assert.Equal(t, seq, 1)
122 seq, err = db.NextSequence("widgets")
123 assert.NoError(t, err)
124 assert.Equal(t, seq, 2)
125
126 // Buckets should be separate.
127 seq, err = db.NextSequence("woojits")
128 assert.NoError(t, err)
129 assert.Equal(t, seq, 1)
130
131 // Missing buckets return an error.
132 seq, err = db.NextSequence("no_such_bucket")
133 assert.Equal(t, err, ErrBucketNotFound)
134 assert.Equal(t, seq, 0)
135 })
136 }
137
138 // Ensure that incrementing past the maximum sequence number will return an error.
139 func TestRWTransactionNextSequenceOverflow(t *testing.T) {
140 withOpenDB(func(db *DB, path string) {
141 db.CreateBucket("widgets")
142 db.Do(func(txn *RWTransaction) error {
143 b := txn.Bucket("widgets")
144 b.bucket.sequence = uint64(maxInt)
145 seq, err := txn.NextSequence("widgets")
146 assert.Equal(t, err, ErrSequenceOverflow)
147 assert.Equal(t, seq, 0)
148 return nil
149 })
150 })
151 }
152
153 // Ensure that an error is returned when inserting into a bucket that doesn't exist.
154 func TestRWTransactionPutBucketNotFound(t *testing.T) {
155 withOpenDB(func(db *DB, path string) {
156 err := db.Put("widgets", []byte("foo"), []byte("bar"))
157 assert.Equal(t, err, ErrBucketNotFound)
158 })
159 }
160
161 // Ensure that an error is returned when inserting with an empty key.
162 func TestRWTransactionPutEmptyKey(t *testing.T) {
163 withOpenDB(func(db *DB, path string) {
164 db.CreateBucket("widgets")
165 err := db.Put("widgets", []byte(""), []byte("bar"))
166 assert.Equal(t, err, ErrKeyRequired)
167 err = db.Put("widgets", nil, []byte("bar"))
168 assert.Equal(t, err, ErrKeyRequired)
169 })
170 }
171
172 // Ensure that an error is returned when inserting with a key that's too large.
173 func TestRWTransactionPutKeyTooLarge(t *testing.T) {
174 withOpenDB(func(db *DB, path string) {
175 db.CreateBucket("widgets")
176 err := db.Put("widgets", make([]byte, 32769), []byte("bar"))
177 assert.Equal(t, err, ErrKeyTooLarge)
178 })
179 }
180
181 // Ensure that an error is returned when deleting from a bucket that doesn't exist.
182 func TestRWTransactionDeleteBucketNotFound(t *testing.T) {
183 withOpenDB(func(db *DB, path string) {
184 err := db.DeleteBucket("widgets")
185 assert.Equal(t, err, ErrBucketNotFound)
186 })
187 }
188
189 // Ensure that a bucket can write random keys and values across multiple txns.
190 func TestRWTransactionPutSingle(t *testing.T) {
191 index := 0
192 f := func(items testdata) bool {
193 withOpenDB(func(db *DB, path string) {
194 m := make(map[string][]byte)
195
196 db.CreateBucket("widgets")
197 for _, item := range items {
198 if err := db.Put("widgets", item.Key, item.Value); err != nil {
199 panic("put error: " + err.Error())
200 }
201 m[string(item.Key)] = item.Value
202
203 // Verify all key/values so far.
204 i := 0
205 for k, v := range m {
206 value, err := db.Get("widgets", []byte(k))
207 if err != nil {
208 panic("get error: " + err.Error())
209 }
210 if !bytes.Equal(value, v) {
211 db.CopyFile("/tmp/bolt.put.single.db", 0666)
212 t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v)
213 }
214 i++
215 }
216 }
217
218 fmt.Fprint(os.Stderr, ".")
219 })
220 index++
221 return true
222 }
223 if err := quick.Check(f, qconfig()); err != nil {
224 t.Error(err)
225 }
226 fmt.Fprint(os.Stderr, "\n")
227 }
228
229 // Ensure that a transaction can insert multiple key/value pairs at once.
230 func TestRWTransactionPutMultiple(t *testing.T) {
231 f := func(items testdata) bool {
232 withOpenDB(func(db *DB, path string) {
233 // Bulk insert all values.
234 db.CreateBucket("widgets")
235 rwtxn, _ := db.RWTransaction()
236 for _, item := range items {
237 assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
238 }
239 assert.NoError(t, rwtxn.Commit())
240
241 // Verify all items exist.
242 txn, _ := db.Transaction()
243 for _, item := range items {
244 value, err := txn.Get("widgets", item.Key)
245 assert.NoError(t, err)
246 if !assert.Equal(t, item.Value, value) {
247 db.CopyFile("/tmp/bolt.put.multiple.db", 0666)
248 t.FailNow()
249 }
250 }
251 txn.Close()
252 })
253 fmt.Fprint(os.Stderr, ".")
254 return true
255 }
256 if err := quick.Check(f, qconfig()); err != nil {
257 t.Error(err)
258 }
259 fmt.Fprint(os.Stderr, "\n")
260 }
261
262 // Ensure that a transaction can delete all key/value pairs and return to a single leaf page.
263 func TestRWTransactionDelete(t *testing.T) {
264 f := func(items testdata) bool {
265 withOpenDB(func(db *DB, path string) {
266 // Bulk insert all values.
267 db.CreateBucket("widgets")
268 rwtxn, _ := db.RWTransaction()
269 for _, item := range items {
270 assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
271 }
272 assert.NoError(t, rwtxn.Commit())
273
274 // Remove items one at a time and check consistency.
275 for i, item := range items {
276 assert.NoError(t, db.Delete("widgets", item.Key))
277
278 // Anything before our deletion index should be nil.
279 txn, _ := db.Transaction()
280 for j, exp := range items {
281 if j > i {
282 value, err := txn.Get("widgets", exp.Key)
283 assert.NoError(t, err)
284 if !assert.Equal(t, exp.Value, value) {
285 t.FailNow()
286 }
287 } else {
288 value, err := txn.Get("widgets", exp.Key)
289 assert.NoError(t, err)
290 if !assert.Nil(t, value) {
291 t.FailNow()
292 }
293 }
294 }
295 txn.Close()
296 }
297 })
298 fmt.Fprint(os.Stderr, ".")
299 return true
300 }
301 if err := quick.Check(f, qconfig()); err != nil {
302 t.Error(err)
303 }
304 fmt.Fprint(os.Stderr, "\n")
305 }
00 package bolt
11
22 import (
3 "sort"
4 "unsafe"
3 "bytes"
54 )
5
6 // Transaction represents a read-only transaction on the database.
7 // It can be used for retrieving values for keys as well as creating cursors for
8 // iterating over the data.
9 //
10 // IMPORTANT: You must close transactions when you are done with them. Pages
11 // can not be reclaimed by the writer until no more transactions are using them.
12 // A long running read transaction can cause the database to quickly grow.
13 type Transaction struct {
14 db *DB
15 meta *meta
16 buckets *buckets
17 pages map[pgid]*page
18 }
619
720 // txnid represents the internal transaction identifier.
821 type txnid uint64
922
10 // Transaction represents a consistent view into the database.
11 // Read-only transactions can be created by calling DB.Transaction().
12 // Read-write transactions can be created by calling DB.RWTransaction().
13 // Only one read-write transaction is allowed at a time.
14 type Transaction struct {
15 db *DB
16 meta *meta
17 buckets *buckets
18 writable bool
19 pages map[pgid]*page
20 nodes map[pgid]*node
21 pending []*node
22 }
23
24 // init initializes the transaction.
23 // init initializes the transaction and associates it with a database.
2524 func (t *Transaction) init(db *DB) {
2625 t.db = db
2726 t.pages = nil
3332 // Read in the buckets page.
3433 t.buckets = &buckets{}
3534 t.buckets.read(t.page(t.meta.buckets))
36
37 t.pages = make(map[pgid]*page)
38
39 // Increment the transaction id.
40 t.meta.txnid += txnid(1)
4135 }
4236
4337 // id returns the transaction id.
4539 return t.meta.txnid
4640 }
4741
42 // Close closes the transaction and releases any pages it is using.
43 func (t *Transaction) Close() {
44 t.db.removeTransaction(t)
45 }
46
4847 // DB returns a reference to the database that created the transaction.
4948 func (t *Transaction) DB() *DB {
5049 return t.db
5150 }
5251
53 // Writable returns whether the transaction can change data.
54 func (t *Transaction) Writable() bool {
55 return t.writable
56 }
57
5852 // Bucket retrieves a bucket by name.
5953 // Returns nil if the bucket does not exist.
6054 func (t *Transaction) Bucket(name string) *Bucket {
61 _assert(t.isOpen(), "transaction not open")
6255 b := t.buckets.get(name)
6356 if b == nil {
6457 return nil
7366
7467 // Buckets retrieves a list of all buckets.
7568 func (t *Transaction) Buckets() []*Bucket {
76 _assert(t.isOpen(), "transaction not open")
7769 buckets := make([]*Bucket, 0, len(t.buckets.items))
7870 for name, b := range t.buckets.items {
7971 bucket := &Bucket{bucket: b, transaction: t, name: name}
8274 return buckets
8375 }
8476
85 // CreateBucket creates a new bucket.
86 // Returns an error if the transaction is read-only, if bucket already exists,
87 // if the bucket name is blank, or if the bucket name is too long.
88 func (t *Transaction) CreateBucket(name string) error {
89 _assert(t.isOpen(), "transaction not open")
90 if !t.writable {
91 return ErrTransactionNotWritable
92 } else if b := t.Bucket(name); b != nil {
93 return ErrBucketExists
94 } else if len(name) == 0 {
95 return ErrBucketNameRequired
96 } else if len(name) > MaxBucketNameSize {
97 return ErrBucketNameTooLarge
77 // Cursor creates a cursor associated with a given bucket.
78 // The cursor is only valid as long as the Transaction is open.
79 // Do not use a cursor after the transaction is closed.
80 func (t *Transaction) Cursor(name string) (*Cursor, error) {
81 b := t.Bucket(name)
82 if b == nil {
83 return nil, ErrBucketNotFound
9884 }
85 return b.cursor(), nil
86 }
9987
100 // Create a blank root leaf page.
101 p, err := t.allocate(1)
88 // Get retrieves the value for a key in a named bucket.
89 // Returns a nil value if the key does not exist.
90 // Returns an error if the bucket does not exist.
91 func (t *Transaction) Get(name string, key []byte) (value []byte, err error) {
92 c, err := t.Cursor(name)
93 if err != nil {
94 return nil, err
95 }
96 k, v := c.Seek(key)
97 // If our target node isn't the same key as what's passed in then return nil.
98 if !bytes.Equal(key, k) {
99 return nil, nil
100 }
101 return v, nil
102 }
103
104 // ForEach executes a function for each key/value pair in a bucket.
105 // An error is returned if the bucket cannot be found.
106 func (t *Transaction) ForEach(name string, fn func(k, v []byte) error) error {
107 // Open a cursor on the bucket.
108 c, err := t.Cursor(name)
102109 if err != nil {
103110 return err
104111 }
105 p.flags = leafPageFlag
106112
107 // Add bucket to buckets page.
108 t.buckets.put(name, &bucket{root: p.id})
109
110 return nil
111 }
112
113 // CreateBucketIfNotExists creates a new bucket if it doesn't already exist.
114 // Returns an error if the transaction is read-only, if the bucket name is
115 // blank, or if the bucket name is too long.
116 func (t *Transaction) CreateBucketIfNotExists(name string) error {
117 _assert(t.isOpen(), "transaction not open")
118 err := t.CreateBucket(name)
119 if err != nil && err != ErrBucketExists {
120 return err
121 }
122 return nil
123 }
124
125 // DeleteBucket deletes a bucket.
126 // Returns an error if the transaction is read-only or if the bucket cannot be found.
127 func (t *Transaction) DeleteBucket(name string) error {
128 _assert(t.isOpen(), "transaction not open")
129 if !t.writable {
130 return ErrTransactionNotWritable
131 } else if b := t.Bucket(name); b == nil {
132 return ErrBucketNotFound
133 }
134
135 // Remove from buckets page.
136 t.buckets.del(name)
137
138 // TODO(benbjohnson): Free all pages.
139
140 return nil
141 }
142
143 // Commit writes all changes to disk and updates the meta page.
144 // Read-only transactions will simply be closed.
145 // Returns an error if a disk write error occurs.
146 func (t *Transaction) Commit() error {
147 defer t.close()
148
149 // Ignore commit for read-only transactions.
150 if !t.writable {
151 return nil
152 }
153
154 // TODO(benbjohnson): Use vectorized I/O to write out dirty pages.
155
156 // Rebalance and spill data onto dirty pages.
157 t.rebalance()
158 t.spill()
159
160 // Spill buckets page.
161 p, err := t.allocate((t.buckets.size() / t.db.pageSize) + 1)
162 if err != nil {
163 return err
164 }
165 t.buckets.write(p)
166
167 // Write dirty pages to disk.
168 if err := t.write(); err != nil {
169 return err
170 }
171
172 // Update the meta.
173 t.meta.buckets = p.id
174
175 // Write meta to disk.
176 if err := t.writeMeta(); err != nil {
177 return err
178 }
179
180 return nil
181 }
182
183 // Rollback closes the transaction and rolls back any pending changes.
184 func (t *Transaction) Rollback() {
185 t.close()
186 }
187
188 func (t *Transaction) close() {
189 if t.writable {
190 t.db.rwlock.Unlock()
191 } else {
192 t.db.removeTransaction(t)
193 }
194
195 // Detach from the database.
196 t.db = nil
197 }
198
199 // isOpen returns whether the transaction is currently open.
200 func (t *Transaction) isOpen() bool {
201 return t.db != nil
202 }
203
204 // allocate returns a contiguous block of memory starting at a given page.
205 func (t *Transaction) allocate(count int) (*page, error) {
206 p, err := t.db.allocate(count)
207 if err != nil {
208 return nil, err
209 }
210
211 // Save to our page cache.
212 t.pages[p.id] = p
213
214 return p, nil
215 }
216
217 // rebalance attempts to balance all nodes.
218 func (t *Transaction) rebalance() {
219 for _, n := range t.nodes {
220 n.rebalance()
221 }
222 }
223
224 // spill writes all the nodes to dirty pages.
225 func (t *Transaction) spill() error {
226 // Keep track of the current root nodes.
227 // We will update this at the end once all nodes are created.
228 type root struct {
229 node *node
230 pgid pgid
231 }
232 var roots []root
233
234 // Sort nodes by highest depth first.
235 nodes := make(nodesByDepth, 0, len(t.nodes))
236 for _, n := range t.nodes {
237 nodes = append(nodes, n)
238 }
239 sort.Sort(nodes)
240
241 // Spill nodes by deepest first.
242 for i := 0; i < len(nodes); i++ {
243 n := nodes[i]
244
245 // Save existing root buckets for later.
246 if n.parent == nil && n.pgid != 0 {
247 roots = append(roots, root{n, n.pgid})
248 }
249
250 // Split nodes into appropriate sized nodes.
251 // The first node in this list will be a reference to n to preserve ancestry.
252 newNodes := n.split(t.db.pageSize)
253 t.pending = newNodes
254
255 // If this is a root node that split then create a parent node.
256 if n.parent == nil && len(newNodes) > 1 {
257 n.parent = &node{transaction: t, isLeaf: false}
258 nodes = append(nodes, n.parent)
259 }
260
261 // Add node's page to the freelist.
262 if n.pgid > 0 {
263 t.db.freelist.free(t.id(), t.page(n.pgid))
264 }
265
266 // Write nodes to dirty pages.
267 for i, newNode := range newNodes {
268 // Allocate contiguous space for the node.
269 p, err := t.allocate((newNode.size() / t.db.pageSize) + 1)
270 if err != nil {
271 return err
272 }
273
274 // Write the node to the page.
275 newNode.write(p)
276 newNode.pgid = p.id
277 newNode.parent = n.parent
278
279 // The first node should use the existing entry, other nodes are inserts.
280 var oldKey []byte
281 if i == 0 {
282 oldKey = n.key
283 } else {
284 oldKey = newNode.inodes[0].key
285 }
286
287 // Update the parent entry.
288 if newNode.parent != nil {
289 newNode.parent.put(oldKey, newNode.inodes[0].key, nil, newNode.pgid)
290 }
291 }
292
293 t.pending = nil
294 }
295
296 // Update roots with new roots.
297 for _, root := range roots {
298 t.buckets.updateRoot(root.pgid, root.node.root().pgid)
299 }
300
301 // Clear out nodes now that they are all spilled.
302 t.nodes = make(map[pgid]*node)
303
304 return nil
305 }
306
307 // write writes any dirty pages to disk.
308 func (t *Transaction) write() error {
309 // Sort pages by id.
310 pages := make(pages, 0, len(t.pages))
311 for _, p := range t.pages {
312 pages = append(pages, p)
313 }
314 sort.Sort(pages)
315
316 // Write pages to disk in order.
317 for _, p := range pages {
318 size := (int(p.overflow) + 1) * t.db.pageSize
319 buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:size]
320 offset := int64(p.id) * int64(t.db.pageSize)
321 if _, err := t.db.file.WriteAt(buf, offset); err != nil {
113 // Iterate over each key/value pair in the bucket.
114 for k, v := c.First(); k != nil; k, v = c.Next() {
115 if err := fn(k, v); err != nil {
322116 return err
323117 }
324118 }
325
326 // Clear out page cache.
327 t.pages = make(map[pgid]*page)
328
329 return nil
330 }
331
332 // writeMeta writes the meta to the disk.
333 func (t *Transaction) writeMeta() error {
334 // Create a temporary buffer for the meta page.
335 buf := make([]byte, t.db.pageSize)
336 p := t.db.pageInBuffer(buf, 0)
337 t.meta.write(p)
338
339 // Write the meta page to file.
340 t.db.metafile.WriteAt(buf, int64(p.id)*int64(t.db.pageSize))
341119
342120 return nil
343121 }
356134 return t.db.page(id)
357135 }
358136
359 // node creates a node from a page and associates it with a given parent.
360 func (t *Transaction) node(pgid pgid, parent *node) *node {
361 // Retrieve node if it has already been fetched.
362 if n := t.nodes[pgid]; n != nil {
363 return n
364 }
365
366 // Otherwise create a branch and cache it.
367 n := &node{transaction: t, parent: parent}
368 if n.parent != nil {
369 n.depth = n.parent.depth + 1
370 }
371 n.read(t.page(pgid))
372 t.nodes[pgid] = n
373
374 return n
375 }
376
377 // dereference removes all references to the old mmap.
378 func (t *Transaction) dereference() {
379 for _, n := range t.nodes {
380 n.dereference()
381 }
382
383 for _, n := range t.pending {
384 n.dereference()
385 }
386 }
387
388137 // forEachPage iterates over every page within a given page and executes a function.
389138 func (t *Transaction) forEachPage(pgid pgid, depth int, fn func(*page, int)) {
390139 p := t.page(pgid)
00 package bolt
11
22 import (
3 "bytes"
43 "fmt"
54 "os"
65 "sort"
7 "strings"
86 "testing"
97 "testing/quick"
108
5452 withOpenDB(func(db *DB, path string) {
5553 db.CreateBucket("widgets")
5654 txn, _ := db.Transaction()
57 c := txn.Bucket("widgets").Cursor()
55 c, err := txn.Cursor("widgets")
56 assert.NoError(t, err)
5857 k, v := c.First()
5958 assert.Nil(t, k)
6059 assert.Nil(t, v)
61 txn.Rollback()
60 txn.Close()
61 })
62 }
63
64 // Ensure that a Transaction returns a nil when a bucket doesn't exist.
65 func TestTransactionCursorMissingBucket(t *testing.T) {
66 withOpenDB(func(db *DB, path string) {
67 db.CreateBucket("widgets")
68 txn, _ := db.Transaction()
69 c, err := txn.Cursor("woojits")
70 assert.Nil(t, c)
71 assert.Equal(t, err, ErrBucketNotFound)
72 txn.Close()
6273 })
6374 }
6475
7081 db.Put("widgets", []byte("foo"), []byte{0})
7182 db.Put("widgets", []byte("bar"), []byte{1})
7283 txn, _ := db.Transaction()
73 c := txn.Bucket("widgets").Cursor()
84 c, err := txn.Cursor("widgets")
85 assert.NoError(t, err)
7486
7587 k, v := c.First()
7688 assert.Equal(t, string(k), "bar")
92104 assert.Nil(t, k)
93105 assert.Nil(t, v)
94106
95 txn.Rollback()
107 txn.Close()
96108 })
97109 }
98110
104116 db.Put("widgets", []byte("foo"), []byte{0})
105117 db.Put("widgets", []byte("bar"), []byte{1})
106118 txn, _ := db.Transaction()
107 c := txn.Bucket("widgets").Cursor()
119 c, err := txn.Cursor("widgets")
120 assert.NoError(t, err)
108121
109122 k, v := c.Last()
110123 assert.Equal(t, string(k), "foo")
126139 assert.Nil(t, k)
127140 assert.Nil(t, v)
128141
129 txn.Rollback()
142 txn.Close()
130143 })
131144 }
132145
138151 db.Put("widgets", []byte("foo"), []byte{})
139152
140153 txn, _ := db.Transaction()
141 c := txn.Bucket("widgets").Cursor()
154 c, err := txn.Cursor("widgets")
155 assert.NoError(t, err)
142156
143157 k, _ := c.First()
144158 assert.Equal(t, string(k), "bar")
152166 k, _ = c.Next()
153167 assert.Equal(t, string(k), "foo")
154168
155 txn.Rollback()
169 txn.Close()
156170 })
157171 }
158172
162176 withOpenDB(func(db *DB, path string) {
163177 // Bulk insert all values.
164178 db.CreateBucket("widgets")
165 txn, _ := db.RWTransaction()
166 b := txn.Bucket("widgets")
179 rwtxn, _ := db.RWTransaction()
167180 for _, item := range items {
168 assert.NoError(t, b.Put(item.Key, item.Value))
169 }
170 assert.NoError(t, txn.Commit())
181 assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
182 }
183 assert.NoError(t, rwtxn.Commit())
171184
172185 // Sort test data.
173186 sort.Sort(items)
174187
175188 // Iterate over all items and check consistency.
176189 var index = 0
177 txn, _ = db.Transaction()
178 c := txn.Bucket("widgets").Cursor()
190 txn, _ := db.Transaction()
191 c, err := txn.Cursor("widgets")
192 assert.NoError(t, err)
179193 for k, v := c.First(); k != nil && index < len(items); k, v = c.Next() {
180194 assert.Equal(t, k, items[index].Key)
181195 assert.Equal(t, v, items[index].Value)
182196 index++
183197 }
184198 assert.Equal(t, len(items), index)
185 txn.Rollback()
199 txn.Close()
186200 })
187201 fmt.Fprint(os.Stderr, ".")
188202 return true
199213 withOpenDB(func(db *DB, path string) {
200214 // Bulk insert all values.
201215 db.CreateBucket("widgets")
202 txn, _ := db.RWTransaction()
203 b := txn.Bucket("widgets")
216 rwtxn, _ := db.RWTransaction()
204217 for _, item := range items {
205 assert.NoError(t, b.Put(item.Key, item.Value))
206 }
207 assert.NoError(t, txn.Commit())
218 assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value))
219 }
220 assert.NoError(t, rwtxn.Commit())
208221
209222 // Sort test data.
210223 sort.Sort(revtestdata(items))
211224
212225 // Iterate over all items and check consistency.
213226 var index = 0
214 txn, _ = db.Transaction()
215 c := txn.Bucket("widgets").Cursor()
227 txn, _ := db.Transaction()
228 c, err := txn.Cursor("widgets")
229 assert.NoError(t, err)
216230 for k, v := c.Last(); k != nil && index < len(items); k, v = c.Prev() {
217231 assert.Equal(t, k, items[index].Key)
218232 assert.Equal(t, v, items[index].Value)
219233 index++
220234 }
221235 assert.Equal(t, len(items), index)
222 txn.Rollback()
236 txn.Close()
223237 })
224238 fmt.Fprint(os.Stderr, ".")
225239 return true
229243 }
230244 fmt.Fprint(os.Stderr, "\n")
231245 }
232
233 // Ensure that a bucket can be created and retrieved.
234 func TestTransactionCreateBucket(t *testing.T) {
235 withOpenDB(func(db *DB, path string) {
236 // Create a bucket.
237 err := db.CreateBucket("widgets")
238 assert.NoError(t, err)
239
240 // Read the bucket through a separate transaction.
241 b, err := db.Bucket("widgets")
242 assert.NotNil(t, b)
243 assert.NoError(t, err)
244 })
245 }
246
247 // Ensure that a bucket can be created if it doesn't already exist.
248 func TestTransactionCreateBucketIfNotExists(t *testing.T) {
249 withOpenDB(func(db *DB, path string) {
250 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
251 assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
252
253 // Read the bucket through a separate transaction.
254 b, err := db.Bucket("widgets")
255 assert.NotNil(t, b)
256 assert.NoError(t, err)
257 })
258 }
259
260 // Ensure that a bucket cannot be created twice.
261 func TestTransactionRecreateBucket(t *testing.T) {
262 withOpenDB(func(db *DB, path string) {
263 // Create a bucket.
264 err := db.CreateBucket("widgets")
265 assert.NoError(t, err)
266
267 // Create the same bucket again.
268 err = db.CreateBucket("widgets")
269 assert.Equal(t, err, ErrBucketExists)
270 })
271 }
272
273 // Ensure that a bucket is created with a non-blank name.
274 func TestTransactionCreateBucketWithoutName(t *testing.T) {
275 withOpenDB(func(db *DB, path string) {
276 err := db.CreateBucket("")
277 assert.Equal(t, err, ErrBucketNameRequired)
278 })
279 }
280
281 // Ensure that a bucket name is not too long.
282 func TestTransactionCreateBucketWithLongName(t *testing.T) {
283 withOpenDB(func(db *DB, path string) {
284 err := db.CreateBucket(strings.Repeat("X", 255))
285 assert.NoError(t, err)
286
287 err = db.CreateBucket(strings.Repeat("X", 256))
288 assert.Equal(t, err, ErrBucketNameTooLarge)
289 })
290 }
291
292 // Ensure that a bucket can be deleted.
293 func TestTransactionDeleteBucket(t *testing.T) {
294 withOpenDB(func(db *DB, path string) {
295 // Create a bucket and add a value.
296 db.CreateBucket("widgets")
297 db.Put("widgets", []byte("foo"), []byte("bar"))
298
299 // Delete the bucket and make sure we can't get the value.
300 assert.NoError(t, db.DeleteBucket("widgets"))
301 value, err := db.Get("widgets", []byte("foo"))
302 assert.Equal(t, err, ErrBucketNotFound)
303 assert.Nil(t, value)
304
305 // Create the bucket again and make sure there's not a phantom value.
306 assert.NoError(t, db.CreateBucket("widgets"))
307 value, err = db.Get("widgets", []byte("foo"))
308 assert.NoError(t, err)
309 assert.Nil(t, value)
310 })
311 }
312
313 // Ensure that a bucket can return an autoincrementing sequence.
314 func TestTransactionNextSequence(t *testing.T) {
315 withOpenDB(func(db *DB, path string) {
316 db.CreateBucket("widgets")
317 db.CreateBucket("woojits")
318
319 // Make sure sequence increments.
320 seq, err := db.NextSequence("widgets")
321 assert.NoError(t, err)
322 assert.Equal(t, seq, 1)
323 seq, err = db.NextSequence("widgets")
324 assert.NoError(t, err)
325 assert.Equal(t, seq, 2)
326
327 // Buckets should be separate.
328 seq, err = db.NextSequence("woojits")
329 assert.NoError(t, err)
330 assert.Equal(t, seq, 1)
331
332 // Missing buckets return an error.
333 seq, err = db.NextSequence("no_such_bucket")
334 assert.Equal(t, err, ErrBucketNotFound)
335 assert.Equal(t, seq, 0)
336 })
337 }
338
339 // Ensure that incrementing past the maximum sequence number will return an error.
340 func TestTransactionNextSequenceOverflow(t *testing.T) {
341 withOpenDB(func(db *DB, path string) {
342 db.CreateBucket("widgets")
343 db.Do(func(txn *Transaction) error {
344 b := txn.Bucket("widgets")
345 b.bucket.sequence = uint64(maxInt)
346 seq, err := b.NextSequence()
347 assert.Equal(t, err, ErrSequenceOverflow)
348 assert.Equal(t, seq, 0)
349 return nil
350 })
351 })
352 }
353
354 // Ensure that an error is returned when inserting into a bucket that doesn't exist.
355 func TestTransactionPutBucketNotFound(t *testing.T) {
356 withOpenDB(func(db *DB, path string) {
357 err := db.Put("widgets", []byte("foo"), []byte("bar"))
358 assert.Equal(t, err, ErrBucketNotFound)
359 })
360 }
361
362 // Ensure that an error is returned when inserting with an empty key.
363 func TestTransactionPutEmptyKey(t *testing.T) {
364 withOpenDB(func(db *DB, path string) {
365 db.CreateBucket("widgets")
366 err := db.Put("widgets", []byte(""), []byte("bar"))
367 assert.Equal(t, err, ErrKeyRequired)
368 err = db.Put("widgets", nil, []byte("bar"))
369 assert.Equal(t, err, ErrKeyRequired)
370 })
371 }
372
373 // Ensure that an error is returned when inserting with a key that's too large.
374 func TestTransactionPutKeyTooLarge(t *testing.T) {
375 withOpenDB(func(db *DB, path string) {
376 db.CreateBucket("widgets")
377 err := db.Put("widgets", make([]byte, 32769), []byte("bar"))
378 assert.Equal(t, err, ErrKeyTooLarge)
379 })
380 }
381
382 // Ensure that an error is returned when deleting from a bucket that doesn't exist.
383 func TestTransactionDeleteBucketNotFound(t *testing.T) {
384 withOpenDB(func(db *DB, path string) {
385 err := db.DeleteBucket("widgets")
386 assert.Equal(t, err, ErrBucketNotFound)
387 })
388 }
389
390 // Ensure that a bucket can write random keys and values across multiple txns.
391 func TestTransactionPutSingle(t *testing.T) {
392 index := 0
393 f := func(items testdata) bool {
394 withOpenDB(func(db *DB, path string) {
395 m := make(map[string][]byte)
396
397 db.CreateBucket("widgets")
398 for _, item := range items {
399 if err := db.Put("widgets", item.Key, item.Value); err != nil {
400 panic("put error: " + err.Error())
401 }
402 m[string(item.Key)] = item.Value
403
404 // Verify all key/values so far.
405 i := 0
406 for k, v := range m {
407 value, err := db.Get("widgets", []byte(k))
408 if err != nil {
409 panic("get error: " + err.Error())
410 }
411 if !bytes.Equal(value, v) {
412 db.CopyFile("/tmp/bolt.put.single.db", 0666)
413 t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v)
414 }
415 i++
416 }
417 }
418
419 fmt.Fprint(os.Stderr, ".")
420 })
421 index++
422 return true
423 }
424 if err := quick.Check(f, qconfig()); err != nil {
425 t.Error(err)
426 }
427 fmt.Fprint(os.Stderr, "\n")
428 }
429
430 // Ensure that a transaction can insert multiple key/value pairs at once.
431 func TestTransactionPutMultiple(t *testing.T) {
432 f := func(items testdata) bool {
433 withOpenDB(func(db *DB, path string) {
434 // Bulk insert all values.
435 db.CreateBucket("widgets")
436 txn, _ := db.RWTransaction()
437 b := txn.Bucket("widgets")
438 for _, item := range items {
439 assert.NoError(t, b.Put(item.Key, item.Value))
440 }
441 assert.NoError(t, txn.Commit())
442
443 // Verify all items exist.
444 txn, _ = db.Transaction()
445 for _, item := range items {
446 value := txn.Bucket("widgets").Get(item.Key)
447 if !assert.Equal(t, item.Value, value) {
448 db.CopyFile("/tmp/bolt.put.multiple.db", 0666)
449 t.FailNow()
450 }
451 }
452 txn.Rollback()
453 })
454 fmt.Fprint(os.Stderr, ".")
455 return true
456 }
457 if err := quick.Check(f, qconfig()); err != nil {
458 t.Error(err)
459 }
460 fmt.Fprint(os.Stderr, "\n")
461 }
462
463 // Ensure that a transaction can delete all key/value pairs and return to a single leaf page.
464 func TestTransactionDelete(t *testing.T) {
465 f := func(items testdata) bool {
466 withOpenDB(func(db *DB, path string) {
467 // Bulk insert all values.
468 db.CreateBucket("widgets")
469 txn, _ := db.RWTransaction()
470 b := txn.Bucket("widgets")
471 for _, item := range items {
472 assert.NoError(t, b.Put(item.Key, item.Value))
473 }
474 assert.NoError(t, txn.Commit())
475
476 // Remove items one at a time and check consistency.
477 for i, item := range items {
478 assert.NoError(t, db.Delete("widgets", item.Key))
479
480 // Anything before our deletion index should be nil.
481 txn, _ := db.Transaction()
482 for j, exp := range items {
483 if j > i {
484 value := txn.Bucket("widgets").Get(exp.Key)
485 if !assert.Equal(t, exp.Value, value) {
486 t.FailNow()
487 }
488 } else {
489 value := txn.Bucket("widgets").Get(exp.Key)
490 if !assert.Nil(t, value) {
491 t.FailNow()
492 }
493 }
494 }
495 txn.Rollback()
496 }
497 })
498 fmt.Fprint(os.Stderr, ".")
499 return true
500 }
501 if err := quick.Check(f, qconfig()); err != nil {
502 t.Error(err)
503 }
504 fmt.Fprint(os.Stderr, "\n")
505 }