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  }