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