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  }