Merge remote-tracking branch 'db/master' into unstable
This commit is contained in:
commit
d234a164f7
|
@ -0,0 +1,3 @@
|
||||||
|
Tendermint Go-DB Copyright (C) 2015 All in Bits, Inc
|
||||||
|
|
||||||
|
Released under the Apache2.0 license
|
|
@ -0,0 +1 @@
|
||||||
|
TODO: syndtr/goleveldb should be replaced with actual LevelDB instance
|
|
@ -0,0 +1,152 @@
|
||||||
|
// +build gcc
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/jmhodges/levigo"
|
||||||
|
|
||||||
|
. "github.com/tendermint/go-common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbCreator := func(name string, dir string) (DB, error) {
|
||||||
|
return NewCLevelDB(name, dir)
|
||||||
|
}
|
||||||
|
registerDBCreator(LevelDBBackendStr, dbCreator, true)
|
||||||
|
registerDBCreator(CLevelDBBackendStr, dbCreator, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CLevelDB struct {
|
||||||
|
db *levigo.DB
|
||||||
|
ro *levigo.ReadOptions
|
||||||
|
wo *levigo.WriteOptions
|
||||||
|
woSync *levigo.WriteOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCLevelDB(name string, dir string) (*CLevelDB, error) {
|
||||||
|
dbPath := path.Join(dir, name+".db")
|
||||||
|
|
||||||
|
opts := levigo.NewOptions()
|
||||||
|
opts.SetCache(levigo.NewLRUCache(1 << 30))
|
||||||
|
opts.SetCreateIfMissing(true)
|
||||||
|
db, err := levigo.Open(dbPath, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ro := levigo.NewReadOptions()
|
||||||
|
wo := levigo.NewWriteOptions()
|
||||||
|
woSync := levigo.NewWriteOptions()
|
||||||
|
woSync.SetSync(true)
|
||||||
|
database := &CLevelDB{
|
||||||
|
db: db,
|
||||||
|
ro: ro,
|
||||||
|
wo: wo,
|
||||||
|
woSync: woSync,
|
||||||
|
}
|
||||||
|
return database, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Get(key []byte) []byte {
|
||||||
|
res, err := db.db.Get(db.ro, key)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Set(key []byte, value []byte) {
|
||||||
|
err := db.db.Put(db.wo, key, value)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) SetSync(key []byte, value []byte) {
|
||||||
|
err := db.db.Put(db.woSync, key, value)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Delete(key []byte) {
|
||||||
|
err := db.db.Delete(db.wo, key)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) DeleteSync(key []byte) {
|
||||||
|
err := db.db.Delete(db.woSync, key)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) DB() *levigo.DB {
|
||||||
|
return db.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Close() {
|
||||||
|
db.db.Close()
|
||||||
|
db.ro.Close()
|
||||||
|
db.wo.Close()
|
||||||
|
db.woSync.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Print() {
|
||||||
|
iter := db.db.NewIterator(db.ro)
|
||||||
|
defer iter.Close()
|
||||||
|
for iter.Seek(nil); iter.Valid(); iter.Next() {
|
||||||
|
key := iter.Key()
|
||||||
|
value := iter.Value()
|
||||||
|
fmt.Printf("[%X]:\t[%X]\n", key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Stats() map[string]string {
|
||||||
|
// TODO: Find the available properties for the C LevelDB implementation
|
||||||
|
keys := []string{}
|
||||||
|
|
||||||
|
stats := make(map[string]string)
|
||||||
|
for _, key := range keys {
|
||||||
|
str, err := db.db.GetProperty(key)
|
||||||
|
if err == nil {
|
||||||
|
stats[key] = str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) Iterator() Iterator {
|
||||||
|
return db.db.NewIterator(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *CLevelDB) NewBatch() Batch {
|
||||||
|
batch := levigo.NewWriteBatch()
|
||||||
|
return &cLevelDBBatch{db, batch}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type cLevelDBBatch struct {
|
||||||
|
db *CLevelDB
|
||||||
|
batch *levigo.WriteBatch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *cLevelDBBatch) Set(key, value []byte) {
|
||||||
|
mBatch.batch.Put(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *cLevelDBBatch) Delete(key []byte) {
|
||||||
|
mBatch.batch.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *cLevelDBBatch) Write() {
|
||||||
|
err := mBatch.db.db.Write(mBatch.db.wo, mBatch.batch)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// +build gcc
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/tendermint/go-common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkRandomReadsWrites2(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
numItems := int64(1000000)
|
||||||
|
internal := map[int64]int64{}
|
||||||
|
for i := 0; i < int(numItems); i++ {
|
||||||
|
internal[int64(i)] = int64(0)
|
||||||
|
}
|
||||||
|
db, err := NewCLevelDB(Fmt("test_%x", RandStr(12)), "")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("ok, starting")
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// Write something
|
||||||
|
{
|
||||||
|
idx := (int64(RandInt()) % numItems)
|
||||||
|
internal[idx] += 1
|
||||||
|
val := internal[idx]
|
||||||
|
idxBytes := int642Bytes(int64(idx))
|
||||||
|
valBytes := int642Bytes(int64(val))
|
||||||
|
//fmt.Printf("Set %X -> %X\n", idxBytes, valBytes)
|
||||||
|
db.Set(
|
||||||
|
idxBytes,
|
||||||
|
valBytes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Read something
|
||||||
|
{
|
||||||
|
idx := (int64(RandInt()) % numItems)
|
||||||
|
val := internal[idx]
|
||||||
|
idxBytes := int642Bytes(int64(idx))
|
||||||
|
valBytes := db.Get(idxBytes)
|
||||||
|
//fmt.Printf("Get %X -> %X\n", idxBytes, valBytes)
|
||||||
|
if val == 0 {
|
||||||
|
if !bytes.Equal(valBytes, nil) {
|
||||||
|
b.Errorf("Expected %X for %v, got %X",
|
||||||
|
nil, idx, valBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(valBytes) != 8 {
|
||||||
|
b.Errorf("Expected length 8 for %v, got %X",
|
||||||
|
idx, valBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
valGot := bytes2Int64(valBytes)
|
||||||
|
if val != valGot {
|
||||||
|
b.Errorf("Expected %v for %v, got %v",
|
||||||
|
val, idx, valGot)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func int642Bytes(i int64) []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, uint64(i))
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func bytes2Int64(buf []byte) int64 {
|
||||||
|
return int64(binary.BigEndian.Uint64(buf))
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,60 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import . "github.com/tendermint/go-common"
|
||||||
|
|
||||||
|
type DB interface {
|
||||||
|
Get([]byte) []byte
|
||||||
|
Set([]byte, []byte)
|
||||||
|
SetSync([]byte, []byte)
|
||||||
|
Delete([]byte)
|
||||||
|
DeleteSync([]byte)
|
||||||
|
Close()
|
||||||
|
NewBatch() Batch
|
||||||
|
|
||||||
|
// For debugging
|
||||||
|
Print()
|
||||||
|
Iterator() Iterator
|
||||||
|
Stats() map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Batch interface {
|
||||||
|
Set(key, value []byte)
|
||||||
|
Delete(key []byte)
|
||||||
|
Write()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Iterator interface {
|
||||||
|
Next() bool
|
||||||
|
|
||||||
|
Key() []byte
|
||||||
|
Value() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelDBBackendStr = "leveldb" // legacy, defaults to goleveldb.
|
||||||
|
CLevelDBBackendStr = "cleveldb"
|
||||||
|
GoLevelDBBackendStr = "goleveldb"
|
||||||
|
MemDBBackendStr = "memdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dbCreator func(name string, dir string) (DB, error)
|
||||||
|
|
||||||
|
var backends = map[string]dbCreator{}
|
||||||
|
|
||||||
|
func registerDBCreator(backend string, creator dbCreator, force bool) {
|
||||||
|
_, ok := backends[backend]
|
||||||
|
if !force && ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backends[backend] = creator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDB(name string, backend string, dir string) DB {
|
||||||
|
db, err := backends[backend](name, dir)
|
||||||
|
if err != nil {
|
||||||
|
PanicSanity(Fmt("Error initializing DB: %v", err))
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
|
|
||||||
|
. "github.com/tendermint/go-common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbCreator := func(name string, dir string) (DB, error) {
|
||||||
|
return NewGoLevelDB(name, dir)
|
||||||
|
}
|
||||||
|
registerDBCreator(LevelDBBackendStr, dbCreator, false)
|
||||||
|
registerDBCreator(GoLevelDBBackendStr, dbCreator, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoLevelDB struct {
|
||||||
|
db *leveldb.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGoLevelDB(name string, dir string) (*GoLevelDB, error) {
|
||||||
|
dbPath := path.Join(dir, name+".db")
|
||||||
|
db, err := leveldb.OpenFile(dbPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
database := &GoLevelDB{db: db}
|
||||||
|
return database, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Get(key []byte) []byte {
|
||||||
|
res, err := db.db.Get(key, nil)
|
||||||
|
if err != nil {
|
||||||
|
if err == errors.ErrNotFound {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Set(key []byte, value []byte) {
|
||||||
|
err := db.db.Put(key, value, nil)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) SetSync(key []byte, value []byte) {
|
||||||
|
err := db.db.Put(key, value, &opt.WriteOptions{Sync: true})
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Delete(key []byte) {
|
||||||
|
err := db.db.Delete(key, nil)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) DeleteSync(key []byte) {
|
||||||
|
err := db.db.Delete(key, &opt.WriteOptions{Sync: true})
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) DB() *leveldb.DB {
|
||||||
|
return db.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Close() {
|
||||||
|
db.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Print() {
|
||||||
|
str, _ := db.db.GetProperty("leveldb.stats")
|
||||||
|
fmt.Printf("%v\n", str)
|
||||||
|
|
||||||
|
iter := db.db.NewIterator(nil, nil)
|
||||||
|
for iter.Next() {
|
||||||
|
key := iter.Key()
|
||||||
|
value := iter.Value()
|
||||||
|
fmt.Printf("[%X]:\t[%X]\n", key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Stats() map[string]string {
|
||||||
|
keys := []string{
|
||||||
|
"leveldb.num-files-at-level{n}",
|
||||||
|
"leveldb.stats",
|
||||||
|
"leveldb.sstables",
|
||||||
|
"leveldb.blockpool",
|
||||||
|
"leveldb.cachedblock",
|
||||||
|
"leveldb.openedtables",
|
||||||
|
"leveldb.alivesnaps",
|
||||||
|
"leveldb.aliveiters",
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := make(map[string]string)
|
||||||
|
for _, key := range keys {
|
||||||
|
str, err := db.db.GetProperty(key)
|
||||||
|
if err == nil {
|
||||||
|
stats[key] = str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) Iterator() Iterator {
|
||||||
|
return db.db.NewIterator(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *GoLevelDB) NewBatch() Batch {
|
||||||
|
batch := new(leveldb.Batch)
|
||||||
|
return &goLevelDBBatch{db, batch}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type goLevelDBBatch struct {
|
||||||
|
db *GoLevelDB
|
||||||
|
batch *leveldb.Batch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *goLevelDBBatch) Set(key, value []byte) {
|
||||||
|
mBatch.batch.Put(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *goLevelDBBatch) Delete(key []byte) {
|
||||||
|
mBatch.batch.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *goLevelDBBatch) Write() {
|
||||||
|
err := mBatch.db.db.Write(mBatch.batch, nil)
|
||||||
|
if err != nil {
|
||||||
|
PanicCrisis(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/tendermint/go-common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkRandomReadsWrites(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
numItems := int64(1000000)
|
||||||
|
internal := map[int64]int64{}
|
||||||
|
for i := 0; i < int(numItems); i++ {
|
||||||
|
internal[int64(i)] = int64(0)
|
||||||
|
}
|
||||||
|
db, err := NewGoLevelDB(Fmt("test_%x", RandStr(12)), "")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("ok, starting")
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// Write something
|
||||||
|
{
|
||||||
|
idx := (int64(RandInt()) % numItems)
|
||||||
|
internal[idx] += 1
|
||||||
|
val := internal[idx]
|
||||||
|
idxBytes := int642Bytes(int64(idx))
|
||||||
|
valBytes := int642Bytes(int64(val))
|
||||||
|
//fmt.Printf("Set %X -> %X\n", idxBytes, valBytes)
|
||||||
|
db.Set(
|
||||||
|
idxBytes,
|
||||||
|
valBytes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Read something
|
||||||
|
{
|
||||||
|
idx := (int64(RandInt()) % numItems)
|
||||||
|
val := internal[idx]
|
||||||
|
idxBytes := int642Bytes(int64(idx))
|
||||||
|
valBytes := db.Get(idxBytes)
|
||||||
|
//fmt.Printf("Get %X -> %X\n", idxBytes, valBytes)
|
||||||
|
if val == 0 {
|
||||||
|
if !bytes.Equal(valBytes, nil) {
|
||||||
|
b.Errorf("Expected %X for %v, got %X",
|
||||||
|
nil, idx, valBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(valBytes) != 8 {
|
||||||
|
b.Errorf("Expected length 8 for %v, got %X",
|
||||||
|
idx, valBytes)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
valGot := bytes2Int64(valBytes)
|
||||||
|
if val != valGot {
|
||||||
|
b.Errorf("Expected %v for %v, got %v",
|
||||||
|
val, idx, valGot)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func int642Bytes(i int64) []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, uint64(i))
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func bytes2Int64(buf []byte) int64 {
|
||||||
|
return int64(binary.BigEndian.Uint64(buf))
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerDBCreator(MemDBBackendStr, func(name string, dir string) (DB, error) {
|
||||||
|
return NewMemDB(), nil
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemDB struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
db map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemDB() *MemDB {
|
||||||
|
database := &MemDB{db: make(map[string][]byte)}
|
||||||
|
return database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Get(key []byte) []byte {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
return db.db[string(key)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Set(key []byte, value []byte) {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
db.db[string(key)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) SetSync(key []byte, value []byte) {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
db.db[string(key)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Delete(key []byte) {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
delete(db.db, string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) DeleteSync(key []byte) {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
delete(db.db, string(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Close() {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
db = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Print() {
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
for key, value := range db.db {
|
||||||
|
fmt.Printf("[%X]:\t[%X]\n", []byte(key), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Stats() map[string]string {
|
||||||
|
stats := make(map[string]string)
|
||||||
|
stats["database.type"] = "memDB"
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
type memDBIterator struct {
|
||||||
|
last int
|
||||||
|
keys []string
|
||||||
|
db *MemDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMemDBIterator() *memDBIterator {
|
||||||
|
return &memDBIterator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *memDBIterator) Next() bool {
|
||||||
|
if it.last >= len(it.keys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.last++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *memDBIterator) Key() []byte {
|
||||||
|
return []byte(it.keys[it.last])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *memDBIterator) Value() []byte {
|
||||||
|
return it.db.Get(it.Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) Iterator() Iterator {
|
||||||
|
it := newMemDBIterator()
|
||||||
|
it.db = db
|
||||||
|
it.last = -1
|
||||||
|
|
||||||
|
db.mtx.Lock()
|
||||||
|
defer db.mtx.Unlock()
|
||||||
|
|
||||||
|
// unfortunately we need a copy of all of the keys
|
||||||
|
for key, _ := range db.db {
|
||||||
|
it.keys = append(it.keys, key)
|
||||||
|
}
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *MemDB) NewBatch() Batch {
|
||||||
|
return &memDBBatch{db, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type memDBBatch struct {
|
||||||
|
db *MemDB
|
||||||
|
ops []operation
|
||||||
|
}
|
||||||
|
|
||||||
|
type opType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
opTypeSet = 1
|
||||||
|
opTypeDelete = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type operation struct {
|
||||||
|
opType
|
||||||
|
key []byte
|
||||||
|
value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *memDBBatch) Set(key, value []byte) {
|
||||||
|
mBatch.ops = append(mBatch.ops, operation{opTypeSet, key, value})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *memDBBatch) Delete(key []byte) {
|
||||||
|
mBatch.ops = append(mBatch.ops, operation{opTypeDelete, key, nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mBatch *memDBBatch) Write() {
|
||||||
|
mBatch.db.mtx.Lock()
|
||||||
|
defer mBatch.db.mtx.Unlock()
|
||||||
|
|
||||||
|
for _, op := range mBatch.ops {
|
||||||
|
if op.opType == opTypeSet {
|
||||||
|
mBatch.db.db[string(op.key)] = op.value
|
||||||
|
} else if op.opType == opTypeDelete {
|
||||||
|
delete(mBatch.db.db, string(op.key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue