-
Notifications
You must be signed in to change notification settings - Fork 628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tx: load freelist on Check() #49
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,9 +116,12 @@ type DB struct { | |
opened bool | ||
rwtx *Tx | ||
txs []*Tx | ||
freelist *freelist | ||
stats Stats | ||
|
||
freelist *freelist | ||
freelistLoad sync.Once | ||
freelistReady chan struct{} // closed once freelist is loaded | ||
|
||
pagePool sync.Pool | ||
|
||
batchMu sync.Mutex | ||
|
@@ -157,8 +160,10 @@ 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, | ||
freelistReady: make(chan struct{}), | ||
} | ||
// Set default options if no options are provided. | ||
if options == nil { | ||
options = DefaultOptions | ||
|
@@ -254,20 +259,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) | ||
hasFreeList := db.readFreelist() | ||
|
||
// 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 && !hasFreeList { | ||
tx, err := db.Begin(true) | ||
if tx != nil { | ||
err = tx.Commit() | ||
|
@@ -282,6 +278,26 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) { | |
return db, nil | ||
} | ||
|
||
// readFreelist is called on Open and tx.Check. It assumes there are no | ||
// concurrenct accesses are being made to the freelist. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "concurrent accesses being made to the freelist." |
||
func (db *DB) readFreelist() bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found the return value of this function a bit tricky to reason about. It might be cleaner to not return bool and fully separate the concerns of loading the freelist in
And we could even add a |
||
hasFreeList := db.meta().freelist != pgidNoFreelist | ||
db.freelistLoad.Do(func() { | ||
db.freelist = newFreelist() | ||
if !hasFreeList { | ||
// 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) | ||
close(db.freelistReady) | ||
}) | ||
<-db.freelistReady | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conveniently, it looks like we don't need the Go playground sanity check: https://play.golang.org/p/ZJEX1V-IbY |
||
return hasFreeList | ||
} | ||
|
||
// 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 { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rename this to
loadFreelist
? That might more strongly suggest what side effects it has.Might also be worth summarizing purpose of the function in a bit more detail, e.g:
"loadFreelist reads the freelist if it is synced, or reconstructs it by scanning the DB if it is not synced. This must be called before any operations that require on the freelist are executed...."