Skip to content
Snippets Groups Projects
Commit 50381055 authored by Wolfgang Welz's avatar Wolfgang Welz
Browse files

Improve error handling in BadgerDB singleton

parent 8f198da2
No related branches found
No related tags found
No related merge requests found
...@@ -6,48 +6,63 @@ import ( ...@@ -6,48 +6,63 @@ import (
"github.com/dgraph-io/badger" "github.com/dgraph-io/badger"
"github.com/dgraph-io/badger/options" "github.com/dgraph-io/badger/options"
"github.com/pkg/errors"
) )
var instance *badger.DB var instance *badger.DB
var once sync.Once
var openLock sync.Mutex // Returns whether the given file or directory exists.
func exists(path string) (bool, error) {
func GetBadgerInstance() (result *badger.DB, err error) { _, err := os.Stat(path)
openLock.Lock() if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
if instance == nil { func checkDir(dir string) error {
directory := *DIRECTORY.Value exists, err := exists(dir)
if err != nil {
return err
}
if _, osErr := os.Stat(directory); os.IsNotExist(osErr) { if !exists {
if osErr := os.Mkdir(directory, 0700); osErr != nil { return os.Mkdir(dir, 0700)
err = osErr }
return nil
}
return func createDB() (*badger.DB, error) {
} directory := *DIRECTORY.Value
} else if osErr != nil { if err := checkDir(directory); err != nil {
err = osErr return nil, errors.Wrap(err, "Could not check directory")
}
return opts := badger.DefaultOptions(directory)
} opts.Logger = &logger{}
opts.Truncate = true
opts.TableLoadingMode = options.MemoryMap
opts := badger.DefaultOptions(directory) db, err := badger.Open(opts)
opts.Logger = &logger{} if err != nil {
opts.Truncate = true return nil, errors.Wrap(err, "Could not open new DB")
opts.TableLoadingMode = options.MemoryMap }
db, badgerErr := badger.Open(opts) return db, nil
if badgerErr != nil { }
err = badgerErr
return func GetBadgerInstance() *badger.DB {
once.Do(func() {
db, err := createDB()
if err != nil {
// errors should cause a panic to avoid singleton deadlocks
panic(err)
} }
instance = db instance = db
} })
return instance
openLock.Unlock()
result = instance
return
} }
...@@ -6,12 +6,10 @@ import ( ...@@ -6,12 +6,10 @@ import (
"github.com/dgraph-io/badger" "github.com/dgraph-io/badger"
) )
var databasesByName = make(map[string]*databaseImpl) var dbMap = make(map[string]*prefixDb)
var getLock sync.Mutex var mu sync.Mutex
var ErrKeyNotFound = badger.ErrKeyNotFound type prefixDb struct {
type databaseImpl struct {
db *badger.DB db *badger.DB
name string name string
prefix []byte prefix []byte
...@@ -19,30 +17,32 @@ type databaseImpl struct { ...@@ -19,30 +17,32 @@ type databaseImpl struct {
} }
func Get(name string) (Database, error) { func Get(name string) (Database, error) {
getLock.Lock() // avoid locking if it's a clean hit
defer getLock.Unlock() if db, exists := dbMap[name]; exists {
return db, nil
if database, exists := databasesByName[name]; exists {
return database, nil
} }
badgerInstance, err := GetBadgerInstance() mu.Lock()
if err != nil { defer mu.Unlock()
return nil, err
// needs to be re-checked after locking
if db, exists := dbMap[name]; exists {
return db, nil
} }
database := &databaseImpl{ badger := GetBadgerInstance()
db: badgerInstance, db := &prefixDb{
db: badger,
name: name, name: name,
prefix: []byte(name + "_"), prefix: []byte(name + "_"),
} }
databasesByName[name] = database dbMap[name] = db
return databasesByName[name], nil return db, nil
} }
func (this *databaseImpl) Set(key []byte, value []byte) error { func (this *prefixDb) Set(key []byte, value []byte) error {
if err := this.db.Update(func(txn *badger.Txn) error { return txn.Set(append(this.prefix, key...), value) }); err != nil { if err := this.db.Update(func(txn *badger.Txn) error { return txn.Set(append(this.prefix, key...), value) }); err != nil {
return err return err
} }
...@@ -50,7 +50,7 @@ func (this *databaseImpl) Set(key []byte, value []byte) error { ...@@ -50,7 +50,7 @@ func (this *databaseImpl) Set(key []byte, value []byte) error {
return nil return nil
} }
func (this *databaseImpl) Contains(key []byte) (bool, error) { func (this *prefixDb) Contains(key []byte) (bool, error) {
err := this.db.View(func(txn *badger.Txn) error { err := this.db.View(func(txn *badger.Txn) error {
_, err := txn.Get(append(this.prefix, key...)) _, err := txn.Get(append(this.prefix, key...))
if err != nil { if err != nil {
...@@ -60,14 +60,14 @@ func (this *databaseImpl) Contains(key []byte) (bool, error) { ...@@ -60,14 +60,14 @@ func (this *databaseImpl) Contains(key []byte) (bool, error) {
return nil return nil
}) })
if err == ErrKeyNotFound { if err == badger.ErrKeyNotFound {
return false, nil return false, nil
} else { } else {
return err == nil, err return err == nil, err
} }
} }
func (this *databaseImpl) Get(key []byte) ([]byte, error) { func (this *prefixDb) Get(key []byte) ([]byte, error) {
var result []byte = nil var result []byte = nil
err := this.db.View(func(txn *badger.Txn) error { err := this.db.View(func(txn *badger.Txn) error {
...@@ -86,7 +86,7 @@ func (this *databaseImpl) Get(key []byte) ([]byte, error) { ...@@ -86,7 +86,7 @@ func (this *databaseImpl) Get(key []byte) ([]byte, error) {
return result, err return result, err
} }
func (this *databaseImpl) Delete(key []byte) error { func (this *prefixDb) Delete(key []byte) error {
err := this.db.Update(func(txn *badger.Txn) error { err := this.db.Update(func(txn *badger.Txn) error {
err := txn.Delete(append(this.prefix, key...)) err := txn.Delete(append(this.prefix, key...))
return err return err
...@@ -94,10 +94,10 @@ func (this *databaseImpl) Delete(key []byte) error { ...@@ -94,10 +94,10 @@ func (this *databaseImpl) Delete(key []byte) error {
return err return err
} }
func (this *databaseImpl) ForEach(consumer func([]byte, []byte)) error { func (this *prefixDb) ForEach(consumer func([]byte, []byte)) error {
err := this.db.View(func(txn *badger.Txn) error { err := this.db.View(func(txn *badger.Txn) error {
iteratorOptions := badger.DefaultIteratorOptions iteratorOptions := badger.DefaultIteratorOptions
iteratorOptions.Prefix = this.prefix iteratorOptions.Prefix = this.prefix // filter by prefix
// create an iterator the default options // create an iterator the default options
it := txn.NewIterator(iteratorOptions) it := txn.NewIterator(iteratorOptions)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment