mirror of https://github.com/etcd-io/bbolt.git
add PreLoadFreelist to support loading free pages in readonly mode
Signed-off-by: Benjamin Wang <wachao@vmware.com>pull/381/head
parent
51c763c4e9
commit
6bc57389f0
|
@ -195,7 +195,10 @@ func (cmd *CheckCommand) Run(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open database.
|
// Open database.
|
||||||
db, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true})
|
db, err := bolt.Open(path, 0666, &bolt.Options{
|
||||||
|
ReadOnly: true,
|
||||||
|
PreLoadFreelist: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -644,7 +647,10 @@ func (cmd *PagesCommand) Run(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open database.
|
// Open database.
|
||||||
db, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true})
|
db, err := bolt.Open(path, 0666, &bolt.Options{
|
||||||
|
ReadOnly: true,
|
||||||
|
PreLoadFreelist: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,6 +255,37 @@ func TestGetCommand_Run(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the "pages" command neither panic, nor change the db file.
|
||||||
|
func TestPagesCommand_Run(t *testing.T) {
|
||||||
|
db := btesting.MustCreateDB(t)
|
||||||
|
|
||||||
|
err := db.Update(func(tx *bolt.Tx) error {
|
||||||
|
for _, name := range []string{"foo", "bar"} {
|
||||||
|
b, err := tx.CreateBucket([]byte(name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
key := fmt.Sprintf("%s-%d", name, i)
|
||||||
|
val := fmt.Sprintf("val-%s-%d", name, i)
|
||||||
|
if err := b.Put([]byte(key), []byte(val)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
db.Close()
|
||||||
|
|
||||||
|
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
|
||||||
|
|
||||||
|
// Run the command.
|
||||||
|
m := NewMain()
|
||||||
|
err = m.Run("pages", db.Path())
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Main represents a test wrapper for main.Main that records output.
|
// Main represents a test wrapper for main.Main that records output.
|
||||||
type Main struct {
|
type Main struct {
|
||||||
*main.Main
|
*main.Main
|
||||||
|
|
20
db.go
20
db.go
|
@ -95,6 +95,11 @@ type DB struct {
|
||||||
// https://github.com/boltdb/bolt/issues/284
|
// https://github.com/boltdb/bolt/issues/284
|
||||||
NoGrowSync bool
|
NoGrowSync bool
|
||||||
|
|
||||||
|
// When `true`, bbolt will always load the free pages when opening the DB.
|
||||||
|
// When opening db in write mode, this flag will always automatically
|
||||||
|
// set to `true`.
|
||||||
|
PreLoadFreelist bool
|
||||||
|
|
||||||
// If you want to read the entire database fast, you can set MmapFlag to
|
// If you want to read the entire database fast, you can set MmapFlag to
|
||||||
// syscall.MAP_POPULATE on Linux 2.6.23+ for sequential read-ahead.
|
// syscall.MAP_POPULATE on Linux 2.6.23+ for sequential read-ahead.
|
||||||
MmapFlags int
|
MmapFlags int
|
||||||
|
@ -196,6 +201,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
||||||
db.NoGrowSync = options.NoGrowSync
|
db.NoGrowSync = options.NoGrowSync
|
||||||
db.MmapFlags = options.MmapFlags
|
db.MmapFlags = options.MmapFlags
|
||||||
db.NoFreelistSync = options.NoFreelistSync
|
db.NoFreelistSync = options.NoFreelistSync
|
||||||
|
db.PreLoadFreelist = options.PreLoadFreelist
|
||||||
db.FreelistType = options.FreelistType
|
db.FreelistType = options.FreelistType
|
||||||
db.Mlock = options.Mlock
|
db.Mlock = options.Mlock
|
||||||
|
|
||||||
|
@ -208,6 +214,9 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
||||||
if options.ReadOnly {
|
if options.ReadOnly {
|
||||||
flag = os.O_RDONLY
|
flag = os.O_RDONLY
|
||||||
db.readOnly = true
|
db.readOnly = true
|
||||||
|
} else {
|
||||||
|
// always load free pages in write mode
|
||||||
|
db.PreLoadFreelist = true
|
||||||
}
|
}
|
||||||
|
|
||||||
db.openFile = options.OpenFile
|
db.openFile = options.OpenFile
|
||||||
|
@ -277,12 +286,14 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if db.PreLoadFreelist {
|
||||||
|
db.loadFreelist()
|
||||||
|
}
|
||||||
|
|
||||||
if db.readOnly {
|
if db.readOnly {
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
db.loadFreelist()
|
|
||||||
|
|
||||||
// Flush freelist when transitioning from no sync to sync so
|
// Flush freelist when transitioning from no sync to sync so
|
||||||
// NoFreelistSync unaware boltdb can open the db later.
|
// NoFreelistSync unaware boltdb can open the db later.
|
||||||
if !db.NoFreelistSync && !db.hasSyncedFreelist() {
|
if !db.NoFreelistSync && !db.hasSyncedFreelist() {
|
||||||
|
@ -1163,6 +1174,11 @@ type Options struct {
|
||||||
// under normal operation, but requires a full database re-sync during recovery.
|
// under normal operation, but requires a full database re-sync during recovery.
|
||||||
NoFreelistSync bool
|
NoFreelistSync bool
|
||||||
|
|
||||||
|
// PreLoadFreelist sets whether to load the free pages when opening
|
||||||
|
// the db file. Note when opening db in write mode, bbolt will always
|
||||||
|
// load the free pages.
|
||||||
|
PreLoadFreelist bool
|
||||||
|
|
||||||
// FreelistType sets the backend freelist type. There are two options. Array which is simple but endures
|
// FreelistType sets the backend freelist type. There are two options. Array which is simple but endures
|
||||||
// dramatic performance degradation if database is large and framentation in freelist is common.
|
// dramatic performance degradation if database is large and framentation in freelist is common.
|
||||||
// The alternative one is using hashmap, it is faster in almost all circumstances
|
// The alternative one is using hashmap, it is faster in almost all circumstances
|
||||||
|
|
3
tx.go
3
tx.go
|
@ -652,6 +652,9 @@ func (tx *Tx) Page(id int) (*PageInfo, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force loading free list if opened in ReadOnly mode.
|
||||||
|
tx.db.loadFreelist()
|
||||||
|
|
||||||
// Build the page info.
|
// Build the page info.
|
||||||
p := tx.db.page(pgid(id))
|
p := tx.db.page(pgid(id))
|
||||||
info := &PageInfo{
|
info := &PageInfo{
|
||||||
|
|
Loading…
Reference in New Issue