github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/routing/inmemory/inmemory.go (about)

     1  package inmemory
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	sync "github.com/bacalhau-project/golang-mutex-tracer"
     8  	"github.com/filecoin-project/bacalhau/pkg/model"
     9  	"github.com/filecoin-project/bacalhau/pkg/requester"
    10  	"github.com/filecoin-project/bacalhau/pkg/routing"
    11  	"github.com/libp2p/go-libp2p/core/peer"
    12  	"github.com/rs/zerolog/log"
    13  )
    14  
    15  // TODO: replace the manual and lazy eviction with a more efficient caching library
    16  type nodeInfoWrapper struct {
    17  	model.NodeInfo
    18  	evictAt time.Time
    19  }
    20  
    21  type NodeInfoStoreParams struct {
    22  	TTL time.Duration
    23  }
    24  
    25  type NodeInfoStore struct {
    26  	ttl             time.Duration
    27  	nodeInfoMap     map[peer.ID]nodeInfoWrapper
    28  	engineNodeIDMap map[model.Engine]map[peer.ID]struct{}
    29  	mu              sync.RWMutex
    30  }
    31  
    32  func NewNodeInfoStore(params NodeInfoStoreParams) *NodeInfoStore {
    33  	res := &NodeInfoStore{
    34  		ttl:             params.TTL,
    35  		nodeInfoMap:     make(map[peer.ID]nodeInfoWrapper),
    36  		engineNodeIDMap: make(map[model.Engine]map[peer.ID]struct{}),
    37  	}
    38  	res.mu.EnableTracerWithOpts(sync.Opts{
    39  		Threshold: 10 * time.Millisecond,
    40  		Id:        "InMemoryNodeInfoStore.mu",
    41  	})
    42  	return res
    43  }
    44  
    45  func (r *NodeInfoStore) Add(ctx context.Context, nodeInfo model.NodeInfo) error {
    46  	r.mu.Lock()
    47  	defer r.mu.Unlock()
    48  
    49  	// delete node from previous engines if it already exists to replace old engines with new ones if they've changed
    50  	existingNodeInfo, ok := r.nodeInfoMap[nodeInfo.PeerInfo.ID]
    51  	if ok {
    52  		for _, engine := range existingNodeInfo.ComputeNodeInfo.ExecutionEngines {
    53  			delete(r.engineNodeIDMap[engine], nodeInfo.PeerInfo.ID)
    54  		}
    55  	}
    56  
    57  	// TODO: use data structure that maintains nodes in descending order based on available capacity.
    58  	for _, engine := range nodeInfo.ComputeNodeInfo.ExecutionEngines {
    59  		if _, ok := r.engineNodeIDMap[engine]; !ok {
    60  			r.engineNodeIDMap[engine] = make(map[peer.ID]struct{})
    61  		}
    62  		r.engineNodeIDMap[engine][nodeInfo.PeerInfo.ID] = struct{}{}
    63  	}
    64  
    65  	// add or update the node info
    66  	r.nodeInfoMap[nodeInfo.PeerInfo.ID] = nodeInfoWrapper{
    67  		NodeInfo: nodeInfo,
    68  		evictAt:  time.Now().Add(r.ttl),
    69  	}
    70  
    71  	log.Ctx(ctx).Trace().Msgf("Added node info %+v", nodeInfo)
    72  	return nil
    73  }
    74  
    75  func (r *NodeInfoStore) Get(ctx context.Context, peerID peer.ID) (model.NodeInfo, error) {
    76  	r.mu.RLock()
    77  	defer r.mu.RUnlock()
    78  	infoWrapper, ok := r.nodeInfoMap[peerID]
    79  	if !ok {
    80  		return model.NodeInfo{}, requester.NewErrNodeNotFound(peerID)
    81  	}
    82  	if time.Now().After(infoWrapper.evictAt) {
    83  		go r.evict(ctx, infoWrapper)
    84  		return model.NodeInfo{}, requester.NewErrNodeNotFound(peerID)
    85  	}
    86  	return infoWrapper.NodeInfo, nil
    87  }
    88  
    89  func (r *NodeInfoStore) FindPeer(ctx context.Context, peerID peer.ID) (peer.AddrInfo, error) {
    90  	r.mu.RLock()
    91  	defer r.mu.RUnlock()
    92  	infoWrapper, ok := r.nodeInfoMap[peerID]
    93  	if !ok {
    94  		return peer.AddrInfo{}, nil
    95  	}
    96  	if len(infoWrapper.PeerInfo.Addrs) > 0 {
    97  		return infoWrapper.PeerInfo, nil
    98  	}
    99  	return peer.AddrInfo{}, nil
   100  }
   101  
   102  func (r *NodeInfoStore) List(ctx context.Context) ([]model.NodeInfo, error) {
   103  	r.mu.RLock()
   104  	defer r.mu.RUnlock()
   105  	var nodeInfos []model.NodeInfo
   106  	var toEvict []nodeInfoWrapper
   107  	for _, nodeInfo := range r.nodeInfoMap {
   108  		if time.Now().After(nodeInfo.evictAt) {
   109  			toEvict = append(toEvict, nodeInfo)
   110  		} else {
   111  			nodeInfos = append(nodeInfos, nodeInfo.NodeInfo)
   112  		}
   113  	}
   114  	if len(toEvict) > 0 {
   115  		go r.evict(ctx, toEvict...)
   116  	}
   117  	return nodeInfos, nil
   118  }
   119  
   120  func (r *NodeInfoStore) ListForEngine(ctx context.Context, engine model.Engine) ([]model.NodeInfo, error) {
   121  	r.mu.RLock()
   122  	defer r.mu.RUnlock()
   123  	var nodeInfos []model.NodeInfo
   124  	var toEvict []nodeInfoWrapper
   125  	for nodeID := range r.engineNodeIDMap[engine] {
   126  		nodeInfo := r.nodeInfoMap[nodeID]
   127  		if time.Now().After(nodeInfo.evictAt) {
   128  			toEvict = append(toEvict, nodeInfo)
   129  		} else {
   130  			nodeInfos = append(nodeInfos, nodeInfo.NodeInfo)
   131  		}
   132  	}
   133  	if len(toEvict) > 0 {
   134  		go r.evict(ctx, toEvict...)
   135  	}
   136  	return nodeInfos, nil
   137  }
   138  
   139  func (r *NodeInfoStore) Delete(ctx context.Context, peerID peer.ID) error {
   140  	r.mu.Lock()
   141  	defer r.mu.Unlock()
   142  	return r.doDelete(ctx, peerID)
   143  }
   144  
   145  func (r *NodeInfoStore) evict(ctx context.Context, infoWrappers ...nodeInfoWrapper) {
   146  	r.mu.Lock()
   147  	defer r.mu.Unlock()
   148  	for _, infoWrapper := range infoWrappers {
   149  		nodeInfo, ok := r.nodeInfoMap[infoWrapper.PeerInfo.ID]
   150  		if !ok || nodeInfo.evictAt != infoWrapper.evictAt {
   151  			return // node info already evicted or has been updated since it was scheduled for eviction
   152  		}
   153  		err := r.doDelete(ctx, infoWrapper.PeerInfo.ID)
   154  		if err != nil {
   155  			log.Ctx(ctx).Warn().Err(err).Msgf("Failed to evict expired node info for peer %s", infoWrapper.PeerInfo.ID)
   156  		}
   157  	}
   158  }
   159  
   160  func (r *NodeInfoStore) doDelete(ctx context.Context, peerID peer.ID) error {
   161  	nodeInfo, ok := r.nodeInfoMap[peerID]
   162  	if !ok {
   163  		return nil
   164  	}
   165  	for _, engine := range nodeInfo.ComputeNodeInfo.ExecutionEngines {
   166  		delete(r.engineNodeIDMap[engine], peerID)
   167  	}
   168  	delete(r.nodeInfoMap, peerID)
   169  	return nil
   170  }
   171  
   172  // compile time check that we implement the interface
   173  var _ routing.NodeInfoStore = (*NodeInfoStore)(nil)