github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/mempool/queue/heroQueue.go (about)

     1  package queue
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/rs/zerolog"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  	herocache "github.com/onflow/flow-go/module/mempool/herocache/backdata"
    11  	"github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool"
    12  )
    13  
    14  // HeroQueue implements a HeroCache-based in-memory queue.
    15  // HeroCache is a key-value cache with zero heap allocation and optimized Garbage Collection.
    16  type HeroQueue struct {
    17  	mu        sync.RWMutex
    18  	cache     *herocache.Cache
    19  	sizeLimit uint
    20  }
    21  
    22  func NewHeroQueue(sizeLimit uint32, logger zerolog.Logger, collector module.HeroCacheMetrics,
    23  ) *HeroQueue {
    24  	return &HeroQueue{
    25  		cache: herocache.NewCache(
    26  			sizeLimit,
    27  			herocache.DefaultOversizeFactor,
    28  			heropool.NoEjection,
    29  			logger.With().Str("mempool", "hero-queue").Logger(),
    30  			collector),
    31  		sizeLimit: uint(sizeLimit),
    32  	}
    33  }
    34  
    35  // Push stores the entity into the queue.
    36  // Boolean returned variable determines whether push was successful, i.e.,
    37  // push may be dropped if queue is full or already exists.
    38  func (c *HeroQueue) Push(entity flow.Entity) bool {
    39  	c.mu.Lock()
    40  	defer c.mu.Unlock()
    41  
    42  	if c.cache.Size() >= c.sizeLimit {
    43  		// we check size before attempt on a push,
    44  		// although HeroCache is on no-ejection mode and discards pushes beyond limit,
    45  		// we save an id computation by just checking the size here.
    46  		return false
    47  	}
    48  
    49  	return c.cache.Add(entity.ID(), entity)
    50  }
    51  
    52  // Pop removes and returns the head of queue, and updates the head to the next element.
    53  // Boolean return value determines whether pop is successful, i.e., popping an empty queue returns false.
    54  func (c *HeroQueue) Pop() (flow.Entity, bool) {
    55  	c.mu.Lock()
    56  	defer c.mu.Unlock()
    57  
    58  	head, ok := c.cache.Head()
    59  	if !ok {
    60  		// cache is empty, and there is no head yet to pop.
    61  		return nil, false
    62  	}
    63  
    64  	c.cache.Remove(head.ID())
    65  	return head, true
    66  }
    67  
    68  func (c *HeroQueue) Size() uint {
    69  	c.mu.RLock()
    70  	defer c.mu.RUnlock()
    71  
    72  	return c.cache.Size()
    73  }