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 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, // Close closes the DB. This will also releases any outstanding snapshot,
// abort any in-flight compaction and discard open transaction. // 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) //return rle.Decompress(dat)
} }
func (self *LDBDatabase) ReturnBuffer(b []byte) {
self.db.ReturnBuffer(b)
}
// Delete deletes the key from the queue and database // Delete deletes the key from the queue and database
func (self *LDBDatabase) Delete(key []byte) error { func (self *LDBDatabase) Delete(key []byte) error {
// Measure the database delete latency, if requested // Measure the database delete latency, if requested

View file

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

View file

@ -62,6 +62,9 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return nil, errors.New("not found") return nil, errors.New("not found")
} }
func (db *MemDatabase) ReturnBuffer(b []byte) {
}
func (db *MemDatabase) Keys() [][]byte { func (db *MemDatabase) Keys() [][]byte {
db.lock.RLock() db.lock.RLock()
defer db.lock.RUnlock() 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 cn.flags.hash = cachedHash
if db != nil { if db != nil {
cn.flags.dirty = false cn.flags.dirty = false
cn.flags.new = false
} }
case *fullNode: case *fullNode:
cn.flags.hash = cachedHash cn.flags.hash = cachedHash
if db != nil { if db != nil {
cn.flags.dirty = false cn.flags.dirty = false
cn.flags.new = false
} }
} }
return hashed, cached, nil return hashed, cached, nil

View file

@ -60,6 +60,7 @@ type nodeFlag struct {
hash hashNode // cached hash of the node (may be nil) hash hashNode // cached hash of the node (may be nil)
gen uint16 // cache generation counter gen uint16 // cache generation counter
dirty bool // whether the node has changes that must be written to the database 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. // 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 { if err != nil {
return nil, err return nil, err
} }
flag := nodeFlag{hash: hash, gen: cachegen} flag := nodeFlag{hash: hash, gen: cachegen, new: true}
key := compactDecode(kbuf) key := compactDecode(kbuf)
if key[len(key)-1] == 16 { if key[len(key)-1] == 16 {
// value node // 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) { 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++ { for i := 0; i < 16; i++ {
cld, rest, err := decodeRef(elems, cachegen) cld, rest, err := decodeRef(elems, cachegen)
if err != nil { if err != nil {

View file

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

View file

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