github.com/onflow/flow-go@v0.33.17/module/mempool/stdmap/backend.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package stdmap 4 5 import ( 6 "math" 7 "sync" 8 9 "github.com/onflow/flow-go/model/flow" 10 "github.com/onflow/flow-go/module/mempool" 11 "github.com/onflow/flow-go/module/mempool/stdmap/backdata" 12 _ "github.com/onflow/flow-go/utils/binstat" 13 ) 14 15 // Backend is a wrapper around the backdata that provides concurrency-safe operations. 16 type Backend struct { 17 sync.RWMutex 18 backData mempool.BackData 19 guaranteedCapacity uint 20 batchEject BatchEjectFunc 21 eject EjectFunc 22 ejectionCallbacks []mempool.OnEjection 23 } 24 25 // NewBackend creates a new memory pool backend. 26 // This is using EjectRandomFast() 27 func NewBackend(options ...OptionFunc) *Backend { 28 b := Backend{ 29 backData: backdata.NewMapBackData(), 30 guaranteedCapacity: uint(math.MaxUint32), 31 batchEject: EjectRandomFast, 32 eject: nil, 33 ejectionCallbacks: nil, 34 } 35 for _, option := range options { 36 option(&b) 37 } 38 return &b 39 } 40 41 // Has checks if we already contain the item with the given hash. 42 func (b *Backend) Has(entityID flow.Identifier) bool { 43 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".r_lock.(Backend)Has") 44 b.RLock() 45 // binstat.Leave(bs1) 46 47 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Has") 48 // defer binstat.Leave(bs2) 49 defer b.RUnlock() 50 has := b.backData.Has(entityID) 51 return has 52 } 53 54 // Add adds the given item to the pool. 55 func (b *Backend) Add(entity flow.Entity) bool { 56 // bs0 := binstat.EnterTime(binstat.BinStdmap + ".<<lock.(Backend)Add") 57 entityID := entity.ID() // this expensive operation done OUTSIDE of lock :-) 58 // binstat.Leave(bs0) 59 60 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".w_lock.(Backend)Add") 61 b.Lock() 62 // binstat.Leave(bs1) 63 64 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Add") 65 // defer binstat.Leave(bs2) 66 defer b.Unlock() 67 added := b.backData.Add(entityID, entity) 68 b.reduce() 69 return added 70 } 71 72 // Remove will remove the item with the given hash. 73 func (b *Backend) Remove(entityID flow.Identifier) bool { 74 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".w_lock.(Backend)Remove") 75 b.Lock() 76 // binstat.Leave(bs1) 77 78 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Remove") 79 // defer binstat.Leave(bs2) 80 defer b.Unlock() 81 _, removed := b.backData.Remove(entityID) 82 return removed 83 } 84 85 // Adjust will adjust the value item using the given function if the given key can be found. 86 // Returns a bool which indicates whether the value was updated. 87 func (b *Backend) Adjust(entityID flow.Identifier, f func(flow.Entity) flow.Entity) (flow.Entity, bool) { 88 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".w_lock.(Backend)Adjust") 89 b.Lock() 90 // binstat.Leave(bs1) 91 92 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Adjust") 93 // defer binstat.Leave(bs2) 94 defer b.Unlock() 95 entity, wasUpdated := b.backData.Adjust(entityID, f) 96 return entity, wasUpdated 97 } 98 99 // GetWithInit returns the given entity from the backdata. If the entity does not exist, it creates a new entity 100 // using the factory function and stores it in the backdata. 101 // Args: 102 // - entityID: the identifier of the entity to get. 103 // - init: the function that initializes the entity when it is not found. 104 // Returns: 105 // - the entity. 106 // 107 // - a bool which indicates whether the entity was found (or created). 108 func (b *Backend) GetWithInit(entityID flow.Identifier, init func() flow.Entity) (flow.Entity, bool) { 109 b.Lock() 110 defer b.Unlock() 111 112 return b.backData.GetWithInit(entityID, init) 113 } 114 115 // AdjustWithInit adjusts the entity using the given function if the given identifier can be found. When the 116 // entity is not found, it initializes the entity using the given init function and then applies the adjust function. 117 // Args: 118 // - entityID: the identifier of the entity to adjust. 119 // - adjust: the function that adjusts the entity. 120 // - init: the function that initializes the entity when it is not found. 121 // Returns: 122 // - the adjusted entity. 123 // 124 // - a bool which indicates whether the entity was adjusted. 125 func (b *Backend) AdjustWithInit(entityID flow.Identifier, adjust func(flow.Entity) flow.Entity, init func() flow.Entity) (flow.Entity, bool) { 126 b.Lock() 127 defer b.Unlock() 128 129 return b.backData.AdjustWithInit(entityID, adjust, init) 130 } 131 132 // ByID returns the given item from the pool. 133 func (b *Backend) ByID(entityID flow.Identifier) (flow.Entity, bool) { 134 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".r_lock.(Backend)ByID") 135 b.RLock() 136 // binstat.Leave(bs1) 137 138 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)ByID") 139 // defer binstat.Leave(bs2) 140 defer b.RUnlock() 141 entity, exists := b.backData.ByID(entityID) 142 return entity, exists 143 } 144 145 // Run executes a function giving it exclusive access to the backdata 146 func (b *Backend) Run(f func(backdata mempool.BackData) error) error { 147 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".w_lock.(Backend)Run") 148 b.Lock() 149 // binstat.Leave(bs1) 150 151 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Run") 152 // defer binstat.Leave(bs2) 153 defer b.Unlock() 154 err := f(b.backData) 155 b.reduce() 156 return err 157 } 158 159 // Size will return the size of the backend. 160 func (b *Backend) Size() uint { 161 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".r_lock.(Backend)Size") 162 b.RLock() 163 // binstat.Leave(bs1) 164 165 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Size") 166 // defer binstat.Leave(bs2) 167 defer b.RUnlock() 168 size := b.backData.Size() 169 return size 170 } 171 172 // Limit returns the maximum number of items allowed in the backend. 173 func (b *Backend) Limit() uint { 174 return b.guaranteedCapacity 175 } 176 177 // All returns all entities from the pool. 178 func (b *Backend) All() []flow.Entity { 179 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".r_lock.(Backend)All") 180 b.RLock() 181 // binstat.Leave(bs1) 182 183 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)All") 184 // defer binstat.Leave(bs2) 185 defer b.RUnlock() 186 187 return b.backData.Entities() 188 } 189 190 // Clear removes all entities from the pool. 191 func (b *Backend) Clear() { 192 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".w_lock.(Backend)Clear") 193 b.Lock() 194 // binstat.Leave(bs1) 195 196 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)Clear") 197 // defer binstat.Leave(bs2) 198 defer b.Unlock() 199 b.backData.Clear() 200 } 201 202 // RegisterEjectionCallbacks adds the provided OnEjection callbacks 203 func (b *Backend) RegisterEjectionCallbacks(callbacks ...mempool.OnEjection) { 204 // bs1 := binstat.EnterTime(binstat.BinStdmap + ".r_lock.(Backend)RegisterEjectionCallbacks") 205 b.Lock() 206 // binstat.Leave(bs1) 207 208 // bs2 := binstat.EnterTime(binstat.BinStdmap + ".inlock.(Backend)RegisterEjectionCallbacks") 209 // defer binstat.Leave(bs2) 210 defer b.Unlock() 211 b.ejectionCallbacks = append(b.ejectionCallbacks, callbacks...) 212 } 213 214 // reduce will reduce the size of the kept entities until we are within the 215 // configured memory pool size limit. 216 func (b *Backend) reduce() { 217 // bs := binstat.EnterTime(binstat.BinStdmap + ".??lock.(Backend)reduce") 218 // defer binstat.Leave(bs) 219 220 // we keep reducing the cache size until we are at limit again 221 // this was a loop, but the loop is now in EjectRandomFast() 222 // the ejections are batched, so this call to eject() may not actually 223 // do anything until the batch threshold is reached (currently 128) 224 if b.backData.Size() > b.guaranteedCapacity { 225 // get the key from the eject function 226 // we don't do anything if there is an error 227 if b.batchEject != nil { 228 _, _ = b.batchEject(b) 229 } else { 230 _, _, _ = b.eject(b) 231 } 232 } 233 }