This commit is contained in:
Felix Lange 2016-10-20 11:03:27 +00:00 committed by GitHub
commit 3abf3580a3
8 changed files with 53 additions and 36 deletions

View file

@ -1017,6 +1017,10 @@ func (db *DB) SizeOf(ranges []util.Range) (Sizes, error) {
return sizes, nil
}
func (db *DB) ReturnBuffer(b []byte) {
db.s.tops.bpool.Put(b)
}
// Close closes the DB. This will also releases any outstanding snapshot,
// abort any in-flight compaction and discard open transaction.
//

View file

@ -142,6 +142,10 @@ func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
//return rle.Decompress(dat)
}
func (self *LDBDatabase) ReturnBuffer(b []byte) {
self.db.ReturnBuffer(b)
}
// Delete deletes the key from the queue and database
func (self *LDBDatabase) Delete(key []byte) error {
// Measure the database delete latency, if requested

View file

@ -22,6 +22,8 @@ type Database interface {
Delete(key []byte) error
Close()
NewBatch() Batch
ReturnBuffer([]byte)
}
type Batch interface {

View file

@ -62,6 +62,9 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return nil, errors.New("not found")
}
func (db *MemDatabase) ReturnBuffer(b []byte) {
}
func (db *MemDatabase) Keys() [][]byte {
db.lock.RLock()
defer db.lock.RUnlock()

View file

@ -85,11 +85,13 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error)
cn.flags.hash = cachedHash
if db != nil {
cn.flags.dirty = false
cn.flags.new = false
}
case *fullNode:
cn.flags.hash = cachedHash
if db != nil {
cn.flags.dirty = false
cn.flags.new = false
}
}
return hashed, cached, nil

View file

@ -60,6 +60,7 @@ type nodeFlag struct {
hash hashNode // cached hash of the node (may be nil)
gen uint16 // cache generation counter
dirty bool // whether the node has changes that must be written to the database
new bool // whether the node was allocated in the current generation
}
// canUnload tells whether a node can be unloaded.
@ -138,7 +139,7 @@ func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
if err != nil {
return nil, err
}
flag := nodeFlag{hash: hash, gen: cachegen}
flag := nodeFlag{hash: hash, gen: cachegen, new: true}
key := compactDecode(kbuf)
if key[len(key)-1] == 16 {
// value node
@ -156,7 +157,7 @@ func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
}
func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) {
n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}}
n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen, new: true}}
for i := 0; i < 16; i++ {
cld, rest, err := decodeRef(elems, cachegen)
if err != nil {

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/rcrowley/go-metrics"
@ -98,7 +99,7 @@ func (t *Trie) SetCacheLimit(l uint16) {
// newFlag returns the cache flag value for a newly created node.
func (t *Trie) newFlag() nodeFlag {
return nodeFlag{dirty: true, gen: t.cachegen}
return nodeFlag{new: true, dirty: true, gen: t.cachegen}
}
// New creates a trie with an existing root node from db.
@ -142,46 +143,52 @@ func (t *Trie) Get(key []byte) []byte {
// If a node was not found in the database, a MissingNodeError is returned.
func (t *Trie) TryGet(key []byte) ([]byte, error) {
key = compactHexDecode(key)
value, newroot, didResolve, err := t.tryGet(t.root, key, 0)
if err == nil && didResolve {
value, newroot, err := t.tryGet(t.root, key, 0)
if err == nil {
t.root = newroot
}
return value, err
}
func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) {
func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, err error) {
switch n := (origNode).(type) {
case nil:
return nil, nil, false, nil
return nil, nil, nil
case valueNode:
return n, n, false, nil
return n, n, nil
case *shortNode:
if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
// key not found in trie
return nil, n, false, nil
return nil, n, nil
}
value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key))
if err == nil && didResolve {
n = n.copy()
n.Val = newnode
value, newnode, err = t.tryGet(n.Val, key, pos+len(n.Key))
if err == nil {
if !n.flags.new {
n = n.copy()
n.flags.new = true
}
n.flags.gen = t.cachegen
n.Val = newnode
}
return value, n, didResolve, err
return value, n, err
case *fullNode:
value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1)
if err == nil && didResolve {
n = n.copy()
value, newnode, err = t.tryGet(n.Children[key[pos]], key, pos+1)
if err == nil {
if !n.flags.new {
n = n.copy()
n.flags.new = true
}
n.flags.gen = t.cachegen
n.Children[key[pos]] = newnode
}
return value, n, didResolve, err
return value, n, err
case hashNode:
child, err := t.resolveHash(n, key[:pos], key[pos:])
if err != nil {
return nil, n, true, err
return nil, n, err
}
value, newnode, _, err := t.tryGet(child, key, pos)
return value, newnode, true, err
value, newnode, err := t.tryGet(child, key, pos)
return value, newnode, err
default:
panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode))
}
@ -283,11 +290,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
if err != nil {
return false, nil, err
}
dirty, nn, err := t.insert(rn, prefix, key, value)
if !dirty || err != nil {
return false, rn, err
}
return true, nn, nil
return t.insert(rn, prefix, key, value)
default:
panic(fmt.Sprintf("%T: invalid node: %v", n, n))
@ -414,11 +417,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
if err != nil {
return false, nil, err
}
dirty, nn, err := t.delete(rn, prefix, key)
if !dirty || err != nil {
return false, rn, err
}
return true, nn, nil
return t.delete(rn, prefix, key)
default:
panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key))
@ -453,6 +452,9 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) {
}
}
dec := mustDecodeNode(n, enc, t.cachegen)
if edb, ok := t.db.(*ethdb.LDBDatabase); ok {
edb.ReturnBuffer(enc)
}
return dec, nil
}

View file

@ -329,8 +329,7 @@ func TestCacheUnload(t *testing.T) {
root, _ := trie.Commit()
// Commit the trie repeatedly and access key1.
// The branch containing it is loaded from DB exactly two times:
// in the 0th and 6th iteration.
// The branch containing it is loaded from DB exactly once.
db := &countingDB{Database: trie.db, gets: make(map[string]int)}
trie, _ = New(root, db)
trie.SetCacheLimit(5)
@ -339,10 +338,10 @@ func TestCacheUnload(t *testing.T) {
trie.Commit()
}
// Check that it got loaded two times.
// Check that it got loaded once.
for dbkey, count := range db.gets {
if count != 2 {
t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 2)
if count != 1 {
t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 1)
}
}
}