github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/common/synchronization/request_heap.go (about) 1 package synchronization 2 3 import ( 4 "sync" 5 6 "github.com/onflow/flow-go/engine" 7 "github.com/onflow/flow-go/model/flow" 8 ) 9 10 // RequestHeap is a special structure that implements engine.MessageStore interface and 11 // indexes requests by originator. If request will be sent by same originator then it will replace the old one. 12 // Comparing to default FIFO queue this one can contain MAX one request for origin ID. 13 // Getting value from queue as well as ejecting is pseudo-random. 14 type RequestHeap struct { 15 lock sync.Mutex 16 limit uint 17 requests map[flow.Identifier]*engine.Message 18 } 19 20 func NewRequestHeap(limit uint) *RequestHeap { 21 return &RequestHeap{ 22 limit: limit, 23 requests: make(map[flow.Identifier]*engine.Message), 24 } 25 } 26 27 // Put stores message into requests map using OriginID as key. 28 // Returns always true 29 func (q *RequestHeap) Put(message *engine.Message) bool { 30 q.lock.Lock() 31 defer q.lock.Unlock() 32 // first try to eject if we are at max capacity, we need to do this way 33 // to prevent a situation where just inserted item gets ejected 34 if _, found := q.requests[message.OriginID]; !found { 35 // if no message from the origin is stored, make sure we have room to store the new message: 36 q.reduce() 37 } 38 // at this point we can be sure that there is at least one slot 39 q.requests[message.OriginID] = message 40 return true 41 } 42 43 // Get returns pseudo-random element from request storage using go map properties. 44 func (q *RequestHeap) Get() (*engine.Message, bool) { 45 q.lock.Lock() 46 defer q.lock.Unlock() 47 48 if len(q.requests) == 0 { 49 return nil, false 50 } 51 52 // pick arbitrary element using go map randomness property 53 var originID flow.Identifier 54 var msg *engine.Message 55 for originID, msg = range q.requests { 56 break 57 } 58 delete(q.requests, originID) 59 60 return msg, true 61 } 62 63 // reduce will reduce the size of the kept entities until we are within the 64 // configured memory pool size limit. If called on max capacity will eject at least one element. 65 func (q *RequestHeap) reduce() { 66 for overCapacity := len(q.requests) - int(q.limit); overCapacity >= 0; overCapacity-- { 67 for originID := range q.requests { // pick first element using go map randomness property 68 delete(q.requests, originID) 69 break 70 } 71 } 72 }