Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

Commit

Permalink
lazily read freelist on write transaction and check
Browse files Browse the repository at this point in the history
  • Loading branch information
funny-falcon committed Dec 21, 2016
1 parent 7656291 commit e40ad77
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 13 deletions.
37 changes: 25 additions & 12 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
)
Expand Down Expand Up @@ -115,10 +116,11 @@ type DB struct {
batchMu sync.Mutex
batch *batch

rwlock sync.Mutex // Allows only one writer at a time.
metalock sync.Mutex // Protects meta page access.
mmaplock sync.RWMutex // Protects mmap access during remapping.
statlock sync.RWMutex // Protects stats access.
rwlock sync.Mutex // Allows only one writer at a time.
metalock sync.Mutex // Protects meta page access.
mmaplock sync.RWMutex // Protects mmap access during remapping.
statlock sync.RWMutex // Protects stats access.
freelistonce sync.Once // Protects reading freelist from file.

ops struct {
writeAt func(b []byte, off int64) (n int, err error)
Expand Down Expand Up @@ -232,12 +234,6 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
return nil, err
}

if !db.readOnly {
// Read in the freelist.
db.freelist = newFreelist()
db.freelist.read(db.page(db.meta().freelist))
}

// Mark the database as opened and return.
return db, nil
}
Expand Down Expand Up @@ -441,6 +437,19 @@ func (db *DB) close() error {
return nil
}

func (db *DB) ensureFreelist() *freelist {
flp := (*unsafe.Pointer)(unsafe.Pointer(&db.freelist))
if fl := atomic.LoadPointer(flp); fl != nil {
return (*freelist)(fl)
}
db.freelistonce.Do(func() {
fl := newFreelist()
fl.read(db.page(db.meta().freelist))
atomic.StorePointer(flp, unsafe.Pointer(fl))
})
return db.freelist
}

// Begin starts a new transaction.
// Multiple read-only transactions can be used concurrently but only one
// write transaction can be used at a time. Starting multiple write transactions
Expand Down Expand Up @@ -516,11 +525,11 @@ func (db *DB) beginRWTx() (*Tx, error) {
// Once we have the writer lock then we can lock the meta pages so that
// we can set up the transaction.
db.metalock.Lock()
defer db.metalock.Unlock()

// Exit if the database is not open yet.
if !db.opened {
db.rwlock.Unlock()
db.metalock.Unlock()
return nil, ErrDatabaseNotOpen
}

Expand All @@ -536,10 +545,14 @@ func (db *DB) beginRWTx() (*Tx, error) {
minid = t.meta.txid
}
}

// Release meta here cause ensureFreelist can take a long time
db.metalock.Unlock()

db.ensureFreelist()
if minid > 0 {
db.freelist.release(minid - 1)
}

return t, nil
}

Expand Down
2 changes: 1 addition & 1 deletion tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ func (tx *Tx) Check() <-chan error {
func (tx *Tx) check(ch chan error) {
// Check if any pages are double freed.
freed := make(map[pgid]bool)
for _, id := range tx.db.freelist.all() {
for _, id := range tx.db.ensureFreelist().all() {
if freed[id] {
ch <- fmt.Errorf("page %d: already freed", id)
}
Expand Down

0 comments on commit e40ad77

Please sign in to comment.