more comments

This commit is contained in:
Josh Baker 2016-09-03 12:25:30 -07:00
parent 813541b8cb
commit 301cfc2ead
1 changed files with 48 additions and 21 deletions

View File

@ -131,24 +131,30 @@ const btreeDegrees = 64
// If the file does not exist then it will be created automatically. // If the file does not exist then it will be created automatically.
func Open(path string) (*DB, error) { func Open(path string) (*DB, error) {
db := &DB{} db := &DB{}
// initialize trees and indexes
db.keys = btree.New(btreeDegrees, nil) db.keys = btree.New(btreeDegrees, nil)
db.exps = btree.New(btreeDegrees, &exctx{db}) db.exps = btree.New(btreeDegrees, &exctx{db})
db.idxs = make(map[string]*index) db.idxs = make(map[string]*index)
// initialize reusable blank buffer
db.buf = &bytes.Buffer{} db.buf = &bytes.Buffer{}
// initialize default configuration
db.config = Config{ db.config = Config{
SyncPolicy: EverySecond, SyncPolicy: EverySecond,
AutoShrinkPercentage: 100, AutoShrinkPercentage: 100,
AutoShrinkMinSize: 32 * 1024 * 1024, AutoShrinkMinSize: 32 * 1024 * 1024,
} }
// turn off persistence for pure in-memory
db.persist = path != ":memory:" db.persist = path != ":memory:"
if db.persist { if db.persist {
var err error var err error
// Hardcoding 0666 as the default mode. // hardcoding 0666 as the default mode.
db.file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666) db.file, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0666)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// load the database from disk
if err := db.load(); err != nil { if err := db.load(); err != nil {
// close on error, ignore close error
_ = db.file.Close() _ = db.file.Close()
return nil, err return nil, err
} }
@ -180,28 +186,32 @@ func (db *DB) Close() error {
} }
// Save writes a snapshot of the database to a writer. This operation blocks all // Save writes a snapshot of the database to a writer. This operation blocks all
// writes, but not reads. // writes, but not reads. This can be used for snapshots and backups for pure
// in-memory databases using the ":memory:". Database that persist to disk
// can be snapshotted by simply copying the database file.
func (db *DB) Save(wr io.Writer) error { func (db *DB) Save(wr io.Writer) error {
var err error var err error
db.mu.RLock() db.mu.RLock()
defer db.mu.RUnlock() defer db.mu.RUnlock()
// use a buffered writer and flush every 4MB
w := bufio.NewWriter(wr) w := bufio.NewWriter(wr)
db.keys.Ascend( // iterated through every item in the database and write to the buffer
func(item btree.Item) bool { db.keys.Ascend(func(item btree.Item) bool {
dbi := item.(*dbItem) dbi := item.(*dbItem)
dbi.writeSetTo(w) dbi.writeSetTo(w)
if w.Buffered() > 1024*20 { // 20MB buffer if w.Buffered() > 1024*1024*4 {
err = w.Flush() // flush when buffer is over 4MB
if err != nil { err = w.Flush()
return false if err != nil {
} return false
} }
return true }
}, return true
) })
if err != nil { if err != nil {
return err return err
} }
// one final flush
err = w.Flush() err = w.Flush()
if err != nil { if err != nil {
return err return err
@ -216,6 +226,7 @@ func (db *DB) Load(rd io.Reader) error {
db.mu.Lock() db.mu.Lock()
defer db.mu.Unlock() defer db.mu.Unlock()
if db.persist { if db.persist {
// cannot load into databases that persist to disk
return ErrPersistenceActive return ErrPersistenceActive
} }
return db.readLoad(rd, time.Now()) return db.readLoad(rd, time.Now())
@ -234,7 +245,9 @@ type index struct {
} }
// clearCopy creates a copy of the index, but with an empty dataset. // clearCopy creates a copy of the index, but with an empty dataset.
// This is used with the DeleteAll command.
func (idx *index) clearCopy() *index { func (idx *index) clearCopy() *index {
// copy the index meta information
nidx := &index{ nidx := &index{
name: idx.name, name: idx.name,
pattern: idx.pattern, pattern: idx.pattern,
@ -242,6 +255,7 @@ func (idx *index) clearCopy() *index {
less: idx.less, less: idx.less,
rect: idx.rect, rect: idx.rect,
} }
// initialize with empty trees
if nidx.less != nil { if nidx.less != nil {
nidx.btr = btree.New(btreeDegrees, nidx) nidx.btr = btree.New(btreeDegrees, nidx)
} }
@ -309,10 +323,7 @@ func (db *DB) ReplaceSpatialIndex(name, pattern string,
} }
// createIndex is called by CreateIndex() and CreateSpatialIndex() // createIndex is called by CreateIndex() and CreateSpatialIndex()
func (db *DB) createIndex( func (db *DB) createIndex(replace bool, name string, pattern string,
replace bool,
name string,
pattern string,
lessers []func(a, b string) bool, lessers []func(a, b string) bool,
rect func(item string) (min, max []float64), rect func(item string) (min, max []float64),
) error { ) error {
@ -322,18 +333,26 @@ func (db *DB) createIndex(
return ErrDatabaseClosed return ErrDatabaseClosed
} }
if name == "" { if name == "" {
// cannot create an index without a name.
// an empty name index is designated for the main "keys" tree.
return ErrIndexExists return ErrIndexExists
} }
// check if an index with that name already exists.
if _, ok := db.idxs[name]; ok { if _, ok := db.idxs[name]; ok {
if replace { if replace {
// the "replace" param is specified, simply delete the old index.
delete(db.idxs, name) delete(db.idxs, name)
} else { } else {
// index with name already exists. error.
return ErrIndexExists return ErrIndexExists
} }
} }
// genreate a less function
var less func(a, b string) bool var less func(a, b string) bool
switch len(lessers) { switch len(lessers) {
default: default:
// multiple less functions specified.
// create a compound less function.
less = func(a, b string) bool { less = func(a, b string) bool {
for i := 0; i < len(lessers)-1; i++ { for i := 0; i < len(lessers)-1; i++ {
if lessers[i](a, b) { if lessers[i](a, b) {
@ -346,10 +365,11 @@ func (db *DB) createIndex(
return lessers[len(lessers)-1](a, b) return lessers[len(lessers)-1](a, b)
} }
case 0: case 0:
less = func(a, b string) bool { return false } // no less function
case 1: case 1:
less = lessers[0] less = lessers[0]
} }
// intialize new index
idx := &index{ idx := &index{
name: name, name: name,
pattern: pattern, pattern: pattern,
@ -357,15 +377,18 @@ func (db *DB) createIndex(
rect: rect, rect: rect,
db: db, db: db,
} }
// initialize trees
if less != nil { if less != nil {
idx.btr = btree.New(btreeDegrees, idx) idx.btr = btree.New(btreeDegrees, idx)
} }
if rect != nil { if rect != nil {
idx.rtr = rtree.New(idx) idx.rtr = rtree.New(idx)
} }
// iterate through all keys and fill the index
db.keys.Ascend(func(item btree.Item) bool { db.keys.Ascend(func(item btree.Item) bool {
dbi := item.(*dbItem) dbi := item.(*dbItem)
if !match.Match(dbi.key, idx.pattern) { if idx.pattern != "*" && !match.Match(dbi.key, idx.pattern) {
// does not match the pattern, conintue
return true return true
} }
if less != nil { if less != nil {
@ -376,6 +399,7 @@ func (db *DB) createIndex(
} }
return true return true
}) })
// save the index
db.idxs[name] = idx db.idxs[name] = idx
return nil return nil
} }
@ -388,11 +412,14 @@ func (db *DB) DropIndex(name string) error {
return ErrDatabaseClosed return ErrDatabaseClosed
} }
if name == "" { if name == "" {
// cannot drop the default "keys" index
return ErrInvalidOperation return ErrInvalidOperation
} }
if _, ok := db.idxs[name]; !ok { if _, ok := db.idxs[name]; !ok {
return ErrNotFound return ErrNotFound
} }
// delete from the map.
// this is all that is needed to delete an index.
delete(db.idxs, name) delete(db.idxs, name)
return nil return nil
} }
@ -963,7 +990,7 @@ type txWriteContext struct {
itercount int // stack of iterators itercount int // stack of iterators
} }
// DeleteAll deletes all items from the database. // ClearAll deletes all items from the database.
func (tx *Tx) DeleteAll() error { func (tx *Tx) DeleteAll() error {
if tx.db == nil { if tx.db == nil {
return ErrTxClosed return ErrTxClosed