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 Aug 19, 2015
1 parent fb518cf commit 03fb180
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 18 deletions.
47 changes: 30 additions & 17 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"runtime/debug"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
)
Expand Down Expand Up @@ -96,10 +97,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 @@ -196,12 +198,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 @@ -397,6 +393,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 @@ -455,7 +464,7 @@ func (db *DB) beginTx() (*Tx, error) {
return t, nil
}

func (db *DB) beginRWTx() (*Tx, error) {
func (db *DB) beginRWTx() (t *Tx, err error) {
// If the database was opened with Options.ReadOnly, return an error.
if db.readOnly {
return nil, ErrDatabaseReadOnly
Expand All @@ -465,10 +474,18 @@ func (db *DB) beginRWTx() (*Tx, error) {
// This enforces only one writer transaction at a time.
db.rwlock.Lock()

var minid txid = 0xFFFFFFFFFFFFFFFF

// 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()
defer func() {
db.metalock.Unlock()
db.ensureFreelist()
if err == nil && minid > 0 {
db.freelist.release(minid - 1)
}
}()

// Exit if the database is not open yet.
if !db.opened {
Expand All @@ -477,20 +494,16 @@ func (db *DB) beginRWTx() (*Tx, error) {
}

// Create a transaction associated with the database.
t := &Tx{writable: true}
t = &Tx{writable: true}
t.init(db)
db.rwtx = t

// Free any pages associated with closed read-only transactions.
var minid txid = 0xFFFFFFFFFFFFFFFF
for _, t := range db.txs {
if t.meta.txid < minid {
minid = t.meta.txid
}
}
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 @@ -326,7 +326,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 03fb180

Please sign in to comment.