github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/mempool/stdmap/incorporated_result_seals.go (about) 1 package stdmap 2 3 import ( 4 "log" 5 6 "github.com/onflow/flow-go/model/flow" 7 "github.com/onflow/flow-go/module/mempool" 8 ) 9 10 type sealSet map[flow.Identifier]*flow.IncorporatedResultSeal 11 12 // IncorporatedResultSeals implements the incorporated result seals memory pool 13 // of the consensus nodes, used to store seals that need to be added to blocks. 14 // ATTENTION: This data structure should NEVER eject seals because it can break liveness. 15 // Modules that are using this structure expect that it NEVER ejects a seal. 16 type IncorporatedResultSeals struct { 17 *Backend 18 // index the seals by the height of the executed block 19 byHeight map[uint64]sealSet 20 lowestHeight uint64 21 } 22 23 func indexByHeight(seal *flow.IncorporatedResultSeal) uint64 { 24 return seal.Header.Height 25 } 26 27 // NewIncorporatedResultSeals creates a mempool for the incorporated result seals 28 func NewIncorporatedResultSeals(limit uint) *IncorporatedResultSeals { 29 byHeight := make(map[uint64]sealSet) 30 31 // This mempool implementation supports pruning by height, meaning that as soon as sealing advances 32 // seals will be gradually removed from mempool 33 // ejecting a seal from mempool means that we have reached our limit and something is very bad, meaning that sealing 34 // is not actually happening. 35 // By setting high limit ~12 hours we ensure that we have some safety window for sealing to recover and make progress 36 ejector := func(b *Backend) (flow.Identifier, flow.Entity, bool) { 37 log.Fatalf("incorporated result seals reached max capacity %d", limit) 38 panic("incorporated result seals reached max capacity") 39 } 40 41 r := &IncorporatedResultSeals{ 42 Backend: NewBackend(WithLimit(limit), WithEject(ejector)), 43 byHeight: byHeight, 44 } 45 46 return r 47 } 48 49 func (ir *IncorporatedResultSeals) removeFromIndex(id flow.Identifier, height uint64) { 50 sealsAtHeight := ir.byHeight[height] 51 delete(sealsAtHeight, id) 52 if len(sealsAtHeight) == 0 { 53 delete(ir.byHeight, height) 54 } 55 } 56 57 func (ir *IncorporatedResultSeals) removeByHeight(height uint64) { 58 for sealID := range ir.byHeight[height] { 59 ir.backData.Remove(sealID) 60 } 61 delete(ir.byHeight, height) 62 } 63 64 // Add adds an IncorporatedResultSeal to the mempool 65 func (ir *IncorporatedResultSeals) Add(seal *flow.IncorporatedResultSeal) (bool, error) { 66 added := false 67 sealID := seal.ID() 68 err := ir.Backend.Run(func(_ mempool.BackData) error { 69 // skip elements below the pruned 70 if seal.Header.Height < ir.lowestHeight { 71 return nil 72 } 73 74 added = ir.backData.Add(sealID, seal) 75 if !added { 76 return nil 77 } 78 79 height := indexByHeight(seal) 80 sameHeight, ok := ir.byHeight[height] 81 if !ok { 82 sameHeight = make(sealSet) 83 ir.byHeight[height] = sameHeight 84 } 85 sameHeight[sealID] = seal 86 return nil 87 }) 88 89 return added, err 90 } 91 92 // Size returns the size of the underlying backing store 93 func (ir *IncorporatedResultSeals) Size() uint { 94 return ir.Backend.Size() 95 } 96 97 // All returns all the items in the mempool 98 func (ir *IncorporatedResultSeals) All() []*flow.IncorporatedResultSeal { 99 entities := ir.Backend.All() 100 res := make([]*flow.IncorporatedResultSeal, 0, ir.backData.Size()) 101 for _, entity := range entities { 102 // uncaught type assertion; should never panic as the mempool only stores IncorporatedResultSeal: 103 res = append(res, entity.(*flow.IncorporatedResultSeal)) 104 } 105 return res 106 } 107 108 // ByID gets an IncorporatedResultSeal by IncorporatedResult ID 109 func (ir *IncorporatedResultSeals) ByID(id flow.Identifier) (*flow.IncorporatedResultSeal, bool) { 110 entity, ok := ir.Backend.ByID(id) 111 if !ok { 112 return nil, false 113 } 114 // uncaught type assertion; should never panic as the mempool only stores IncorporatedResultSeal: 115 return entity.(*flow.IncorporatedResultSeal), true 116 } 117 118 // Remove removes an IncorporatedResultSeal from the mempool 119 func (ir *IncorporatedResultSeals) Remove(id flow.Identifier) bool { 120 removed := false 121 err := ir.Backend.Run(func(_ mempool.BackData) error { 122 var entity flow.Entity 123 entity, removed = ir.backData.Remove(id) 124 if !removed { 125 return nil 126 } 127 seal := entity.(*flow.IncorporatedResultSeal) 128 ir.removeFromIndex(id, seal.Header.Height) 129 return nil 130 }) 131 if err != nil { 132 panic(err) 133 } 134 return removed 135 } 136 137 func (ir *IncorporatedResultSeals) Clear() { 138 err := ir.Backend.Run(func(_ mempool.BackData) error { 139 ir.backData.Clear() 140 ir.byHeight = make(map[uint64]sealSet) 141 return nil 142 }) 143 if err != nil { 144 panic(err) 145 } 146 } 147 148 // PruneUpToHeight remove all seals for blocks whose height is strictly 149 // smaller that height. Note: seals for blocks at height are retained. 150 // After pruning, seals below for blocks below the given height are dropped. 151 // 152 // Monotonicity Requirement: 153 // The pruned height cannot decrease, as we cannot recover already pruned elements. 154 // If `height` is smaller than the previous value, the previous value is kept 155 // and the sentinel mempool.BelowPrunedThresholdError is returned. 156 func (ir *IncorporatedResultSeals) PruneUpToHeight(height uint64) error { 157 return ir.Backend.Run(func(backData mempool.BackData) error { 158 if height < ir.lowestHeight { 159 return mempool.NewBelowPrunedThresholdErrorf( 160 "pruning height: %d, existing height: %d", height, ir.lowestHeight) 161 } 162 163 if backData.Size() == 0 { 164 ir.lowestHeight = height 165 return nil 166 } 167 // Optimization: if there are less height in the index than the height range to prune, 168 // range to prune, then just go through each seal. 169 // Otherwise, go through each height to prune. 170 if uint64(len(ir.byHeight)) < height-ir.lowestHeight { 171 for h := range ir.byHeight { 172 if h < height { 173 ir.removeByHeight(h) 174 } 175 } 176 } else { 177 for h := ir.lowestHeight; h < height; h++ { 178 ir.removeByHeight(h) 179 } 180 } 181 ir.lowestHeight = height 182 return nil 183 }) 184 }