@@ -466,7 +466,13 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
466466 return
467467 }
468468 // 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+ }
470476 if node .parents == 0 {
471477 // Remove the node from the flush-list
472478 if child == db .oldest {
@@ -717,3 +723,45 @@ func (db *Database) Size() (common.StorageSize, common.StorageSize) {
717723 var flushlistSize = common .StorageSize ((len (db .nodes ) - 1 ) * 2 * common .HashLength )
718724 return db .nodesSize + flushlistSize , db .preimagesSize
719725}
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