Skip to content

Commit 47c23e6

Browse files
authored
fix(lib/grandpa): capped number of tracked commit messages (#2490)
- Removes oldest commit message when tracker capacity is reached - Efficient removal of multiple messages at any place in the tracker queue (linked list) if they get processed - Efficient removal of oldest message - Uses a bit more space to store each block hash (+2 pointers) for each commit message - Order is not modified for the same commit message (same block hash)
1 parent 4d2b2f6 commit 47c23e6

File tree

3 files changed

+449
-16
lines changed

3 files changed

+449
-16
lines changed

lib/grandpa/commits_tracker.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2022 ChainSafe Systems (ON)
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
package grandpa
5+
6+
import (
7+
"container/list"
8+
9+
"github.com/ChainSafe/gossamer/lib/common"
10+
)
11+
12+
// commitsTracker tracks vote messages that could
13+
// not be processed, and removes the oldest ones once
14+
// its maximum capacity is reached.
15+
// It is NOT THREAD SAFE to use.
16+
type commitsTracker struct {
17+
// map of commit block hash to linked list commit message.
18+
mapping map[common.Hash]*list.Element
19+
// double linked list of commit messages
20+
// to track the order commit messages were added in.
21+
linkedList *list.List
22+
capacity int
23+
}
24+
25+
// newCommitsTracker creates a new commit messages tracker
26+
// with the capacity specified.
27+
func newCommitsTracker(capacity int) commitsTracker {
28+
return commitsTracker{
29+
mapping: make(map[common.Hash]*list.Element, capacity),
30+
linkedList: list.New(),
31+
capacity: capacity,
32+
}
33+
}
34+
35+
// add adds a commit message to the commit message tracker.
36+
// If the commit message tracker capacity is reached,
37+
// the oldest commit message is removed.
38+
func (ct *commitsTracker) add(commitMessage *CommitMessage) {
39+
blockHash := commitMessage.Vote.Hash
40+
41+
listElement, has := ct.mapping[blockHash]
42+
if has {
43+
// commit already exists so override the commit message in the linked list;
44+
// do not move the list element in the linked list to avoid
45+
// someone re-sending the same commit message and going at the
46+
// front of the list, hence erasing other possible valid commit messages
47+
// in the tracker.
48+
listElement.Value = commitMessage
49+
return
50+
}
51+
52+
// add new block hash in tracker
53+
ct.cleanup()
54+
listElement = ct.linkedList.PushFront(commitMessage)
55+
ct.mapping[blockHash] = listElement
56+
}
57+
58+
// cleanup removes the oldest commit message from the tracker
59+
// if the number of commit messages is at the tracker capacity.
60+
// This method is designed to be called automatically from the
61+
// add method and should not be called elsewhere.
62+
func (ct *commitsTracker) cleanup() {
63+
if ct.linkedList.Len() < ct.capacity {
64+
return
65+
}
66+
67+
oldestElement := ct.linkedList.Back()
68+
ct.linkedList.Remove(oldestElement)
69+
70+
oldestCommitMessage := oldestElement.Value.(*CommitMessage)
71+
oldestBlockHash := oldestCommitMessage.Vote.Hash
72+
delete(ct.mapping, oldestBlockHash)
73+
}
74+
75+
// delete deletes all the vote messages for a particular
76+
// block hash from the vote messages tracker.
77+
func (ct *commitsTracker) delete(blockHash common.Hash) {
78+
listElement, has := ct.mapping[blockHash]
79+
if !has {
80+
return
81+
}
82+
83+
ct.linkedList.Remove(listElement)
84+
delete(ct.mapping, blockHash)
85+
}
86+
87+
// message returns a pointer to the
88+
// commit message for a particular block hash from
89+
// the tracker. It returns nil if the block hash
90+
// does not exist in the tracker
91+
func (ct *commitsTracker) message(blockHash common.Hash) (
92+
message *CommitMessage) {
93+
listElement, ok := ct.mapping[blockHash]
94+
if !ok {
95+
return nil
96+
}
97+
98+
return listElement.Value.(*CommitMessage)
99+
}
100+
101+
// forEach runs the function `f` on each
102+
// commit message stored in the tracker.
103+
func (ct *commitsTracker) forEach(f func(message *CommitMessage)) {
104+
for _, data := range ct.mapping {
105+
message := data.Value.(*CommitMessage)
106+
f(message)
107+
}
108+
}

0 commit comments

Comments
 (0)