mirror of https://github.com/tidwall/buntdb.git
more comments
This commit is contained in:
parent
813541b8cb
commit
301cfc2ead
55
buntdb.go
55
buntdb.go
|
@ -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 {
|
||||||
|
// flush when buffer is over 4MB
|
||||||
err = w.Flush()
|
err = w.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
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
|
||||||
|
|
Loading…
Reference in New Issue