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 }