@@ -466,7 +466,13 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
466
466
return
467
467
}
468
468
// If there are no more references to the child, delete it and cascade
469
- node .parents --
469
+ if node .parents > 0 {
470
+ // This is a special cornercase where a node loaded from disk (i.e. not in the
471
+ // memcache any more) gets reinjected as a new node (short node split into full,
472
+ // then reverted into short), causing a cached node to have no parents. That is
473
+ // no problem in itself, but don't make maxint parents out of it.
474
+ node .parents --
475
+ }
470
476
if node .parents == 0 {
471
477
// Remove the node from the flush-list
472
478
if child == db .oldest {
@@ -717,3 +723,45 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
717
723
var flushlistSize = common .StorageSize ((len (db .nodes ) - 1 ) * 2 * common .HashLength )
718
724
return db .nodesSize + flushlistSize , db .preimagesSize
719
725
}
726
+
727
+ // verifyIntegrity is a debug method to iterate over the entire trie stored in
728
+ // memory and check whether every node is reachable from the meta root. The goal
729
+ // is to find any errors that might cause memory leaks and or trie nodes to go
730
+ // missing.
731
+ //
732
+ // This method is extremely CPU and memory intensive, only use when must.
733
+ func (db * Database ) verifyIntegrity () {
734
+ // Iterate over all the cached nodes and accumulate them into a set
735
+ reachable := map [common.Hash ]struct {}{{}: {}}
736
+
737
+ for child := range db .nodes [common.Hash {}].children {
738
+ db .accumulate (child , reachable )
739
+ }
740
+ // Find any unreachable but cached nodes
741
+ unreachable := []string {}
742
+ for hash , node := range db .nodes {
743
+ if _ , ok := reachable [hash ]; ! ok {
744
+ unreachable = append (unreachable , fmt .Sprintf ("%x: {Node: %v, Parents: %d, Prev: %x, Next: %x}" ,
745
+ hash , node .node , node .parents , node .flushPrev , node .flushNext ))
746
+ }
747
+ }
748
+ if len (unreachable ) != 0 {
749
+ panic (fmt .Sprintf ("trie cache memory leak: %v" , unreachable ))
750
+ }
751
+ }
752
+
753
+ // accumulate iterates over the trie defined by hash and accumulates all the
754
+ // cached children found in memory.
755
+ func (db * Database ) accumulate (hash common.Hash , reachable map [common.Hash ]struct {}) {
756
+ // Mark the node reachable if present in the memory cache
757
+ node , ok := db .nodes [hash ]
758
+ if ! ok {
759
+ return
760
+ }
761
+ reachable [hash ] = struct {}{}
762
+
763
+ // Iterate over all the children and accumulate them too
764
+ for _ , child := range node .childs () {
765
+ db .accumulate (child , reachable )
766
+ }
767
+ }
0 commit comments