|
|
@@ -40,6 +40,9 @@ const (
|
|
|
// default page size for db is set to the OS page size.
|
|
|
var defaultPageSize = os.Getpagesize()
|
|
|
|
|
|
+// The time elapsed between consecutive file locking attempts.
|
|
|
+const flockRetryTimeout = 50 * time.Millisecond
|
|
|
+
|
|
|
// DB represents a collection of buckets persisted to a file on disk.
|
|
|
// All data access is performed through transactions which can be obtained through the DB.
|
|
|
// All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called.
|
|
|
@@ -113,9 +116,11 @@ type DB struct {
|
|
|
opened bool
|
|
|
rwtx *Tx
|
|
|
txs []*Tx
|
|
|
- freelist *freelist
|
|
|
stats Stats
|
|
|
|
|
|
+ freelist *freelist
|
|
|
+ freelistLoad sync.Once
|
|
|
+
|
|
|
pagePool sync.Pool
|
|
|
|
|
|
batchMu sync.Mutex
|
|
|
@@ -154,12 +159,14 @@ func (db *DB) String() string {
|
|
|
// If the file does not exist then it will be created automatically.
|
|
|
// Passing in nil options will cause Bolt to open the database with the default options.
|
|
|
func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
|
|
- var db = &DB{opened: true}
|
|
|
-
|
|
|
+ db := &DB{
|
|
|
+ opened: true,
|
|
|
+ }
|
|
|
// Set default options if no options are provided.
|
|
|
if options == nil {
|
|
|
options = DefaultOptions
|
|
|
}
|
|
|
+ db.NoSync = options.NoSync
|
|
|
db.NoGrowSync = options.NoGrowSync
|
|
|
db.MmapFlags = options.MmapFlags
|
|
|
db.NoFreelistSync = options.NoFreelistSync
|
|
|
@@ -250,20 +257,11 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
|
|
return db, nil
|
|
|
}
|
|
|
|
|
|
- db.freelist = newFreelist()
|
|
|
- noFreeList := db.meta().freelist == pgidNoFreelist
|
|
|
- if noFreeList {
|
|
|
- // Reconstruct free list by scanning the DB.
|
|
|
- db.freelist.readIDs(db.freepages())
|
|
|
- } else {
|
|
|
- // Read free list from freelist page.
|
|
|
- db.freelist.read(db.page(db.meta().freelist))
|
|
|
- }
|
|
|
- db.stats.FreePageN = len(db.freelist.ids)
|
|
|
+ db.loadFreelist()
|
|
|
|
|
|
// Flush freelist when transitioning from no sync to sync so
|
|
|
// NoFreelistSync unaware boltdb can open the db later.
|
|
|
- if !db.NoFreelistSync && noFreeList {
|
|
|
+ if !db.NoFreelistSync && !db.hasSyncedFreelist() {
|
|
|
tx, err := db.Begin(true)
|
|
|
if tx != nil {
|
|
|
err = tx.Commit()
|
|
|
@@ -278,6 +276,27 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
|
|
return db, nil
|
|
|
}
|
|
|
|
|
|
+// loadFreelist reads the freelist if it is synced, or reconstructs it
|
|
|
+// by scanning the DB if it is not synced. It assumes there are no
|
|
|
+// concurrent accesses being made to the freelist.
|
|
|
+func (db *DB) loadFreelist() {
|
|
|
+ db.freelistLoad.Do(func() {
|
|
|
+ db.freelist = newFreelist()
|
|
|
+ if !db.hasSyncedFreelist() {
|
|
|
+ // Reconstruct free list by scanning the DB.
|
|
|
+ db.freelist.readIDs(db.freepages())
|
|
|
+ } else {
|
|
|
+ // Read free list from freelist page.
|
|
|
+ db.freelist.read(db.page(db.meta().freelist))
|
|
|
+ }
|
|
|
+ db.stats.FreePageN = len(db.freelist.ids)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func (db *DB) hasSyncedFreelist() bool {
|
|
|
+ return db.meta().freelist != pgidNoFreelist
|
|
|
+}
|
|
|
+
|
|
|
// mmap opens the underlying memory-mapped file and initializes the meta references.
|
|
|
// minsz is the minimum size that the new mmap can be.
|
|
|
func (db *DB) mmap(minsz int) error {
|
|
|
@@ -683,11 +702,7 @@ func (db *DB) View(fn func(*Tx) error) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if err := t.Rollback(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
+ return t.Rollback()
|
|
|
}
|
|
|
|
|
|
// Batch calls fn as part of a batch. It behaves similar to Update,
|
|
|
@@ -1008,6 +1023,11 @@ type Options struct {
|
|
|
|
|
|
// PageSize overrides the default OS page size.
|
|
|
PageSize int
|
|
|
+
|
|
|
+ // NoSync sets the initial value of DB.NoSync. Normally this can just be
|
|
|
+ // set directly on the DB itself when returned from Open(), but this option
|
|
|
+ // is useful in APIs which expose Options but not the underlying DB.
|
|
|
+ NoSync bool
|
|
|
}
|
|
|
|
|
|
// DefaultOptions represent the options used if nil options are passed into Open().
|