Queued level db writes and batch writes. Closes #647

This commit is contained in:
obscuren 2015-04-07 22:19:01 +02:00
parent 758205b187
commit 7f32a08b60
3 changed files with 97 additions and 49 deletions

View File

@ -4,9 +4,7 @@ package common
type Database interface { type Database interface {
Put(key []byte, value []byte) Put(key []byte, value []byte)
Get(key []byte) ([]byte, error) Get(key []byte) ([]byte, error)
//GetKeys() []*Key
Delete(key []byte) error Delete(key []byte) error
LastKnownTD() []byte LastKnownTD() []byte
Close() Close()
Print()
} }

View File

@ -1,17 +1,25 @@
package ethdb package ethdb
import ( import (
"fmt" "sync"
"time"
"github.com/ethereum/go-ethereum/compression/rle" "github.com/ethereum/go-ethereum/compression/rle"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/iterator"
) )
type LDBDatabase struct { type LDBDatabase struct {
fn string
mu sync.Mutex
db *leveldb.DB db *leveldb.DB
comp bool
queue map[string][]byte
quit chan struct{}
} }
func NewLDBDatabase(file string) (*LDBDatabase, error) { func NewLDBDatabase(file string) (*LDBDatabase, error) {
@ -20,35 +28,61 @@ func NewLDBDatabase(file string) (*LDBDatabase, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
database := &LDBDatabase{db: db, comp: true} database := &LDBDatabase{
fn: file,
db: db,
quit: make(chan struct{}),
}
database.makeQueue()
go database.update()
return database, nil return database, nil
} }
func (self *LDBDatabase) makeQueue() {
self.queue = make(map[string][]byte)
}
func (self *LDBDatabase) Put(key []byte, value []byte) { func (self *LDBDatabase) Put(key []byte, value []byte) {
if self.comp { self.mu.Lock()
defer self.mu.Unlock()
self.queue[string(key)] = value
/*
value = rle.Compress(value) value = rle.Compress(value)
}
err := self.db.Put(key, value, nil) err := self.db.Put(key, value, nil)
if err != nil { if err != nil {
fmt.Println("Error put", err) fmt.Println("Error put", err)
} }
*/
} }
func (self *LDBDatabase) Get(key []byte) ([]byte, error) { func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
self.mu.Lock()
defer self.mu.Unlock()
// Check queue first
if dat, ok := self.queue[string(key)]; ok {
return dat, nil
}
dat, err := self.db.Get(key, nil) dat, err := self.db.Get(key, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if self.comp {
return rle.Decompress(dat) return rle.Decompress(dat)
}
return dat, nil
} }
func (self *LDBDatabase) Delete(key []byte) error { func (self *LDBDatabase) Delete(key []byte) error {
self.mu.Lock()
defer self.mu.Unlock()
// make sure it's not in the queue
delete(self.queue, string(key))
return self.db.Delete(key, nil) return self.db.Delete(key, nil)
} }
@ -66,23 +100,46 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
return self.db.NewIterator(nil, nil) return self.db.NewIterator(nil, nil)
} }
func (self *LDBDatabase) Write(batch *leveldb.Batch) error { func (self *LDBDatabase) Flush() error {
self.mu.Lock()
defer self.mu.Unlock()
batch := new(leveldb.Batch)
for key, value := range self.queue {
batch.Put([]byte(key), rle.Compress(value))
}
self.makeQueue() // reset the queue
return self.db.Write(batch, nil) return self.db.Write(batch, nil)
} }
func (self *LDBDatabase) Close() { func (self *LDBDatabase) Close() {
self.quit <- struct{}{}
<-self.quit
glog.V(logger.Info).Infoln("flushed and closed db:", self.fn)
}
func (self *LDBDatabase) update() {
ticker := time.NewTicker(1 * time.Minute)
done:
for {
select {
case <-ticker.C:
if err := self.Flush(); err != nil {
glog.V(logger.Error).Infof("error: flush '%s': %v\n", self.fn, err)
}
case <-self.quit:
break done
}
}
if err := self.Flush(); err != nil {
glog.V(logger.Error).Infof("error: flush '%s': %v\n", self.fn, err)
}
// Close the leveldb database // Close the leveldb database
self.db.Close() self.db.Close()
}
func (self *LDBDatabase) Print() { self.quit <- struct{}{}
iter := self.db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Printf("%x(%d): ", key, len(key))
node := common.NewValueFromBytes(value)
fmt.Printf("%v\n", node)
}
} }

View File

@ -1,26 +1,19 @@
package ethdb package ethdb
/*
import ( import (
"bytes" "os"
"testing" "path"
"github.com/ethereum/go-ethereum/common"
) )
func TestCompression(t *testing.T) { func newDb() *LDBDatabase {
db, err := NewLDBDatabase("testdb") file := path.Join("/", "tmp", "ldbtesttmpfile")
if err != nil { if common.FileExist(file) {
t.Fatal(err) os.RemoveAll(file)
} }
in := make([]byte, 10) db, _ := NewLDBDatabase(file)
db.Put([]byte("test1"), in)
out, err := db.Get([]byte("test1"))
if err != nil {
t.Fatal(err)
}
if bytes.Compare(out, in) != 0 { return db
t.Error("put get", in, out)
}
} }
*/