diff --git a/trie/trie.go b/trie/trie.go index e920ccd23f..92d76c5940 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -151,18 +151,26 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode return nil, n, false, nil } value, newnode, didResolve, err = t.tryGet(n.Val, key, pos+len(n.Key)) - if err == nil && didResolve { + if err == nil && (didResolve || n.canUnload(t.cachegen, t.cachelimit/2)) { + // Subtrie expanded or generation old enough to reset n = n.copy() n.Val = newnode n.flags.gen = t.cachegen + + // Fake a resolution so all nodes towards the root are refreshed + didResolve = true } return value, n, didResolve, err case *fullNode: value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1) - if err == nil && didResolve { + if err == nil && (didResolve || n.canUnload(t.cachegen, t.cachelimit/2)) { + // Subtrie expanded or generation old enough to reset n = n.copy() n.flags.gen = t.cachegen n.Children[key[pos]] = newnode + + // Fake a resolution so all nodes towards the root are refreshed + didResolve = true } return value, n, didResolve, err case hashNode: diff --git a/trie/trie_test.go b/trie/trie_test.go index f8e5fd12a1..00aedb119a 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -339,21 +339,28 @@ func TestCacheUnload(t *testing.T) { root, _ := trie.Commit(nil) trie.db.Commit(root, true) - // Commit the trie repeatedly and access key1. - // The branch containing it is loaded from DB exactly two times: - // in the 0th and 6th iteration. + // Commit the trie repeatedly and update key1. The branch containing it is + // loaded from DB once, in the 0th iteration. We attempt to load key2 before + // and after cache-generation numer of iterations. In both cases it should + // reach out to disk. db := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)} trie, _ = New(root, NewDatabase(db)) trie.SetCacheLimit(5) - for i := 0; i < 12; i++ { - getString(trie, key1) + + getString(trie, key2) + for i := 0; i < 6; i++ { + updateString(trie, key1, fmt.Sprintf("this is the branch of key1, update #%d", i)) trie.Commit(nil) } + getString(trie, key2) + // Check that it got loaded two times. - for dbkey, count := range db.gets { - if count != 2 { - t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 2) - } + loads := make(map[int]int) + for _, count := range db.gets { + loads[count]++ + } + if len(loads) != 2 || loads[1] != 3 || loads[2] != 1 { + t.Fatalf("Mismatching number of database loads: want map[1:3 2:1], have %v", loads) } }