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