2014-01-08 15:06:17 +00:00
package bolt
2014-02-23 06:08:30 +00:00
import (
"bytes"
2014-04-07 22:24:51 +00:00
"fmt"
"unsafe"
2014-03-24 14:31:15 +00:00
)
2014-05-05 13:56:54 +00:00
const (
// MaxKeySize is the maximum length of a key, in bytes.
MaxKeySize = 32768
// MaxValueSize is the maximum length of a value, in bytes.
MaxValueSize = 4294967295
)
const (
maxUint = ^ uint ( 0 )
minUint = 0
maxInt = int ( ^ uint ( 0 ) >> 1 )
minInt = - maxInt - 1
)
2014-05-05 16:27:02 +00:00
const bucketHeaderSize = int ( unsafe . Sizeof ( bucket { } ) )
2014-07-24 16:36:09 +00:00
const (
minFillPercent = 0.1
maxFillPercent = 1.0
)
// DefaultFillPercent is the percentage that split pages are filled.
// This value can be changed by setting Bucket.FillPercent.
const DefaultFillPercent = 0.5
2014-02-12 21:57:27 +00:00
// Bucket represents a collection of key/value pairs inside the database.
2014-01-10 14:32:12 +00:00
type Bucket struct {
2014-01-14 20:01:02 +00:00
* bucket
2014-05-07 17:23:30 +00:00
tx * Tx // the associated transaction
buckets map [ string ] * Bucket // subbucket cache
page * page // inline page reference
rootNode * node // materialized node for the root page.
nodes map [ pgid ] * node // node cache
2014-07-24 16:36:09 +00:00
// Sets the threshold for filling nodes when they split. By default,
// the bucket will fill to 50% but it can be useful to increase this
// amount if you know that your write workloads are mostly append-only.
2014-07-24 17:25:25 +00:00
//
// This is non-persisted across transactions so it must be set in every Tx.
2014-07-24 16:36:09 +00:00
FillPercent float64
2014-01-14 20:01:02 +00:00
}
2014-02-12 21:57:27 +00:00
// bucket represents the on-file representation of a bucket.
2014-05-07 17:23:30 +00:00
// This is stored as the "value" of a bucket key. If the bucket is small enough,
// then its root page can be stored inline in the "value", after the bucket
// header. In the case of inline buckets, the "root" will be 0.
2014-01-14 20:01:02 +00:00
type bucket struct {
2014-05-07 17:23:30 +00:00
root pgid // page id of the bucket's root-level page
sequence uint64 // monotonically incrementing, used by NextSequence()
2014-01-08 15:06:17 +00:00
}
2014-01-26 22:29:06 +00:00
2014-04-07 22:24:51 +00:00
// newBucket returns a new bucket associated with a transaction.
func newBucket ( tx * Tx ) Bucket {
2014-07-24 16:36:09 +00:00
var b = Bucket { tx : tx , FillPercent : DefaultFillPercent }
2014-04-07 22:24:51 +00:00
if tx . writable {
2014-05-23 16:36:23 +00:00
b . buckets = make ( map [ string ] * Bucket )
2014-04-07 22:24:51 +00:00
b . nodes = make ( map [ pgid ] * node )
}
return b
2014-01-30 05:11:46 +00:00
}
2014-04-15 17:56:53 +00:00
// Tx returns the tx of the bucket.
func ( b * Bucket ) Tx ( ) * Tx {
return b . tx
}
// Root returns the root of the bucket.
func ( b * Bucket ) Root ( ) pgid {
return b . root
}
2014-02-23 06:08:30 +00:00
// Writable returns whether the bucket is writable.
func ( b * Bucket ) Writable ( ) bool {
2014-03-09 03:25:37 +00:00
return b . tx . writable
2014-02-23 06:08:30 +00:00
}
// Cursor creates a cursor associated with the bucket.
2014-03-09 00:01:49 +00:00
// The cursor is only valid as long as the transaction is open.
2014-02-23 06:08:30 +00:00
// Do not use a cursor after the transaction is closed.
func ( b * Bucket ) Cursor ( ) * Cursor {
2014-04-02 21:36:53 +00:00
// Update transaction statistics.
b . tx . stats . CursorCount ++
// Allocate and return a cursor.
2014-01-27 15:11:54 +00:00
return & Cursor {
2014-04-07 22:24:51 +00:00
bucket : b ,
stack : make ( [ ] elemRef , 0 ) ,
}
}
// Bucket retrieves a nested bucket by name.
// Returns nil if the bucket does not exist.
func ( b * Bucket ) Bucket ( name [ ] byte ) * Bucket {
2014-05-23 16:36:23 +00:00
if b . buckets != nil {
if child := b . buckets [ string ( name ) ] ; child != nil {
return child
}
2014-04-07 22:24:51 +00:00
}
// Move cursor to key.
c := b . Cursor ( )
k , v , flags := c . seek ( name )
// Return nil if the key doesn't exist or it is not a bucket.
if ! bytes . Equal ( name , k ) || ( flags & bucketLeafFlag ) == 0 {
return nil
}
// Otherwise create a bucket and cache it.
2014-05-09 20:50:55 +00:00
var child = b . openBucket ( v )
2014-05-23 16:36:23 +00:00
if b . buckets != nil {
b . buckets [ string ( name ) ] = child
}
2014-04-07 22:24:51 +00:00
2014-05-09 20:50:55 +00:00
return child
}
// Helper method that re-interprets a sub-bucket value
// from a parent into a Bucket
func ( b * Bucket ) openBucket ( value [ ] byte ) * Bucket {
var child = newBucket ( b . tx )
2014-05-23 16:36:23 +00:00
// If this is a writable transaction then we need to copy the bucket entry.
// Read-only transactions can point directly at the mmap entry.
if b . tx . writable {
child . bucket = & bucket { }
* child . bucket = * ( * bucket ) ( unsafe . Pointer ( & value [ 0 ] ) )
} else {
child . bucket = ( * bucket ) ( unsafe . Pointer ( & value [ 0 ] ) )
}
2014-05-12 17:29:16 +00:00
// Save a reference to the inline page if the bucket is inline.
if child . root == 0 {
child . page = ( * page ) ( unsafe . Pointer ( & value [ bucketHeaderSize ] ) )
}
2014-04-07 22:24:51 +00:00
return & child
}
2014-04-16 03:45:06 +00:00
// CreateBucket creates a new bucket at the given key and returns the new bucket.
2014-04-07 22:24:51 +00:00
// Returns an error if the key already exists, if the bucket name is blank, or if the bucket name is too long.
2014-04-16 03:45:06 +00:00
func ( b * Bucket ) CreateBucket ( key [ ] byte ) ( * Bucket , error ) {
2014-04-07 22:24:51 +00:00
if b . tx . db == nil {
2014-04-16 03:45:06 +00:00
return nil , ErrTxClosed
2014-04-07 22:24:51 +00:00
} else if ! b . tx . writable {
2014-04-16 03:45:06 +00:00
return nil , ErrTxNotWritable
2014-04-07 22:24:51 +00:00
} else if len ( key ) == 0 {
2014-04-16 03:45:06 +00:00
return nil , ErrBucketNameRequired
2014-01-26 22:29:06 +00:00
}
2014-04-07 22:24:51 +00:00
// Move cursor to correct position.
c := b . Cursor ( )
k , _ , flags := c . seek ( key )
// Return an error if there is an existing key.
if bytes . Equal ( key , k ) {
if ( flags & bucketLeafFlag ) != 0 {
2014-04-16 03:45:06 +00:00
return nil , ErrBucketExists
2014-04-07 22:24:51 +00:00
} else {
2014-04-16 03:45:06 +00:00
return nil , ErrIncompatibleValue
2014-04-07 22:24:51 +00:00
}
}
2014-05-05 16:27:02 +00:00
// Create empty, inline bucket.
2014-07-24 16:36:09 +00:00
var bucket = Bucket {
bucket : & bucket { } ,
rootNode : & node { isLeaf : true } ,
FillPercent : DefaultFillPercent ,
}
2014-05-05 16:27:02 +00:00
var value = bucket . write ( )
2014-04-07 22:24:51 +00:00
// Insert into node.
2014-04-29 12:54:01 +00:00
key = cloneBytes ( key )
2014-04-07 22:24:51 +00:00
c . node ( ) . put ( key , key , value , 0 , bucketLeafFlag )
2014-05-07 17:23:30 +00:00
// Since subbuckets are not allowed on inline buckets, we need to
// dereference the inline page, if it exists. This will cause the bucket
// to be treated as a regular, non-inline bucket for the rest of the tx.
b . page = nil
2014-05-05 16:27:02 +00:00
2014-04-16 03:45:06 +00:00
return b . Bucket ( key ) , nil
2014-04-07 22:24:51 +00:00
}
2014-04-16 03:45:06 +00:00
// CreateBucketIfNotExists creates a new bucket if it doesn't already exist and returns a reference to it.
2014-04-07 22:24:51 +00:00
// Returns an error if the bucket name is blank, or if the bucket name is too long.
2014-04-16 03:45:06 +00:00
func ( b * Bucket ) CreateBucketIfNotExists ( key [ ] byte ) ( * Bucket , error ) {
child , err := b . CreateBucket ( key )
if err == ErrBucketExists {
return b . Bucket ( key ) , nil
} else if err != nil {
return nil , err
2014-04-07 22:24:51 +00:00
}
2014-04-16 03:45:06 +00:00
return child , nil
2014-04-07 22:24:51 +00:00
}
// DeleteBucket deletes a bucket at the given key.
// Returns an error if the bucket does not exists, or if the key represents a non-bucket value.
func ( b * Bucket ) DeleteBucket ( key [ ] byte ) error {
if b . tx . db == nil {
return ErrTxClosed
} else if ! b . Writable ( ) {
return ErrTxNotWritable
}
// Move cursor to correct position.
c := b . Cursor ( )
k , _ , flags := c . seek ( key )
// Return an error if bucket doesn't exist or is not a bucket.
if ! bytes . Equal ( key , k ) {
return ErrBucketNotFound
} else if ( flags & bucketLeafFlag ) == 0 {
return ErrIncompatibleValue
}
// Recursively delete all child buckets.
child := b . Bucket ( key )
err := child . ForEach ( func ( k , v [ ] byte ) error {
if v == nil {
if err := child . DeleteBucket ( k ) ; err != nil {
return fmt . Errorf ( "delete bucket: %s" , err )
}
}
return nil
} )
if err != nil {
return err
}
// Remove cached copy.
delete ( b . buckets , string ( key ) )
// Release all bucket pages to freelist.
2014-05-05 16:27:02 +00:00
child . nodes = nil
child . rootNode = nil
child . free ( )
2014-04-07 22:24:51 +00:00
// Delete the node if we have a matching key.
c . node ( ) . del ( key )
return nil
2014-01-26 22:29:06 +00:00
}
2014-02-21 16:20:45 +00:00
2014-02-23 06:08:30 +00:00
// Get retrieves the value for a key in the bucket.
2014-04-07 22:24:51 +00:00
// Returns a nil value if the key does not exist or if the key is a nested bucket.
2015-03-24 17:06:28 +00:00
// The returned value is only valid for the life of the transaction.
2014-02-23 06:08:30 +00:00
func ( b * Bucket ) Get ( key [ ] byte ) [ ] byte {
2014-04-07 22:24:51 +00:00
k , v , flags := b . Cursor ( ) . seek ( key )
// Return nil if this is a bucket.
if ( flags & bucketLeafFlag ) != 0 {
return nil
}
2014-02-23 06:08:30 +00:00
// If our target node isn't the same key as what's passed in then return nil.
if ! bytes . Equal ( key , k ) {
return nil
}
return v
}
// Put sets the value for a key in the bucket.
// If the key exist then its previous value will be overwritten.
// Returns an error if the bucket was created from a read-only transaction, if the key is blank, if the key is too large, or if the value is too large.
func ( b * Bucket ) Put ( key [ ] byte , value [ ] byte ) error {
2014-03-23 18:17:30 +00:00
if b . tx . db == nil {
return ErrTxClosed
} else if ! b . Writable ( ) {
2014-04-07 22:24:51 +00:00
return ErrTxNotWritable
} else if len ( key ) == 0 {
2014-02-23 06:08:30 +00:00
return ErrKeyRequired
} else if len ( key ) > MaxKeySize {
return ErrKeyTooLarge
2014-03-23 05:54:51 +00:00
} else if int64 ( len ( value ) ) > MaxValueSize {
2014-02-23 06:08:30 +00:00
return ErrValueTooLarge
}
// Move cursor to correct position.
c := b . Cursor ( )
2014-04-07 22:24:51 +00:00
k , _ , flags := c . seek ( key )
// Return an error if there is an existing key with a bucket value.
if bytes . Equal ( key , k ) && ( flags & bucketLeafFlag ) != 0 {
return ErrIncompatibleValue
}
2014-02-23 06:08:30 +00:00
2014-04-07 22:24:51 +00:00
// Insert into node.
2014-04-29 12:54:01 +00:00
key = cloneBytes ( key )
2014-04-07 22:24:51 +00:00
c . node ( ) . put ( key , key , value , 0 , 0 )
2014-02-23 06:08:30 +00:00
return nil
}
// Delete removes a key from the bucket.
// If the key does not exist then nothing is done and a nil error is returned.
// Returns an error if the bucket was created from a read-only transaction.
func ( b * Bucket ) Delete ( key [ ] byte ) error {
2014-03-23 18:17:30 +00:00
if b . tx . db == nil {
return ErrTxClosed
} else if ! b . Writable ( ) {
2014-04-07 22:24:51 +00:00
return ErrTxNotWritable
2014-02-23 06:08:30 +00:00
}
// Move cursor to correct position.
c := b . Cursor ( )
2014-04-07 22:24:51 +00:00
_ , _ , flags := c . seek ( key )
// Return an error if there is already existing bucket value.
if ( flags & bucketLeafFlag ) != 0 {
return ErrIncompatibleValue
}
2014-02-23 06:08:30 +00:00
// Delete the node if we have a matching key.
2014-04-07 22:24:51 +00:00
c . node ( ) . del ( key )
2014-02-23 06:08:30 +00:00
return nil
}
// NextSequence returns an autoincrementing integer for the bucket.
2014-06-22 15:33:28 +00:00
func ( b * Bucket ) NextSequence ( ) ( uint64 , error ) {
2014-03-23 18:17:30 +00:00
if b . tx . db == nil {
return 0 , ErrTxClosed
} else if ! b . Writable ( ) {
2014-04-07 22:24:51 +00:00
return 0 , ErrTxNotWritable
2014-02-23 06:08:30 +00:00
}
2015-02-02 15:27:34 +00:00
// Materialize the root node if it hasn't been already so that the
// bucket will be saved during commit.
if b . rootNode == nil {
_ = b . node ( b . root , nil )
}
2014-02-23 06:08:30 +00:00
// Increment and return the sequence.
b . bucket . sequence ++
2014-06-22 15:33:28 +00:00
return b . bucket . sequence , nil
2014-02-23 06:08:30 +00:00
}
// ForEach executes a function for each key/value pair in a bucket.
2014-03-15 15:14:20 +00:00
// If the provided function returns an error then the iteration is stopped and
// the error is returned to the caller.
2014-02-23 06:08:30 +00:00
func ( b * Bucket ) ForEach ( fn func ( k , v [ ] byte ) error ) error {
2014-03-23 18:17:30 +00:00
if b . tx . db == nil {
return ErrTxClosed
}
2014-02-23 06:08:30 +00:00
c := b . Cursor ( )
for k , v := c . First ( ) ; k != nil ; k , v = c . Next ( ) {
if err := fn ( k , v ) ; err != nil {
return err
}
}
return nil
}
2014-02-21 16:20:45 +00:00
// Stat returns stats on a bucket.
2014-04-22 21:11:49 +00:00
func ( b * Bucket ) Stats ( ) BucketStats {
2014-05-09 20:50:55 +00:00
var s , subStats BucketStats
2014-04-22 20:18:09 +00:00
pageSize := b . tx . db . pageSize
2014-05-12 17:29:16 +00:00
s . BucketN += 1
if b . root == 0 {
s . InlineBucketN += 1
}
2014-05-05 16:27:02 +00:00
b . forEachPage ( func ( p * page , depth int ) {
2014-05-12 18:22:45 +00:00
if ( p . flags & leafPageFlag ) != 0 {
2014-05-12 17:29:16 +00:00
s . KeyN += int ( p . count )
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// used totals the used bytes for the page
2014-05-12 19:33:53 +00:00
used := pageHeaderSize
2014-05-14 18:06:31 +00:00
2014-05-12 18:22:45 +00:00
if p . count != 0 {
2014-05-13 17:28:17 +00:00
// If page has any elements, add all element headers.
2014-05-12 19:33:53 +00:00
used += leafPageElementSize * int ( p . count - 1 )
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// Add all element key, value sizes.
// The computation takes advantage of the fact that the position
// of the last element's key/value equals to the total of the sizes
// of all previous elements' keys and values.
// It also includes the last element's header.
2014-05-12 18:22:45 +00:00
lastElement := p . leafPageElement ( p . count - 1 )
used += int ( lastElement . pos + lastElement . ksize + lastElement . vsize )
2014-05-09 18:16:38 +00:00
}
2014-05-14 18:06:31 +00:00
2014-05-12 18:22:45 +00:00
if b . root == 0 {
2014-05-13 17:28:17 +00:00
// For inlined bucket just update the inline stats
2014-05-12 18:22:45 +00:00
s . InlineBucketInuse += used
} else {
2014-05-13 17:28:17 +00:00
// For non-inlined bucket update all the leaf stats
2014-05-12 18:22:45 +00:00
s . LeafPageN ++
s . LeafInuse += used
s . LeafOverflowN += int ( p . overflow )
2014-05-13 17:28:17 +00:00
// Collect stats from sub-buckets.
// Do that by iterating over all element headers
// looking for the ones with the bucketLeafFlag.
2014-05-12 18:22:45 +00:00
for i := uint16 ( 0 ) ; i < p . count ; i ++ {
e := p . leafPageElement ( i )
if ( e . flags & bucketLeafFlag ) != 0 {
2014-05-13 17:28:17 +00:00
// For any bucket element, open the element value
// and recursively call Stats on the contained bucket.
2014-05-12 18:22:45 +00:00
subStats . Add ( b . openBucket ( e . value ( ) ) . Stats ( ) )
}
2014-05-09 20:50:55 +00:00
}
}
2014-02-21 16:20:45 +00:00
} else if ( p . flags & branchPageFlag ) != 0 {
2014-04-22 20:22:18 +00:00
s . BranchPageN ++
2014-04-22 18:22:42 +00:00
lastElement := p . branchPageElement ( p . count - 1 )
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// used totals the used bytes for the page
// Add header and all element headers.
2014-04-22 18:22:42 +00:00
used := pageHeaderSize + ( branchPageElementSize * int ( p . count - 1 ) )
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// Add size of all keys and values.
// Again, use the fact that last element's position equals to
// the total of key, value sizes of all previous elements.
2014-04-22 18:22:42 +00:00
used += int ( lastElement . pos + lastElement . ksize )
2014-04-22 20:18:09 +00:00
s . BranchInuse += used
2014-04-22 20:22:18 +00:00
s . BranchOverflowN += int ( p . overflow )
2014-02-21 16:20:45 +00:00
}
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// Keep track of maximum page depth.
2014-04-22 20:32:09 +00:00
if depth + 1 > s . Depth {
s . Depth = ( depth + 1 )
2014-02-21 16:20:45 +00:00
}
} )
2014-05-14 18:06:31 +00:00
2014-05-13 17:28:17 +00:00
// Alloc stats can be computed from page counts and pageSize.
2014-04-22 20:22:18 +00:00
s . BranchAlloc = ( s . BranchPageN + s . BranchOverflowN ) * pageSize
s . LeafAlloc = ( s . LeafPageN + s . LeafOverflowN ) * pageSize
2014-05-09 20:50:55 +00:00
2014-05-13 17:28:17 +00:00
// Add the max depth of sub-buckets to get total nested depth.
2014-05-09 20:50:55 +00:00
s . Depth += subStats . Depth
2014-05-13 17:28:17 +00:00
// Add the stats for all sub-buckets
2014-05-09 20:50:55 +00:00
s . Add ( subStats )
2014-02-21 16:20:45 +00:00
return s
}
2014-05-05 16:27:02 +00:00
// forEachPage iterates over every page in a bucket, including inline pages.
func ( b * Bucket ) forEachPage ( fn func ( * page , int ) ) {
// If we have an inline page then just use that.
if b . page != nil {
fn ( b . page , 0 )
return
}
// Otherwise traverse the page hierarchy.
b . tx . forEachPage ( b . root , 0 , fn )
}
2014-05-07 16:37:50 +00:00
// forEachPageNode iterates over every page (or node) in a bucket.
// This also includes inline pages.
func ( b * Bucket ) forEachPageNode ( fn func ( * page , * node , int ) ) {
// If we have an inline page or root node then just use that.
if b . page != nil {
fn ( b . page , nil , 0 )
return
}
b . _forEachPageNode ( b . root , 0 , fn )
}
func ( b * Bucket ) _forEachPageNode ( pgid pgid , depth int , fn func ( * page , * node , int ) ) {
var p , n = b . pageNode ( pgid )
// Execute function.
fn ( p , n , depth )
// Recursively loop over children.
if p != nil {
if ( p . flags & branchPageFlag ) != 0 {
for i := 0 ; i < int ( p . count ) ; i ++ {
elem := p . branchPageElement ( uint16 ( i ) )
b . _forEachPageNode ( elem . pgid , depth + 1 , fn )
}
}
} else {
if ! n . isLeaf {
for _ , inode := range n . inodes {
b . _forEachPageNode ( inode . pgid , depth + 1 , fn )
}
}
}
}
2014-04-07 22:24:51 +00:00
// spill writes all the nodes for this bucket to dirty pages.
func ( b * Bucket ) spill ( ) error {
// Spill all child buckets first.
for name , child := range b . buckets {
2014-05-05 16:27:02 +00:00
// If the child bucket is small enough and it has no child buckets then
// write it inline into the parent bucket's page. Otherwise spill it
// like a normal bucket and make the parent value a pointer to the page.
var value [ ] byte
if child . inlineable ( ) {
child . free ( )
value = child . write ( )
} else {
if err := child . spill ( ) ; err != nil {
return err
}
// Update the child bucket header in this bucket.
value = make ( [ ] byte , unsafe . Sizeof ( bucket { } ) )
2014-05-07 18:06:22 +00:00
var bucket = ( * bucket ) ( unsafe . Pointer ( & value [ 0 ] ) )
2014-05-05 16:27:02 +00:00
* bucket = * child . bucket
2014-04-07 22:24:51 +00:00
}
2014-05-05 16:27:02 +00:00
// Skip writing the bucket if there are no materialized nodes.
if child . rootNode == nil {
continue
}
2014-04-07 22:24:51 +00:00
// Update parent node.
2014-05-05 16:27:02 +00:00
var c = b . Cursor ( )
2014-04-07 22:24:51 +00:00
k , _ , flags := c . seek ( [ ] byte ( name ) )
2015-01-30 19:15:49 +00:00
if ! bytes . Equal ( [ ] byte ( name ) , k ) {
panic ( fmt . Sprintf ( "misplaced bucket header: %x -> %x" , [ ] byte ( name ) , k ) )
}
if flags & bucketLeafFlag == 0 {
panic ( fmt . Sprintf ( "unexpected bucket header flag: %x" , flags ) )
}
2014-04-07 22:24:51 +00:00
c . node ( ) . put ( [ ] byte ( name ) , [ ] byte ( name ) , value , 0 , bucketLeafFlag )
}
2014-05-02 19:59:23 +00:00
// Ignore if there's not a materialized root node.
if b . rootNode == nil {
2014-04-07 22:24:51 +00:00
return nil
}
2014-05-02 19:59:23 +00:00
// Spill nodes.
if err := b . rootNode . spill ( ) ; err != nil {
return err
2014-04-07 22:24:51 +00:00
}
2014-05-02 19:59:23 +00:00
b . rootNode = b . rootNode . root ( )
2014-04-07 22:24:51 +00:00
// Update the root node for this bucket.
2015-01-30 19:15:49 +00:00
if b . rootNode . pgid >= b . tx . meta . pgid {
panic ( fmt . Sprintf ( "pgid (%d) above high water mark (%d)" , b . rootNode . pgid , b . tx . meta . pgid ) )
}
2014-05-02 19:59:23 +00:00
b . root = b . rootNode . pgid
2014-04-07 22:24:51 +00:00
return nil
}
2014-05-05 16:27:02 +00:00
// inlineable returns true if a bucket is small enough to be written inline
// and if it contains no subbuckets. Otherwise returns false.
func ( b * Bucket ) inlineable ( ) bool {
var n = b . rootNode
// Bucket must only contain a single leaf node.
if n == nil || ! n . isLeaf {
return false
}
// Bucket is not inlineable if it contains subbuckets or if it goes beyond
// our threshold for inline bucket size.
var size = pageHeaderSize
for _ , inode := range n . inodes {
size += leafPageElementSize + len ( inode . key ) + len ( inode . value )
if inode . flags & bucketLeafFlag != 0 {
return false
} else if size > b . maxInlineBucketSize ( ) {
return false
}
}
return true
}
// Returns the maximum total size of a bucket to make it a candidate for inlining.
func ( b * Bucket ) maxInlineBucketSize ( ) int {
return b . tx . db . pageSize / 4
}
// write allocates and writes a bucket to a byte slice.
func ( b * Bucket ) write ( ) [ ] byte {
// Allocate the appropriate size.
var n = b . rootNode
2014-05-14 18:06:31 +00:00
var value = make ( [ ] byte , bucketHeaderSize + n . size ( ) )
2014-05-05 16:27:02 +00:00
// Write a bucket header.
var bucket = ( * bucket ) ( unsafe . Pointer ( & value [ 0 ] ) )
* bucket = * b . bucket
// Convert byte slice to a fake page and write the root node.
var p = ( * page ) ( unsafe . Pointer ( & value [ bucketHeaderSize ] ) )
n . write ( p )
return value
}
2014-04-07 22:24:51 +00:00
// rebalance attempts to balance all nodes.
func ( b * Bucket ) rebalance ( ) {
for _ , n := range b . nodes {
n . rebalance ( )
}
for _ , child := range b . buckets {
child . rebalance ( )
}
}
// node creates a node from a page and associates it with a given parent.
func ( b * Bucket ) node ( pgid pgid , parent * node ) * node {
_assert ( b . nodes != nil , "nodes map expected" )
2014-06-03 19:21:28 +00:00
2014-04-07 22:24:51 +00:00
// Retrieve node if it's already been created.
if n := b . nodes [ pgid ] ; n != nil {
return n
}
2014-05-05 16:27:02 +00:00
// Otherwise create a node and cache it.
2014-04-07 22:24:51 +00:00
n := & node { bucket : b , parent : parent }
2014-05-02 19:59:23 +00:00
if parent == nil {
b . rootNode = n
} else {
parent . children = append ( parent . children , n )
2014-04-07 22:24:51 +00:00
}
2014-05-05 16:27:02 +00:00
// Use the inline page if this is an inline bucket.
var p = b . page
if p == nil {
p = b . tx . page ( pgid )
}
// Read the page into the node and cache it.
n . read ( p )
2014-04-07 22:24:51 +00:00
b . nodes [ pgid ] = n
// Update statistics.
b . tx . stats . NodeCount ++
return n
}
2014-05-05 16:27:02 +00:00
// free recursively frees all pages in the bucket.
func ( b * Bucket ) free ( ) {
if b . root == 0 {
return
}
var tx = b . tx
2014-05-07 16:37:50 +00:00
b . forEachPageNode ( func ( p * page , n * node , _ int ) {
if p != nil {
2014-07-26 21:11:47 +00:00
tx . db . freelist . free ( tx . meta . txid , p )
2014-05-07 16:37:50 +00:00
} else {
n . free ( )
}
2014-05-05 16:27:02 +00:00
} )
b . root = 0
}
2014-04-07 22:24:51 +00:00
// dereference removes all references to the old mmap.
func ( b * Bucket ) dereference ( ) {
2014-05-08 14:10:14 +00:00
if b . rootNode != nil {
2014-08-22 04:49:58 +00:00
b . rootNode . root ( ) . dereference ( )
2014-04-07 22:24:51 +00:00
}
for _ , child := range b . buckets {
child . dereference ( )
}
}
// pageNode returns the in-memory node, if it exists.
// Otherwise returns the underlying page.
func ( b * Bucket ) pageNode ( id pgid ) ( * page , * node ) {
2014-05-05 16:27:02 +00:00
// Inline buckets have a fake page embedded in their value so treat them
// differently. We'll return the rootNode (if available) or the fake page.
if b . root == 0 {
2015-01-30 19:15:49 +00:00
if id != 0 {
panic ( fmt . Sprintf ( "inline bucket non-zero page access(2): %d != 0" , id ) )
}
2014-05-05 16:27:02 +00:00
if b . rootNode != nil {
return nil , b . rootNode
}
return b . page , nil
}
// Check the node cache for non-inline buckets.
2014-04-07 22:24:51 +00:00
if b . nodes != nil {
if n := b . nodes [ id ] ; n != nil {
return nil , n
}
}
2014-05-05 16:27:02 +00:00
// Finally lookup the page from the transaction if no node is materialized.
2014-04-07 22:24:51 +00:00
return b . tx . page ( id ) , nil
}
2014-04-22 21:11:49 +00:00
// BucketStats records statistics about resources used by a bucket.
type BucketStats struct {
// Page count statistics.
BranchPageN int // number of logical branch pages
BranchOverflowN int // number of physical branch overflow pages
LeafPageN int // number of logical leaf pages
LeafOverflowN int // number of physical leaf overflow pages
// Tree statistics.
KeyN int // number of keys/value pairs
Depth int // number of levels in B+tree
// Page size utilization.
BranchAlloc int // bytes allocated for physical branch pages
BranchInuse int // bytes actually used for branch data
LeafAlloc int // bytes allocated for physical leaf pages
LeafInuse int // bytes actually used for leaf data
2014-05-12 17:29:16 +00:00
// Bucket statistics
BucketN int // total number of buckets including the top bucket
InlineBucketN int // total number on inlined buckets
InlineBucketInuse int // bytes used for inlined buckets (also accounted for in LeafInuse)
2014-02-21 16:20:45 +00:00
}
2014-04-29 12:54:01 +00:00
2014-05-09 20:50:55 +00:00
func ( s * BucketStats ) Add ( other BucketStats ) {
s . BranchPageN += other . BranchPageN
s . BranchOverflowN += other . BranchOverflowN
s . LeafPageN += other . LeafPageN
s . LeafOverflowN += other . LeafOverflowN
2014-05-09 18:16:38 +00:00
s . KeyN += other . KeyN
2014-05-09 20:50:55 +00:00
if s . Depth < other . Depth {
s . Depth = other . Depth
}
s . BranchAlloc += other . BranchAlloc
s . BranchInuse += other . BranchInuse
s . LeafAlloc += other . LeafAlloc
s . LeafInuse += other . LeafInuse
2014-05-12 17:29:16 +00:00
s . BucketN += other . BucketN
s . InlineBucketN += other . InlineBucketN
s . InlineBucketInuse += other . InlineBucketInuse
2014-05-09 20:50:55 +00:00
}
2014-04-29 12:54:01 +00:00
// cloneBytes returns a copy of a given slice.
func cloneBytes ( v [ ] byte ) [ ] byte {
var clone = make ( [ ] byte , len ( v ) )
copy ( clone , v )
return clone
}