github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/model/v1beta1/provider.go (about)

     1  package v1beta1
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/filecoin-project/bacalhau/pkg/util/generic"
     8  )
     9  
    10  // A ProviderKey will usually be some lookup value implemented as an enum member
    11  type ProviderKey interface {
    12  	fmt.Stringer
    13  	comparable
    14  }
    15  
    16  // A Providable is something that a Provider can check for installation status
    17  type Providable interface {
    18  	IsInstalled(context.Context) (bool, error)
    19  }
    20  
    21  // A Provider is an object which is configured to provide certain objects and
    22  // will check their installation status before providing them
    23  type Provider[Key ProviderKey, Value Providable] interface {
    24  	Get(context.Context, Key) (Value, error)
    25  	Has(context.Context, Key) bool
    26  }
    27  
    28  // A MappedProvider is a Provider that stores the providables in a simple map,
    29  // and caches permanently the results of checking installation status
    30  type MappedProvider[Key ProviderKey, Value Providable] struct {
    31  	// We can't use a mutex here because it's possible that calling IsInstalled
    32  	// on a Providable might result in calling Get on this Provider (e.g. if the
    33  	// Providable is implemented by composing together other Providables). So we
    34  	// use SyncMaps instead, at the cost of a safe but inefficient race for
    35  	// filling up the installed cache.
    36  	providables    *generic.SyncMap[Key, Value]
    37  	installedCache *generic.SyncMap[Key, bool]
    38  }
    39  
    40  func (provider *MappedProvider[Key, Value]) Add(key Key, value Value) {
    41  	provider.providables.Put(key, value)
    42  }
    43  
    44  // Get implements Provider
    45  func (provider *MappedProvider[Key, Value]) Get(ctx context.Context, key Key) (v Value, err error) {
    46  	providable, ok := provider.providables.Get(key)
    47  	if !ok {
    48  		return v, fmt.Errorf("no matching %T found on this server: %s", key, key)
    49  	}
    50  
    51  	// cache it being installed so we're not hammering it. TODO: we should evict
    52  	// the cache in case an installed providable gets uninstalled, or vice versa
    53  	installed, ok := provider.installedCache.Get(key)
    54  	if !ok {
    55  		installed, err = providable.IsInstalled(ctx)
    56  		if err != nil {
    57  			return v, err
    58  		}
    59  		provider.installedCache.Put(key, installed)
    60  	}
    61  
    62  	if !installed {
    63  		return v, fmt.Errorf("%T is not installed: %s", key, key)
    64  	}
    65  
    66  	return providable, nil
    67  }
    68  
    69  // Has implements Provider
    70  func (provider *MappedProvider[Key, Value]) Has(ctx context.Context, key Key) bool {
    71  	_, err := provider.Get(ctx, key)
    72  	return err == nil
    73  }
    74  
    75  func NewMappedProvider[Key ProviderKey, Value Providable](providables map[Key]Value) *MappedProvider[Key, Value] {
    76  	return &MappedProvider[Key, Value]{
    77  		providables:    generic.SyncMapFromMap(providables),
    78  		installedCache: &generic.SyncMap[Key, bool]{},
    79  	}
    80  }
    81  
    82  // A NoopProvider is a provider that always returns a singleton providable
    83  // regardless of requested type
    84  type NoopProvider[Key ProviderKey, Value Providable] struct {
    85  	noopProvidable Value
    86  }
    87  
    88  // Get implements Provider
    89  func (p *NoopProvider[Key, Value]) Get(context.Context, Key) (Value, error) {
    90  	return p.noopProvidable, nil
    91  }
    92  
    93  // Has implements Provider
    94  func (p *NoopProvider[Key, Value]) Has(context.Context, Key) bool {
    95  	return true
    96  }
    97  
    98  func NewNoopProvider[Key ProviderKey, Value Providable](noopProvidable Value) Provider[Key, Value] {
    99  	return &NoopProvider[Key, Value]{noopProvidable: noopProvidable}
   100  }