Merge pull request #1865 from hashicorp/f-upgrade-boltdb

Updates BoltDB to v1.2.0 release.
This commit is contained in:
James Phillips 2016-03-22 08:53:04 -07:00
commit 51c3d05c90
12 changed files with 118 additions and 1594 deletions

4
Godeps/Godeps.json generated
View File

@ -28,8 +28,8 @@
}, },
{ {
"ImportPath": "github.com/boltdb/bolt", "ImportPath": "github.com/boltdb/bolt",
"Comment": "v1.1.0-65-gee4a088", "Comment": "v1.2.0",
"Rev": "ee4a0888a9abe7eefe5a0992ca4cb06864839873" "Rev": "c6ba97b89e0454fec9aa92e1d33a4e2c5fc1f631"
}, },
{ {
"ImportPath": "github.com/elazarl/go-bindata-assetfs", "ImportPath": "github.com/elazarl/go-bindata-assetfs",

View File

@ -835,6 +835,10 @@ Below is a list of public, open source projects that use Bolt:
backed by boltdb. backed by boltdb.
* [buckets](https://github.com/joyrexus/buckets) - a bolt wrapper streamlining * [buckets](https://github.com/joyrexus/buckets) - a bolt wrapper streamlining
simple tx and key scans. simple tx and key scans.
* [mbuckets](https://github.com/abhigupta912/mbuckets) - A Bolt wrapper that allows easy operations on multi level (nested) buckets.
* [Request Baskets](https://github.com/darklynx/request-baskets) - A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to [RequestBin](http://requestb.in/) service * [Request Baskets](https://github.com/darklynx/request-baskets) - A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to [RequestBin](http://requestb.in/) service
* [Go Report Card](https://goreportcard.com/) - Go code quality report cards as a (free and open source) service.
* [Boltdb Boilerplate](https://github.com/bobintornado/boltdb-boilerplate) - Boilerplate wrapper around bolt aiming to make simple calls one-liners.
* [lru](https://github.com/crowdriff/lru) - Easy to use Bolt-backed Least-Recently-Used (LRU) read-through cache with chainable remote stores.
If you are using Bolt in a project please send a pull request to add it to the list. If you are using Bolt in a project please send a pull request to add it to the list.

18
vendor/github.com/boltdb/bolt/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,18 @@
version: "{build}"
os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\boltdb\bolt
environment:
GOPATH: c:\gopath
install:
- echo %PATH%
- echo %GOPATH%
- go version
- go env
- go get -v -t ./...
build_script:
- go test -v ./...

9
vendor/github.com/boltdb/bolt/bolt_ppc.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
// +build ppc
package bolt
// maxMapSize represents the largest mmap size supported by Bolt.
const maxMapSize = 0x7FFFFFFF // 2GB
// maxAllocSize is the size used when creating array pointers.
const maxAllocSize = 0xFFFFFFF

9
vendor/github.com/boltdb/bolt/bolt_ppc64.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
// +build ppc64
package bolt
// maxMapSize represents the largest mmap size supported by Bolt.
const maxMapSize = 0xFFFFFFFFFFFF // 256TB
// maxAllocSize is the size used when creating array pointers.
const maxAllocSize = 0x7FFFFFFF

View File

@ -11,7 +11,7 @@ import (
) )
// flock acquires an advisory lock on a file descriptor. // flock acquires an advisory lock on a file descriptor.
func flock(f *os.File, exclusive bool, timeout time.Duration) error { func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
var t time.Time var t time.Time
for { for {
// If we're beyond our timeout then return an error. // If we're beyond our timeout then return an error.
@ -27,7 +27,7 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
} }
// Otherwise attempt to obtain an exclusive lock. // Otherwise attempt to obtain an exclusive lock.
err := syscall.Flock(int(f.Fd()), flag|syscall.LOCK_NB) err := syscall.Flock(int(db.file.Fd()), flag|syscall.LOCK_NB)
if err == nil { if err == nil {
return nil return nil
} else if err != syscall.EWOULDBLOCK { } else if err != syscall.EWOULDBLOCK {
@ -40,8 +40,8 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
} }
// funlock releases an advisory lock on a file descriptor. // funlock releases an advisory lock on a file descriptor.
func funlock(f *os.File) error { func funlock(db *DB) error {
return syscall.Flock(int(f.Fd()), syscall.LOCK_UN) return syscall.Flock(int(db.file.Fd()), syscall.LOCK_UN)
} }
// mmap memory maps a DB's data file. // mmap memory maps a DB's data file.

View File

@ -11,7 +11,7 @@ import (
) )
// flock acquires an advisory lock on a file descriptor. // flock acquires an advisory lock on a file descriptor.
func flock(f *os.File, exclusive bool, timeout time.Duration) error { func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
var t time.Time var t time.Time
for { for {
// If we're beyond our timeout then return an error. // If we're beyond our timeout then return an error.
@ -32,7 +32,7 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
} else { } else {
lock.Type = syscall.F_RDLCK lock.Type = syscall.F_RDLCK
} }
err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &lock) err := syscall.FcntlFlock(db.file.Fd(), syscall.F_SETLK, &lock)
if err == nil { if err == nil {
return nil return nil
} else if err != syscall.EAGAIN { } else if err != syscall.EAGAIN {
@ -45,13 +45,13 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
} }
// funlock releases an advisory lock on a file descriptor. // funlock releases an advisory lock on a file descriptor.
func funlock(f *os.File) error { func funlock(db *DB) error {
var lock syscall.Flock_t var lock syscall.Flock_t
lock.Start = 0 lock.Start = 0
lock.Len = 0 lock.Len = 0
lock.Type = syscall.F_UNLCK lock.Type = syscall.F_UNLCK
lock.Whence = 0 lock.Whence = 0
return syscall.FcntlFlock(uintptr(f.Fd()), syscall.F_SETLK, &lock) return syscall.FcntlFlock(uintptr(db.file.Fd()), syscall.F_SETLK, &lock)
} }
// mmap memory maps a DB's data file. // mmap memory maps a DB's data file.

View File

@ -16,6 +16,8 @@ var (
) )
const ( const (
lockExt = ".lock"
// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
flagLockExclusive = 2 flagLockExclusive = 2
flagLockFailImmediately = 1 flagLockFailImmediately = 1
@ -46,7 +48,16 @@ func fdatasync(db *DB) error {
} }
// flock acquires an advisory lock on a file descriptor. // flock acquires an advisory lock on a file descriptor.
func flock(f *os.File, exclusive bool, timeout time.Duration) error { func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
// Create a separate lock file on windows because a process
// cannot share an exclusive lock on the same file. This is
// needed during Tx.WriteTo().
f, err := os.OpenFile(db.path+lockExt, os.O_CREATE, mode)
if err != nil {
return err
}
db.lockfile = f
var t time.Time var t time.Time
for { for {
// If we're beyond our timeout then return an error. // If we're beyond our timeout then return an error.
@ -62,7 +73,7 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
flag |= flagLockExclusive flag |= flagLockExclusive
} }
err := lockFileEx(syscall.Handle(f.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}) err := lockFileEx(syscall.Handle(db.lockfile.Fd()), flag, 0, 1, 0, &syscall.Overlapped{})
if err == nil { if err == nil {
return nil return nil
} else if err != errLockViolation { } else if err != errLockViolation {
@ -75,8 +86,11 @@ func flock(f *os.File, exclusive bool, timeout time.Duration) error {
} }
// funlock releases an advisory lock on a file descriptor. // funlock releases an advisory lock on a file descriptor.
func funlock(f *os.File) error { func funlock(db *DB) error {
return unlockFileEx(syscall.Handle(f.Fd()), 0, 1, 0, &syscall.Overlapped{}) err := unlockFileEx(syscall.Handle(db.lockfile.Fd()), 0, 1, 0, &syscall.Overlapped{})
db.lockfile.Close()
os.Remove(db.path+lockExt)
return err
} }
// mmap memory maps a DB's data file. // mmap memory maps a DB's data file.

File diff suppressed because it is too large Load Diff

15
vendor/github.com/boltdb/bolt/db.go generated vendored
View File

@ -93,6 +93,7 @@ type DB struct {
path string path string
file *os.File file *os.File
lockfile *os.File // windows only
dataref []byte // mmap'ed readonly, write throws SEGV dataref []byte // mmap'ed readonly, write throws SEGV
data *[maxMapSize]byte data *[maxMapSize]byte
datasz int datasz int
@ -177,7 +178,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
// if !options.ReadOnly. // if !options.ReadOnly.
// The database file is locked using the shared lock (more than one process may // The database file is locked using the shared lock (more than one process may
// hold a lock at the same time) otherwise (options.ReadOnly is set). // hold a lock at the same time) otherwise (options.ReadOnly is set).
if err := flock(db.file, !db.readOnly, options.Timeout); err != nil { if err := flock(db, mode, !db.readOnly, options.Timeout); err != nil {
_ = db.close() _ = db.close()
return nil, err return nil, err
} }
@ -379,6 +380,10 @@ func (db *DB) Close() error {
} }
func (db *DB) close() error { func (db *DB) close() error {
if !db.opened {
return nil
}
db.opened = false db.opened = false
db.freelist = nil db.freelist = nil
@ -397,7 +402,7 @@ func (db *DB) close() error {
// No need to unlock read-only file. // No need to unlock read-only file.
if !db.readOnly { if !db.readOnly {
// Unlock the file. // Unlock the file.
if err := funlock(db.file); err != nil { if err := funlock(db); err != nil {
log.Printf("bolt.Close(): funlock error: %s", err) log.Printf("bolt.Close(): funlock error: %s", err)
} }
} }
@ -824,8 +829,10 @@ func (db *DB) grow(sz int) error {
// Truncate and fsync to ensure file size metadata is flushed. // Truncate and fsync to ensure file size metadata is flushed.
// https://github.com/boltdb/bolt/issues/284 // https://github.com/boltdb/bolt/issues/284
if !db.NoGrowSync && !db.readOnly { if !db.NoGrowSync && !db.readOnly {
if err := db.file.Truncate(int64(sz)); err != nil { if runtime.GOOS != "windows" {
return fmt.Errorf("file resize error: %s", err) if err := db.file.Truncate(int64(sz)); err != nil {
return fmt.Errorf("file resize error: %s", err)
}
} }
if err := db.file.Sync(); err != nil { if err := db.file.Sync(); err != nil {
return fmt.Errorf("file sync error: %s", err) return fmt.Errorf("file sync error: %s", err)

View File

@ -463,43 +463,6 @@ func (n *node) rebalance() {
target = n.prevSibling() target = n.prevSibling()
} }
// If target node has extra nodes then just move one over.
if target.numChildren() > target.minKeys() {
if useNextSibling {
// Reparent and move node.
if child, ok := n.bucket.nodes[target.inodes[0].pgid]; ok {
child.parent.removeChild(child)
child.parent = n
child.parent.children = append(child.parent.children, child)
}
n.inodes = append(n.inodes, target.inodes[0])
target.inodes = target.inodes[1:]
// Update target key on parent.
target.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0)
target.key = target.inodes[0].key
_assert(len(target.key) > 0, "rebalance(1): zero-length node key")
} else {
// Reparent and move node.
if child, ok := n.bucket.nodes[target.inodes[len(target.inodes)-1].pgid]; ok {
child.parent.removeChild(child)
child.parent = n
child.parent.children = append(child.parent.children, child)
}
n.inodes = append(n.inodes, inode{})
copy(n.inodes[1:], n.inodes)
n.inodes[0] = target.inodes[len(target.inodes)-1]
target.inodes = target.inodes[:len(target.inodes)-1]
}
// Update parent key for node.
n.parent.put(n.key, n.inodes[0].key, nil, n.pgid, 0)
n.key = n.inodes[0].key
_assert(len(n.key) > 0, "rebalance(2): zero-length node key")
return
}
// If both this node and the target node are too small then merge them. // If both this node and the target node are too small then merge them.
if useNextSibling { if useNextSibling {
// Reparent all child nodes being moved. // Reparent all child nodes being moved.

46
vendor/github.com/boltdb/bolt/tx.go generated vendored
View File

@ -5,6 +5,7 @@ import (
"io" "io"
"os" "os"
"sort" "sort"
"strings"
"time" "time"
"unsafe" "unsafe"
) )
@ -202,8 +203,17 @@ func (tx *Tx) Commit() error {
// If strict mode is enabled then perform a consistency check. // If strict mode is enabled then perform a consistency check.
// Only the first consistency error is reported in the panic. // Only the first consistency error is reported in the panic.
if tx.db.StrictMode { if tx.db.StrictMode {
if err, ok := <-tx.Check(); ok { ch := tx.Check()
panic("check fail: " + err.Error()) var errs []string
for {
err, ok := <-ch
if !ok {
break
}
errs = append(errs, err.Error())
}
if len(errs) > 0 {
panic("check fail: " + strings.Join(errs, "\n"))
} }
} }
@ -297,12 +307,34 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) {
} }
defer func() { _ = f.Close() }() defer func() { _ = f.Close() }()
// Copy the meta pages. // Generate a meta page. We use the same page data for both meta pages.
tx.db.metalock.Lock() buf := make([]byte, tx.db.pageSize)
n, err = io.CopyN(w, f, int64(tx.db.pageSize*2)) page := (*page)(unsafe.Pointer(&buf[0]))
tx.db.metalock.Unlock() page.flags = metaPageFlag
*page.meta() = *tx.meta
// Write meta 0.
page.id = 0
page.meta().checksum = page.meta().sum64()
nn, err := w.Write(buf)
n += int64(nn)
if err != nil { if err != nil {
return n, fmt.Errorf("meta copy: %s", err) return n, fmt.Errorf("meta 0 copy: %s", err)
}
// Write meta 1 with a lower transaction id.
page.id = 1
page.meta().txid -= 1
page.meta().checksum = page.meta().sum64()
nn, err = w.Write(buf)
n += int64(nn)
if err != nil {
return n, fmt.Errorf("meta 1 copy: %s", err)
}
// Move past the meta pages in the file.
if _, err := f.Seek(int64(tx.db.pageSize*2), os.SEEK_SET); err != nil {
return n, fmt.Errorf("seek: %s", err)
} }
// Copy data pages. // Copy data pages.