Finish badger and sinkhole storage

This commit is contained in:
Daniel 2018-09-05 17:05:23 +02:00
parent 2716b9663c
commit 94598b115b
6 changed files with 266 additions and 28 deletions

View file

@ -1 +1,146 @@
package badger
import (
"errors"
"time"
"github.com/dgraph-io/badger"
"github.com/Safing/portbase/database/iterator"
"github.com/Safing/portbase/database/model"
"github.com/Safing/portbase/database/query"
"github.com/Safing/portbase/database/storage"
)
// Badger database made pluggable for portbase.
type Badger struct {
name string
db *badger.DB
}
func init() {
storage.Register("badger", NewBadger)
}
// NewBadger opens/creates a badger database.
func NewBadger(name, location string) (storage.Interface, error) {
opts := badger.DefaultOptions
opts.Dir = location
opts.ValueDir = location
db, err := badger.Open(opts)
if err != nil {
return nil, err
}
return &Badger{
name: name,
db: db,
}, nil
}
// Exists returns whether an entry with the given key exists.
func (b *Badger) Exists(key string) (bool, error) {
err := b.db.View(func(txn *badger.Txn) error {
_, err := txn.Get([]byte(key))
if err != nil {
if err == badger.ErrKeyNotFound {
return nil
}
return err
}
return nil
})
if err == nil {
return true, nil
}
return false, nil
}
// Get returns a database record.
func (b *Badger) Get(key string) (model.Model, error) {
var item *badger.Item
err := b.db.View(func(txn *badger.Txn) error {
var err error
item, err = txn.Get([]byte(key))
if err != nil {
if err == badger.ErrKeyNotFound {
return storage.ErrNotFound
}
return err
}
return nil
})
if err != nil {
return nil, err
}
if item.IsDeletedOrExpired() {
return nil, storage.ErrNotFound
}
data, err := item.ValueCopy(nil)
if err != nil {
return nil, err
}
m, err := model.NewRawWrapper(b.name, string(item.Key()), data)
if err != nil {
return nil, err
}
return m, nil
}
// Put stores a record in the database.
func (b *Badger) Put(m model.Model) error {
data, err := m.MarshalRecord()
if err != nil {
return err
}
err = b.db.Update(func(txn *badger.Txn) error {
if m.Meta().GetAbsoluteExpiry() > 0 {
txn.SetWithTTL([]byte(m.DatabaseKey()), data, time.Duration(m.Meta().GetRelativeExpiry()))
} else {
txn.Set([]byte(m.DatabaseKey()), data)
}
return nil
})
return err
}
// Delete deletes a record from the database.
func (b *Badger) Delete(key string) error {
return b.db.Update(func(txn *badger.Txn) error {
err := txn.Delete([]byte(key))
if err != nil && err != badger.ErrKeyNotFound {
return err
}
return nil
})
}
// Query returns a an iterator for the supplied query.
func (b *Badger) Query(q *query.Query) (*iterator.Iterator, error) {
return nil, errors.New("query not implemented by badger")
}
// Maintain runs a light maintenance operation on the database.
func (b *Badger) Maintain() error {
b.db.RunValueLogGC(0.7)
return nil
}
// MaintainThorough runs a thorough maintenance operation on the database.
func (b *Badger) MaintainThorough() (err error) {
for err == nil {
err = b.db.RunValueLogGC(0.7)
}
return nil
}
// Shutdown shuts down the database.
func (b *Badger) Shutdown() error {
return b.db.Close()
}

View file

@ -1,9 +0,0 @@
package bbolt
import bolt "go.etcd.io/bbolt"
db, err := bolt.Open(path, 0666, nil)
if err != nil {
return err
}
defer db.Close()

View file

@ -0,0 +1,8 @@
package storage
import "errors"
// Errors for storages
var (
ErrNotFound = errors.New("not found")
)

View file

@ -8,27 +8,13 @@ import (
// Interface defines the database storage API.
type Interface interface {
// Retrieve
Exists(key string) (bool, error)
Get(key string) (model.Model, error)
// Modify
Create(model model.Model) error
Update(model model.Model) error // create when not exists
UpdateOrCreate(model model.Model) error // update, create if not exists.
Put(m model.Model) error
Delete(key string) error
Query(q *query.Query) (*iterator.Iterator, error)
// Partial
// What happens if I mutate a value that does not yet exist? How would I know its type?
InsertPartial(key string, partialObject interface{})
InsertValue(key string, attribute string, value interface{})
// Query
Query(q *query.Query, local, internal bool) (*iterator.Iterator, error)
// Meta
SetAbsoluteExpiry(key string, time int64)
SetRelativateExpiry(key string, duration int64)
MakeCrownJewel(key string)
MakeSecret(key string)
Maintain() error
MaintainThorough() error
Shutdown() error
}

View file

@ -0,0 +1,66 @@
package sinkhole
import (
"errors"
"github.com/Safing/portbase/database/iterator"
"github.com/Safing/portbase/database/model"
"github.com/Safing/portbase/database/query"
"github.com/Safing/portbase/database/storage"
)
// Sinkhole is a dummy storage.
type Sinkhole struct {
name string
}
func init() {
storage.Register("sinkhole", NewSinkhole)
}
// NewSinkhole creates a dummy database.
func NewSinkhole(name, location string) (storage.Interface, error) {
return &Sinkhole{
name: name,
}, nil
}
// Exists returns whether an entry with the given key exists.
func (s *Sinkhole) Exists(key string) (bool, error) {
return false, nil
}
// Get returns a database record.
func (s *Sinkhole) Get(key string) (model.Model, error) {
return nil, storage.ErrNotFound
}
// Put stores a record in the database.
func (s *Sinkhole) Put(m model.Model) error {
return nil
}
// Delete deletes a record from the database.
func (s *Sinkhole) Delete(key string) error {
return nil
}
// Query returns a an iterator for the supplied query.
func (s *Sinkhole) Query(q *query.Query) (*iterator.Iterator, error) {
return nil, errors.New("query not implemented by sinkhole")
}
// Maintain runs a light maintenance operation on the database.
func (s *Sinkhole) Maintain() error {
return nil
}
// MaintainThorough runs a thorough maintenance operation on the database.
func (s *Sinkhole) MaintainThorough() (err error) {
return nil
}
// Shutdown shuts down the database.
func (s *Sinkhole) Shutdown() error {
return nil
}

View file

@ -0,0 +1,42 @@
package storage
import (
"errors"
"fmt"
"sync"
)
// A Factory creates a new database of it's type.
type Factory func(name, location string) (Interface, error)
var (
storages map[string]Factory
storagesLock sync.Mutex
)
// Register registers a new storage type.
func Register(name string, factory Factory) error {
storagesLock.Lock()
defer storagesLock.Unlock()
_, ok := storages[name]
if ok {
return errors.New("factory for this type already exists")
}
storages[name] = factory
return nil
}
// StartDatabase starts a new database with the given name and storageType at location.
func StartDatabase(name, storageType, location string) (Interface, error) {
storagesLock.Lock()
defer storagesLock.Unlock()
factory, ok := storages[name]
if !ok {
return nil, fmt.Errorf("storage of this type (%s) does not exist", storageType)
}
return factory(name, location)
}