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

     1  package stdmap
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/model/flow"
     7  	"github.com/onflow/flow-go/module/mempool"
     8  	"github.com/onflow/flow-go/module/mempool/model"
     9  )
    10  
    11  // IdentifierMap represents a concurrency-safe memory pool for IdMapEntity.
    12  type IdentifierMap struct {
    13  	*Backend
    14  }
    15  
    16  // NewIdentifierMap creates a new memory pool for IdMapEntity.
    17  func NewIdentifierMap(limit uint) (*IdentifierMap, error) {
    18  	i := &IdentifierMap{
    19  		Backend: NewBackend(WithLimit(limit)),
    20  	}
    21  	return i, nil
    22  }
    23  
    24  // Append will append the id to the list of identifiers associated with key.
    25  func (i *IdentifierMap) Append(key, id flow.Identifier) error {
    26  	return i.Backend.Run(func(backdata mempool.BackData) error {
    27  		var ids map[flow.Identifier]struct{}
    28  		entity, ok := backdata.ByID(key)
    29  		if !ok {
    30  			// no record with key is available in the mempool,
    31  			// initializes ids.
    32  			ids = make(map[flow.Identifier]struct{})
    33  		} else {
    34  			idMapEntity, ok := entity.(model.IdMapEntity)
    35  			if !ok {
    36  				return fmt.Errorf("could not assert entity to IdMapEntity")
    37  			}
    38  
    39  			ids = idMapEntity.IDs
    40  			if _, ok := ids[id]; ok {
    41  				// id is already associated with the key
    42  				// no need to append
    43  				return nil
    44  			}
    45  
    46  			// removes map entry associated with key for update
    47  			if _, removed := backdata.Remove(key); !removed {
    48  				return fmt.Errorf("potential race condition on removing from identifier map")
    49  			}
    50  		}
    51  
    52  		// appends id to the ids list
    53  		ids[id] = struct{}{}
    54  
    55  		// adds the new ids list associated with key to mempool
    56  		idMapEntity := model.IdMapEntity{
    57  			Key: key,
    58  			IDs: ids,
    59  		}
    60  
    61  		if added := backdata.Add(key, idMapEntity); !added {
    62  			return fmt.Errorf("potential race condition on adding to identifier map")
    63  		}
    64  
    65  		return nil
    66  	})
    67  }
    68  
    69  // Get returns list of all identifiers associated with key and true, if the key exists in the mempool.
    70  // Otherwise it returns nil and false.
    71  func (i *IdentifierMap) Get(key flow.Identifier) ([]flow.Identifier, bool) {
    72  	ids := make([]flow.Identifier, 0)
    73  	err := i.Run(func(backdata mempool.BackData) error {
    74  		entity, ok := backdata.ByID(key)
    75  		if !ok {
    76  			return fmt.Errorf("could not retrieve key from backend")
    77  		}
    78  
    79  		mapEntity, ok := entity.(model.IdMapEntity)
    80  		if !ok {
    81  			return fmt.Errorf("could not assert entity as IdMapEntity")
    82  		}
    83  
    84  		for id := range mapEntity.IDs {
    85  			ids = append(ids, id)
    86  		}
    87  
    88  		return nil
    89  	})
    90  
    91  	if err != nil {
    92  		return nil, false
    93  	}
    94  	return ids, true
    95  }
    96  
    97  // Has returns true if the key exists in the map, i.e., there is at least an id
    98  // attached to it.
    99  func (i *IdentifierMap) Has(key flow.Identifier) bool {
   100  	return i.Backend.Has(key)
   101  }
   102  
   103  // Remove removes the given key with all associated identifiers.
   104  func (i *IdentifierMap) Remove(key flow.Identifier) bool {
   105  	return i.Backend.Remove(key)
   106  }
   107  
   108  // RemoveIdFromKey removes the id from the list of identifiers associated with key.
   109  // If the list becomes empty, it also removes the key from the map.
   110  func (i *IdentifierMap) RemoveIdFromKey(key, id flow.Identifier) error {
   111  	err := i.Backend.Run(func(backdata mempool.BackData) error {
   112  		// var ids map[flow.Identifier]struct{}
   113  		entity, ok := backdata.ByID(key)
   114  		if !ok {
   115  			// entity key has already been removed
   116  			return nil
   117  		}
   118  
   119  		idMapEntity, ok := entity.(model.IdMapEntity)
   120  		if !ok {
   121  			return fmt.Errorf("could not assert entity to IdMapEntity")
   122  		}
   123  
   124  		if _, ok := idMapEntity.IDs[id]; !ok {
   125  			// id has already been removed from the key map
   126  			return nil
   127  		}
   128  
   129  		// removes map entry associated with key for update
   130  		if _, removed := backdata.Remove(key); !removed {
   131  			return fmt.Errorf("potential race condition on removing from identifier map")
   132  		}
   133  
   134  		// removes id from the secondary map of the key
   135  		delete(idMapEntity.IDs, id)
   136  
   137  		if len(idMapEntity.IDs) == 0 {
   138  			// all ids related to key are removed, so there is no need
   139  			// to add key back to the idMapEntity
   140  			return nil
   141  		}
   142  
   143  		// adds the new ids list associated with key to mempool
   144  		idMapEntity = model.IdMapEntity{
   145  			Key: key,
   146  			IDs: idMapEntity.IDs,
   147  		}
   148  
   149  		if added := backdata.Add(key, idMapEntity); !added {
   150  			return fmt.Errorf("potential race condition on adding to identifier map")
   151  		}
   152  
   153  		return nil
   154  	})
   155  
   156  	return err
   157  }
   158  
   159  // Size returns number of IdMapEntities in mempool
   160  func (i *IdentifierMap) Size() uint {
   161  	return i.Backend.Size()
   162  }
   163  
   164  // Keys returns a list of all keys in the mempool
   165  func (i *IdentifierMap) Keys() ([]flow.Identifier, bool) {
   166  	entities := i.Backend.All()
   167  	keys := make([]flow.Identifier, 0)
   168  	for _, entity := range entities {
   169  		idMapEntity, ok := entity.(model.IdMapEntity)
   170  		if !ok {
   171  			return nil, false
   172  		}
   173  		keys = append(keys, idMapEntity.Key)
   174  	}
   175  	return keys, true
   176  }