sample_chain/database/leveldbhelper.go

202 lines
5.2 KiB
Go

package database
import (
"fmt"
"os"
"sync"
log "github.com/corgi-kx/logcustom"
"github.com/pkg/errors"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
goleveldbutil "github.com/syndtr/goleveldb/leveldb/util"
)
type dbState int32
const (
closed dbState = iota
opened
)
// DB 用于存储区块链账本的levelDB数据库
type DB struct {
dbPath string
db *leveldb.DB
dbState dbState
mutex sync.RWMutex
readOpts *opt.ReadOptions
writeOptsNoSync *opt.WriteOptions
writeOptsSync *opt.WriteOptions
}
// UpdateBatch encloses the details of multiple `updates`
type UpdateBatch struct {
leveldbBatch *leveldb.Batch
dbName string
size int
}
type Snapshot struct {
dbName string
snapshot *leveldb.Snapshot
readOpts *opt.ReadOptions
}
// CreateDB constructs a `DB`
func CreateDB(dp string) *DB {
readOpts := &opt.ReadOptions{}
writeOptsNoSync := &opt.WriteOptions{}
writeOptsSync := &opt.WriteOptions{}
writeOptsSync.Sync = true
return &DB{
dbPath: dp,
dbState: closed,
readOpts: readOpts,
writeOptsNoSync: writeOptsNoSync,
writeOptsSync: writeOptsSync,
}
}
// 打开基础数据库
func (dbInst *DB) Open() {
dbInst.mutex.Lock()
defer dbInst.mutex.Unlock()
if dbInst.dbState == opened {
return
}
dbPath := dbInst.dbPath
var err error
if err = os.MkdirAll(dbPath, 0755); err != nil {
panic(fmt.Sprintf("Error creating dir if missing: %s", err))
}
if dbInst.db, err = leveldb.OpenFile(dbPath, nil); err != nil {
panic(fmt.Sprintf("Error opening leveldb: %s", err))
}
dbInst.dbState = opened
}
// IsEmpty returns whether a database is empty
func (dbInst *DB) IsEmpty() (bool, error) {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
itr := dbInst.db.NewIterator(&goleveldbutil.Range{}, dbInst.readOpts)
defer itr.Release()
hasItems := itr.Next()
return !hasItems,
errors.Wrapf(itr.Error(), "error while trying to see if the leveldb at path [%s] is empty", dbInst.dbPath)
}
// Close closes the underlying db
func (dbInst *DB) Close() {
dbInst.mutex.Lock()
defer dbInst.mutex.Unlock()
if dbInst.dbState == closed {
return
}
if err := dbInst.db.Close(); err != nil {
log.Errorf("Error closing leveldb: %s", err)
}
dbInst.dbState = closed
}
// Get returns the value for the given key
func (dbInst *DB) Get(key []byte) ([]byte, error) {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
value, err := dbInst.db.Get(key, dbInst.readOpts)
if err == leveldb.ErrNotFound {
value = nil
err = nil
}
if err != nil {
log.Errorf("Error retrieving leveldb key [%#v]: %s", key, err)
return nil, errors.Wrapf(err, "error retrieving leveldb key [%#v]", key)
}
return value, nil
}
// Put saves the key/value
func (dbInst *DB) Put(key []byte, value []byte, sync bool) error {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
wo := dbInst.writeOptsNoSync
if sync {
wo = dbInst.writeOptsSync
}
err := dbInst.db.Put(key, value, wo)
if err != nil {
log.Errorf("Error writing leveldb key [%#v]", key)
return errors.Wrapf(err, "error writing leveldb key [%#v]", key)
}
return nil
}
// Delete deletes the given key
func (dbInst *DB) Delete(key []byte, sync bool) error {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
wo := dbInst.writeOptsNoSync
if sync {
wo = dbInst.writeOptsSync
}
err := dbInst.db.Delete(key, wo)
if err != nil {
log.Errorf("Error deleting leveldb key [%#v]", key)
return errors.Wrapf(err, "error deleting leveldb key [%#v]", key)
}
return nil
}
// GetIterator returns an iterator over key-value store. 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 (dbInst *DB) GetIterator(startKey []byte, endKey []byte) iterator.Iterator {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
return dbInst.db.NewIterator(&goleveldbutil.Range{Start: startKey, Limit: endKey}, dbInst.readOpts)
}
func (dbInst *DB) PrefixQuery(Key []byte) iterator.Iterator {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
//BytesPrefix返回满足给定前缀的键范围。这仅适用于标准的“字节比较器”
return dbInst.db.NewIterator(goleveldbutil.BytesPrefix(Key), dbInst.readOpts)
}
// WriteBatch 批量写入
func (dbInst *DB) WriteBatch(batch *leveldb.Batch, sync bool) error {
dbInst.mutex.RLock()
defer dbInst.mutex.RUnlock()
wo := dbInst.writeOptsNoSync
if sync {
wo = dbInst.writeOptsSync
}
if err := dbInst.db.Write(batch, wo); err != nil {
return errors.Wrap(err, "error writing batch to leveldb")
}
return nil
}
/*type Batch struct {
data []byte
index []batchIndex
// internalLen is sums of key/value pair length plus 8-bytes internal key.
internalLen int
}
func (*leveldb.Batch).Delete(key []byte)
func (*leveldb.Batch).Dump() []byte
func (*leveldb.Batch).Len() int
func (*leveldb.Batch).Load(data []byte) error
func (*leveldb.Batch).Put(key []byte, value []byte)
func (*leveldb.Batch).Replay(r leveldb.BatchReplay) error
func (*leveldb.Batch).Reset()
*/