Skip to content

Commit a771824

Browse files
authored
Merge pull request #1353 from onetechnical/onetechnical/relstable2.0.9
Onetechnical/relstable2.0.9
2 parents 890f535 + d60e1c9 commit a771824

File tree

4 files changed

+130
-4
lines changed

4 files changed

+130
-4
lines changed

buildnumber.dat

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8
1+
9

crypto/merkletrie/cache.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type merkleTrieCache struct {
7272

7373
// a list of the pages priorities. The item in the front has higher priority and would not get evicted as quickly as the item on the back
7474
pagesPrioritizationList *list.List
75-
// the list element of each of the priorities
75+
// the list element of each of the priorities. The pagesPrioritizationMap maps a page id to the page priority list element.
7676
pagesPrioritizationMap map[uint64]*list.Element
7777
// the page to load before the nextNodeID at init time. If zero, then nothing is being reloaded.
7878
deferedPageLoad uint64
@@ -145,8 +145,9 @@ func (mtc *merkleTrieCache) getNode(nid storedNodeIdentifier) (pnode *node, err
145145
return
146146
}
147147

148-
// prioritizeNode make sure to move the priorities of the pages according to
149-
// the accessed node identifier
148+
// prioritizePage make sure to adjust the priority of the given node id.
149+
// a new page would be placed on front, and an older page would get moved
150+
// to the front.
150151
func (mtc *merkleTrieCache) prioritizeNode(nid storedNodeIdentifier) {
151152
page := uint64(nid) / uint64(mtc.nodesPerPage)
152153

@@ -350,6 +351,9 @@ func (mtc *merkleTrieCache) commit() error {
350351
element := mtc.pagesPrioritizationMap[uint64(page)]
351352
if element != nil {
352353
mtc.pagesPrioritizationList.Remove(element)
354+
delete(mtc.pagesPrioritizationMap, uint64(page))
355+
mtc.cachedNodeCount -= len(mtc.pageToNIDsPtr[uint64(page)])
356+
delete(mtc.pageToNIDsPtr, uint64(page))
353357
}
354358
}
355359

@@ -440,6 +444,7 @@ func (mtc *merkleTrieCache) encodePage(nodeIDs map[storedNodeIdentifier]*node) [
440444
// evict releases the least used pages from cache until the number of elements in cache are less than cachedNodeCountTarget
441445
func (mtc *merkleTrieCache) evict() (removedNodes int) {
442446
removedNodes = mtc.cachedNodeCount
447+
443448
for mtc.cachedNodeCount > mtc.cachedNodeCountTarget {
444449
// get the least used page off the pagesPrioritizationList
445450
element := mtc.pagesPrioritizationList.Back()
@@ -448,6 +453,7 @@ func (mtc *merkleTrieCache) evict() (removedNodes int) {
448453
}
449454
mtc.pagesPrioritizationList.Remove(element)
450455
pageToRemove := element.Value.(uint64)
456+
delete(mtc.pagesPrioritizationMap, pageToRemove)
451457
mtc.cachedNodeCount -= len(mtc.pageToNIDsPtr[pageToRemove])
452458
delete(mtc.pageToNIDsPtr, pageToRemove)
453459
}

crypto/merkletrie/cache_test.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (C) 2019-2020 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package merkletrie
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
24+
"github.com/algorand/go-algorand/crypto"
25+
)
26+
27+
func verifyCacheNodeCount(t *testing.T, trie *Trie) {
28+
count := 0
29+
for _, pageNodes := range trie.cache.pageToNIDsPtr {
30+
count += len(pageNodes)
31+
}
32+
require.Equal(t, count, trie.cache.cachedNodeCount)
33+
34+
// make sure that the pagesPrioritizationMap aligns with pagesPrioritizationList
35+
require.Equal(t, len(trie.cache.pagesPrioritizationMap), trie.cache.pagesPrioritizationList.Len())
36+
37+
for e := trie.cache.pagesPrioritizationList.Back(); e != nil; e = e.Next() {
38+
page := e.Value.(uint64)
39+
_, has := trie.cache.pagesPrioritizationMap[page]
40+
require.True(t, has)
41+
_, has = trie.cache.pageToNIDsPtr[page]
42+
require.True(t, has)
43+
}
44+
}
45+
46+
func TestCacheEviction1(t *testing.T) {
47+
var memoryCommitter InMemoryCommitter
48+
mt1, _ := MakeTrie(&memoryCommitter, defaultTestEvictSize)
49+
// create 13000 hashes.
50+
leafsCount := 13000
51+
hashes := make([]crypto.Digest, leafsCount)
52+
for i := 0; i < len(hashes); i++ {
53+
hashes[i] = crypto.Hash([]byte{byte(i % 256), byte((i / 256) % 256), byte(i / 65536)})
54+
}
55+
56+
for i := 0; i < defaultTestEvictSize; i++ {
57+
mt1.Add(hashes[i][:])
58+
}
59+
60+
for i := defaultTestEvictSize; i < len(hashes); i++ {
61+
mt1.Add(hashes[i][:])
62+
mt1.Evict(true)
63+
require.GreaterOrEqual(t, defaultTestEvictSize, mt1.cache.cachedNodeCount)
64+
verifyCacheNodeCount(t, mt1)
65+
}
66+
}
67+
68+
func TestCacheEviction2(t *testing.T) {
69+
var memoryCommitter InMemoryCommitter
70+
mt1, _ := MakeTrie(&memoryCommitter, defaultTestEvictSize)
71+
// create 20000 hashes.
72+
leafsCount := 20000
73+
hashes := make([]crypto.Digest, leafsCount)
74+
for i := 0; i < len(hashes); i++ {
75+
hashes[i] = crypto.Hash([]byte{byte(i % 256), byte((i / 256) % 256), byte(i / 65536)})
76+
}
77+
78+
for i := 0; i < defaultTestEvictSize; i++ {
79+
mt1.Add(hashes[i][:])
80+
}
81+
82+
for i := defaultTestEvictSize; i < len(hashes); i++ {
83+
mt1.Delete(hashes[i-2][:])
84+
mt1.Add(hashes[i][:])
85+
mt1.Add(hashes[i-2][:])
86+
87+
if i%(len(hashes)/20) == 0 {
88+
mt1.Evict(true)
89+
require.GreaterOrEqual(t, defaultTestEvictSize, mt1.cache.cachedNodeCount)
90+
verifyCacheNodeCount(t, mt1)
91+
}
92+
}
93+
}
94+
95+
func TestCacheEviction3(t *testing.T) {
96+
var memoryCommitter InMemoryCommitter
97+
mt1, _ := MakeTrie(&memoryCommitter, defaultTestEvictSize)
98+
// create 200000 hashes.
99+
leafsCount := 200000
100+
hashes := make([]crypto.Digest, leafsCount)
101+
for i := 0; i < len(hashes); i++ {
102+
hashes[i] = crypto.Hash([]byte{byte(i % 256), byte((i / 256) % 256), byte(i / 65536)})
103+
}
104+
105+
for i := 0; i < defaultTestEvictSize; i++ {
106+
mt1.Add(hashes[i][:])
107+
}
108+
109+
for i := defaultTestEvictSize; i < len(hashes); i++ {
110+
mt1.Delete(hashes[i-500][:])
111+
mt1.Add(hashes[i][:])
112+
113+
if i%(len(hashes)/20) == 0 {
114+
mt1.Evict(true)
115+
require.GreaterOrEqual(t, defaultTestEvictSize, mt1.cache.cachedNodeCount)
116+
verifyCacheNodeCount(t, mt1)
117+
}
118+
}
119+
}

crypto/merkletrie/trie_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ func TestRandomAddingAndRemoving(t *testing.T) {
133133
if (i % (1 + int(processesHash[0]))) == 42 {
134134
err := mt.Commit()
135135
require.NoError(t, err)
136+
verifyCacheNodeCount(t, mt)
136137
}
137138
}
138139
}

0 commit comments

Comments
 (0)