sample_chain/database/leveldbprovider.go

250 lines
6.8 KiB
Go

package database
import (
log "github.com/corgi-kx/logcustom"
"github.com/pkg/errors"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator"
goleveldbutil "github.com/syndtr/goleveldb/leveldb/util"
)
const maxBatchSize = 1000000
// DBProvider is a core to a named db
type DBProvider struct {
dbName string
db *DB
}
// Iterator extends actual leveldb iterator
type Iterator struct {
dbName string
iterator.Iterator
}
// NewDBProvider constructs a `DBProvider`
//
//dp:dbPath dn:dbname
func NewDBProvider(dp, dn string) *DBProvider {
db := CreateDB(dp)
db.Open()
return &DBProvider{
dbName: dn,
db: db,
}
}
// Get returns the value for the given key
func (p *DBProvider) Get(key []byte) ([]byte, error) {
return p.db.Get(constructLevelKey(p.dbName, key))
}
// Put saves the key/value
func (p *DBProvider) Put(key []byte, value []byte, sync bool) error {
return p.db.Put(constructLevelKey(p.dbName, key), value, sync)
}
// Delete deletes the given key
func (p *DBProvider) Delete(key []byte, sync bool) error {
return p.db.Delete(constructLevelKey(p.dbName, key), sync)
}
// DeleteAll deletes all the keys that belong to the channel (dbName).
func (p *DBProvider) DeleteAll() error {
iter, err := p.GetIterator(nil, nil)
if err != nil {
return err
}
defer iter.Release()
// use leveldb iterator directly to be more efficient
dbIter := iter.Iterator
// This is common code shared by all the leveldb instances. Because each leveldb has its own key size pattern,
// each batch is limited by memory usage instead of number of keys. Once the batch memory usage reaches maxBatchSize,
// the batch will be committed.
numKeys := 0
batchSize := 0
batch := &leveldb.Batch{}
for dbIter.Next() {
if err := dbIter.Error(); err != nil {
return errors.Wrap(err, "internal leveldb error while retrieving data from db iterator")
}
key := dbIter.Key()
numKeys++
batchSize = batchSize + len(key)
batch.Delete(key)
if batchSize >= maxBatchSize {
if err := p.db.WriteBatch(batch, true); err != nil {
return err
}
log.Infof("Have removed %d entries for channel %s in leveldb %s", numKeys, p.dbName, p.db.dbPath)
batchSize = 0
batch.Reset()
}
}
if batch.Len() > 0 {
return p.db.WriteBatch(batch, true)
}
return nil
}
// IsEmpty returns true if no data exists for the DBProvider
func (p *DBProvider) IsEmpty() (bool, error) {
itr, err := p.GetIterator(nil, nil)
if err != nil {
return false, err
}
defer itr.Release()
if err := itr.Error(); err != nil {
return false, errors.WithMessagef(itr.Error(), "internal leveldb error while obtaining next entry from iterator")
}
return !itr.Next(), nil
}
// NewUpdateBatch returns a new UpdateBatch that can be used to update the db
func (p *DBProvider) NewUpdateBatch() *UpdateBatch {
return &UpdateBatch{
dbName: p.dbName,
leveldbBatch: &leveldb.Batch{},
}
}
// WriteBatch writes a batch in an atomic way
func (p *DBProvider) WriteBatch(batch *UpdateBatch, sync bool) error {
if batch == nil || batch.leveldbBatch.Len() == 0 {
return nil
}
if err := p.db.WriteBatch(batch.leveldbBatch, sync); err != nil {
return err
}
return nil
}
// GetIterator gets a core to iterator. The iterator should be released after the use.
// The resultset contains all the keys that are present in the db between the startKey (inclusive) and the endKey (exclusive).
// A nil startKey represents the first available key and a nil endKey represent a logical key after the last available key
func (p *DBProvider) GetIterator(startKey []byte, endKey []byte) (*Iterator, error) {
var itr iterator.Iterator
if startKey == nil && endKey == nil {
itr = p.db.GetIterator(startKey, startKey)
} else if endKey == nil {
sKey := constructLevelKey(p.dbName, startKey)
itr = p.db.GetIterator(sKey, startKey)
} else {
sKey := constructLevelKey(p.dbName, startKey)
eKey := constructLevelKey(p.dbName, endKey)
itr = p.db.GetIterator(sKey, eKey)
}
if err := itr.Error(); err != nil {
itr.Release()
return nil, errors.Wrapf(err, "internal leveldb error while obtaining db iterator")
}
return &Iterator{p.dbName, itr}, nil
}
func (p *DBProvider) PrefixQuery(key []byte) (*Iterator, error) {
k := constructLevelKey(p.dbName, key)
//log.Debugf("Getting iterator for range [%#v] - [%#v]", sKey, eKey)
itr := p.db.PrefixQuery(k)
if err := itr.Error(); err != nil {
itr.Release()
return nil, errors.Wrapf(err, "internal leveldb error while obtaining db iterator")
}
return &Iterator{p.dbName, itr}, nil
}
func (p *DBProvider) GetSnapshot() (*Snapshot, error) {
itr, err := p.db.db.GetSnapshot()
if err != nil {
return nil, errors.Wrap(err, "error writing batch to leveldb")
}
return &Snapshot{
dbName: p.dbName,
snapshot: itr,
}, nil
}
func (sn *Snapshot) Get(key []byte) ([]byte, error) {
k := constructLevelKey(sn.dbName, key)
value, err := sn.snapshot.Get(k, sn.readOpts)
if err != nil {
return nil, errors.Wrap(err, "error writing batch to leveldb")
}
return value, nil
}
func (sn *Snapshot) Release() {
sn.snapshot.Release()
}
func (sn *Snapshot) NewIterator(startKey []byte, endKey []byte) (*Iterator, error) {
var itr iterator.Iterator
if startKey == nil && endKey == nil {
itr = sn.snapshot.NewIterator(&goleveldbutil.Range{Start: startKey, Limit: endKey}, sn.readOpts)
} else {
sKey := constructLevelKey(sn.dbName, startKey)
eKey := constructLevelKey(sn.dbName, endKey)
itr = sn.snapshot.NewIterator(&goleveldbutil.Range{Start: sKey, Limit: eKey}, sn.readOpts)
}
if err := itr.Error(); err != nil {
itr.Release()
return nil, errors.Wrapf(err, "internal leveldb error while obtaining db iterator")
}
return &Iterator{sn.dbName, itr}, nil
}
// Close closes the DBProvider after its db data have been deleted
func (p *DBProvider) Close() {
p.db.Close()
}
// Put adds a KV
func (b *UpdateBatch) Put(key []byte, value []byte) {
if value == nil {
panic("Nil value not allowed")
}
k := constructLevelKey(b.dbName, key)
b.leveldbBatch.Put(k, value)
b.size += len(k) + len(value)
}
// Delete deletes a Key and associated value
func (b *UpdateBatch) Delete(key []byte) {
k := constructLevelKey(b.dbName, key)
b.size += len(k)
b.leveldbBatch.Delete(k)
}
// Size returns the current size of the batch
func (b *UpdateBatch) Size() int {
return b.size
}
// Len returns number of records in the batch
func (b *UpdateBatch) Len() int {
return b.leveldbBatch.Len()
}
// Reset resets the batch
func (b *UpdateBatch) Reset() {
b.leveldbBatch.Reset()
b.size = 0
}
// Seek moves the iterator to the first key/value pair
// whose key is greater than or equal to the given key.
// It returns whether such pair exist.
func (itr *Iterator) Seek(key []byte) bool {
levelKey := constructLevelKey(itr.dbName, key)
return itr.Iterator.Seek(levelKey)
}
func constructLevelKey(dbName string, key []byte) []byte {
return append([]byte(dbName), key...)
}