-
Notifications
You must be signed in to change notification settings - Fork 178
/
backend.go
129 lines (100 loc) · 2.91 KB
/
backend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package buffer
import (
"sync"
"github.com/onflow/flow-go/model/flow"
)
// item represents an item in the cache: a block header, payload, and the ID
// of the node that sent it to us. The payload is generic.
type item struct {
header flow.Slashable[*flow.Header]
payload interface{}
}
// backend implements a simple cache of pending blocks, indexed by parent ID.
type backend struct {
mu sync.RWMutex
// map of pending header IDs, keyed by parent ID for ByParentID lookups
blocksByParent map[flow.Identifier][]flow.Identifier
// set of pending blocks, keyed by ID to avoid duplication
blocksByID map[flow.Identifier]*item
}
// newBackend returns a new pending header cache.
func newBackend() *backend {
cache := &backend{
blocksByParent: make(map[flow.Identifier][]flow.Identifier),
blocksByID: make(map[flow.Identifier]*item),
}
return cache
}
// add adds the item to the cache, returning false if it already exists and
// true otherwise.
func (b *backend) add(block flow.Slashable[*flow.Header], payload interface{}) bool {
b.mu.Lock()
defer b.mu.Unlock()
blockID := block.Message.ID()
_, exists := b.blocksByID[blockID]
if exists {
return false
}
item := &item{
header: block,
payload: payload,
}
b.blocksByID[blockID] = item
b.blocksByParent[block.Message.ParentID] = append(b.blocksByParent[block.Message.ParentID], blockID)
return true
}
func (b *backend) byID(id flow.Identifier) (*item, bool) {
b.mu.RLock()
defer b.mu.RUnlock()
item, exists := b.blocksByID[id]
if !exists {
return nil, false
}
return item, true
}
// byParentID returns a list of cached blocks with the given parent. If no such
// blocks exist, returns false.
func (b *backend) byParentID(parentID flow.Identifier) ([]*item, bool) {
b.mu.RLock()
defer b.mu.RUnlock()
forParent, exists := b.blocksByParent[parentID]
if !exists {
return nil, false
}
items := make([]*item, 0, len(forParent))
for _, blockID := range forParent {
items = append(items, b.blocksByID[blockID])
}
return items, true
}
// dropForParent removes all cached blocks with the given parent (non-recursively).
func (b *backend) dropForParent(parentID flow.Identifier) {
b.mu.Lock()
defer b.mu.Unlock()
children, exists := b.blocksByParent[parentID]
if !exists {
return
}
for _, childID := range children {
delete(b.blocksByID, childID)
}
delete(b.blocksByParent, parentID)
}
// pruneByView prunes any items in the cache that have view less than or
// equal to the given view. The pruning view should be the finalized view.
func (b *backend) pruneByView(view uint64) {
b.mu.Lock()
defer b.mu.Unlock()
for id, item := range b.blocksByID {
if item.header.Message.View <= view {
delete(b.blocksByID, id)
delete(b.blocksByParent, item.header.Message.ParentID)
}
}
}
// size returns the number of elements stored in teh backend
func (b *backend) size() uint {
b.mu.RLock()
defer b.mu.RUnlock()
return uint(len(b.blocksByID))
}