multi

package
v1.17.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 13, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

README

OpenFeature Multi-Provider

[!WARNING] The multi package for the go-sdk is experimental.

The multi-provider allows you to use multiple underlying providers as sources of flag data for the OpenFeature server SDK. The multi-provider acts as a wrapper providing a unified interface to interact with all of those providers at once. When a flag is being evaluated, the Multi-Provider will consult each underlying provider it is managing in order to determine the final result. Different evaluation strategies can be defined to control which providers get evaluated and which result is used.

The multi-provider is defined within Appendix A: Included Utilities of the openfeature spec.

The multi-provider is a powerful tool for performing migrations between flag providers, or combining multiple providers into a single feature flagging interface. For example:

  • Migration: When migrating between two providers, you can run both in parallel under a unified flagging interface. As flags are added to the new provider, the multi-provider will automatically find and return them, falling back to the old provider if the new provider does not have
  • Multiple Data Sources: The multi-provider allows you to seamlessly combine many sources of flagging data, such as environment variables, local files, database values and SaaS hosted feature management systems.

Usage

import (
	"github.com/open-feature/go-sdk/openfeature"
	"github.com/open-feature/go-sdk/openfeature/multi"
	"github.com/open-feature/go-sdk/openfeature/memprovider"
)

mprovider, err := multi.NewProvider(
  multi.StrategyFirstMatch,
  multi.WithProvider("providerA", memprovider.NewInMemoryProvider(/*...*/)),
  multi.WithProvider("providerB", myCustomProvider),
)
if err != nil {
  return err
}

openfeature.SetNamedProviderAndWait("multiprovider", mprovider)

Strategies

There are three strategies that are defined by the spec and are available within this multi-provider implementation. In addition to the three provided strategies a custom strategy can be defined as well.

The three provided strategies are:

  • First Match
  • First Success
  • Comparison

First Match Strategy

The first match strategy works by sequentially calling each provider until a valid result is returned. The first provider that returns a result will be used. It will try calling the next provider whenever it encounters a FLAG_NOT_FOUND error. However, if a provider returns an error other than FLAG_NOT_FOUND the provider will stop and return the default value along with setting the error details if a detailed request is issued.

First Success Strategy

The first success strategy also works by calling each provider sequentially. The first provider that returns a response with no errors is used. This differs from the first match strategy in that any provider raising an error will not halt calling the next provider if a successful result has not yet been encountered. If no provider provides a successful result the default value will be returned to the caller.

Comparison Strategy

The comparison strategy works by calling each provider in parallel. All results are collected from each provider and then the resolved results are compared to each other. If they all agree then that value is returned. If not a fallback provider can be specified to be executed instead or the default value will be returned. If a provider returns FLAG_NOT_FOUND that result will not be included in the comparison. If all providers return not found then the default value is returned. Finally, if any provider returns an error other than FLAG_NOT_FOUND the evaluation immediately stops and that error result is returned with the default value.

The fallback provider can be set using the WithFallbackProvider Option.

Special care must be taken when this strategy is used with ObjectEvaluation. If the resulting value is not a comparable type then the default result or fallback provider will always be used. In order to evaluate non comparable types a Comparator function must be provided as an Option to the constructor.

Custom Strategies

A custom strategy can be defined using the WithCustomStrategy Option along with the StrategyCustom constant. A custom strategy is defined by the following generic function signature:

StrategyFn[T FlagTypes] func(ctx context.Context, flag string, defaultValue T, flatCtx openfeature.FlattenedContext) openfeature.GenericResolutionDetail[T]

However, this doesn't provide any way to retrieve the providers! Therefore, there's the type StrategyConstructor that is called for you to close over the providers inside your StratetegyFn implementation.

type StrategyConstructor func(providers []*NamedProvider) StrategyFn[FlagTypes]

Build your strategy to wrap around the slice of providers

option := multi.WithCustomStrategy(func(providers []NamedProvider) StrategyFn[FlagTypes] {
	return func[T FlagTypes](ctx context.Context, flag string, defaultValue T, flatCtx openfeature.FlattenedContext) openfeature.GenericResolutionDetail[T] {
		// implementation
		// ...
    }
})

It is highly recommended to use the provided exposed functions to build your custom strategy. Specifically, the functions BuildDefaultResult & Evaluate are exposed for those implementing their own custom strategies.

The Evaluate method should be used for evaluating the result of a single NamedProvider. It determines the evaluation type via the type of the generic defaultVal parameter.

The BuildDefaultResult method should be called when an error is encountered or the strategy "fails" and needs to return the default result passed to one of the Evaluation methods of openfeature.FeatureProvider.

Options

The multi.NewProvider constructor implements the optional pattern for setting additional configuration.

General Options

WithLogger

Allows for providing a slog.Logger instance for internal logging of the multi-provider's evaluation processing for debugging purposes. By default, are logs are discarded unless this option is used.

WithCustomStrategy

Allows for setting a custom strategy function for the evaluation of providers. This must be used in conjunction with the StrategyCustom EvaluationStrategy parameter. The option itself takes a StrategyConstructor function, which is essentially a factory that allows the StrategyFn to wrap around a slice of NamedProvider instances.

WithGlobalHooks

Allows for setting global hooks for the multi-provider. These are openfeature.Hook implementations that affect all internal FeatureProvider instances.

WithProvider

Allows for registering a specific FeatureProvider instance under a unique provider name. Optional openfeature.Hook implementations may also be provided, which will execute only for this specific provider. This option can be used multiple times with unique provider names to register multiple providers. The order in which WithProvider options are provided determines the order in which the providers are registered and evaluated.

StrategyComparision specific options

There are two options specifically for usage with the StrategyComparision EvaluationStrategy. If these options are used with a different EvaluationStrategy they are ignored.

WithFallbackProvider

When the results are not in agreement with each other the fallback provider will be called. The result of this provider is what will be returned to the caller. If no fallback provider is set then the default value will be returned instead.

WithCustomComparator

When using ObjectEvaluation there are cases where the results are not able to be compared to each other by default. This happens if the returned type is not comparable. In that situation all the results are passed to the custom Comparator to evaluate if they are in agreement or not. If not provided and the return type is not comparable then either the fallback provider is used or the default value.

Documentation

Overview

Package multi is an experimental implementation of a of.FeatureProvider that supports evaluating multiple feature flag providers together.

Index

Constants

View Source
const (
	MetadataProviderName                   = "multiprovider-provider-name"
	MetadataProviderType                   = "multiprovider-provider-type"
	MetadataSuccessfulProviderName         = "multiprovider-successful-provider-name"
	MetadataSuccessfulProviderNames        = MetadataSuccessfulProviderName + "s"
	MetadataStrategyUsed                   = "multiprovider-strategy-used"
	MetadataFallbackUsed                   = "multiprovider-fallback-used"
	MetadataIsDefaultValue                 = "multiprovider-is-result-default-value"
	MetadataEvaluationError                = "multiprovider-evaluation-error"
	MetadataComparisonDisagreeingProviders = "multiprovider-comparison-disagreeing-providers"
)

Metadata Keys

View Source
const (
	// ReasonAggregated - the resolved value was the agreement of all providers in the multi.Provider using the
	// [StrategyComparison] strategy
	ReasonAggregated of.Reason = "AGGREGATED"
	// ReasonAggregatedFallback ReasonAggregated - the resolved value was result of the fallback provider because the
	// providers in multi.Provider were not in agreement using the [StrategyComparison] strategy.
	ReasonAggregatedFallback of.Reason = "AGGREGATED_FALLBACK"
)

Additional of.Reason options

Variables

View Source
var ErrAggregationNotAllowed = errors.New(errAggregationNotAllowedText)

ErrAggregationNotAllowed is an error returned if of.FeatureProvider.ObjectEvaluation is called using the StrategyComparison strategy without a custom Comparator function configured when response objects are not comparable.

Functions

func BuildDefaultResult

func BuildDefaultResult[R FlagTypes](strategy EvaluationStrategy, defaultValue R, err error) of.GenericResolutionDetail[R]

BuildDefaultResult should be called when a StrategyFn is in a failure state and needs to return a default value. This method will build a resolution detail with the internal provided error set. This method is exported for those writing their own custom StrategyFn.

func Evaluate

func Evaluate[T FlagTypes](ctx context.Context, provider NamedProvider, flag string, defaultVal T, flatCtx of.FlattenedContext) of.GenericResolutionDetail[T]

Evaluate is a generic method used to resolve a flag from a single NamedProvider without losing type information. This method is exported for those writing their own custom StrategyFn. Since any is an allowed FlagTypes this can be set to any type, but this should be done with care outside the specified primitive FlagTypes

Types

type AggregateError

type AggregateError []ProviderError

AggregateError is a map that contains up to one error per provider within the multiprovider.

func NewAggregateError

func NewAggregateError(providerErrors []ProviderError) AggregateError

NewAggregateError creates a new AggregateError from a slice of ProviderError instances

func (AggregateError) Error

func (ae AggregateError) Error() string

type Comparator

type Comparator func(values []any) bool

Comparator is used to compare the results of of.FeatureProvider.ObjectEvaluation. This is required if returned results are not comparable.

type EvaluationStrategy

type EvaluationStrategy = string

EvaluationStrategy Defines a strategy to use for resolving the result from multiple providers.

const (
	// StrategyFirstMatch returns the result of the first [of.FeatureProvider] whose response is not [of.FlagNotFoundCode].
	// This is executed sequentially, and not in parallel. Any returned errors from a provider other than flag not found
	// will result in a default response with a set error.
	StrategyFirstMatch EvaluationStrategy = "strategy-first-match"
	// StrategyFirstSuccess returns the result of the first [of.FeatureProvider] whose response that is not an error.
	// This is very similar to [StrategyFirstMatch], but does not raise errors. This is executed sequentially.
	StrategyFirstSuccess EvaluationStrategy = "strategy-first-success"
	// StrategyComparison returns a response of all [of.FeatureProvider] instances in agreement. All providers are
	// called in parallel and then the results of each non-error result are compared to each other. If all responses
	// agree, then that value will be returned. Otherwise, the value from the designated fallback [of.FeatureProvider]
	// instance's response will be returned. The fallback provider will be assigned to the first provider registered if
	// the [WithFallbackProvider] Option is not explicitly set.
	StrategyComparison EvaluationStrategy = "strategy-comparison"
	// StrategyCustom allows for using a custom [StrategyFn] implementation. If this is set you MUST use the WithCustomStrategy
	// Option to set it
	StrategyCustom EvaluationStrategy = "strategy-custom"
)

EvaluationStrategy options

type FlagTypes

type FlagTypes interface {
	int64 | float64 | string | bool | any
}

FlagTypes defines the types that can be used for flag values.

type NamedProvider

type NamedProvider interface {
	of.FeatureProvider
	// Name returns the unique name assigned to the provider.
	Name() string
}

NamedProvider extends of.FeatureProvider by adding a unique provider name.

type Option

type Option func(*configuration)

Option function used for setting configuration via the options pattern

func WithCustomComparator

func WithCustomComparator(comparator Comparator) Option

WithCustomComparator sets a custom Comparator to use when using StrategyComparison when of.FeatureProvider.ObjectEvaluation is performed. This is required if the returned objects are not comparable, otherwise an error occur..

func WithCustomStrategy

func WithCustomStrategy(s StrategyConstructor) Option

WithCustomStrategy sets a custom strategy function by defining a "constructor" that acts as closure over a slice of NamedProvider instances with your returned custom strategy function. This must be used in conjunction with StrategyCustom

func WithFallbackProvider

func WithFallbackProvider(p of.FeatureProvider) Option

WithFallbackProvider sets a fallback provider when using the StrategyComparison setting. The fallback provider is called when providers are not in agreement. If a fallback provider is not set and providers are not agreement, then the default result will be returned along with an error value.

func WithGlobalHooks

func WithGlobalHooks(hooks ...of.Hook) Option

WithGlobalHooks sets the global hooks for the provider. These are of.Hook instances that affect ALL of.FeatureProvider instances. To apply hooks to specific providers, attach them directly to that provider, or include them in the WithProvider Option if the provider does not support its own hook functionality.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets a logger to be used with slog for internal logging. By default, all logs are discarded unless this is set.

func WithProvider

func WithProvider(providerName string, provider of.FeatureProvider, hooks ...of.Hook) Option

WithProvider registers a specific of.FeatureProvider instance under the given providerName. The providerName must be unique and correspond to the name used when creating the Provider. Optional of.Hook instances may also be provided, which will execute only for this specific provider. This Option can be used multiple times with unique provider names to register multiple providers. The order in which options are provided determines the order in which the providers are registered and evaluated.

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider is an implementation of of.FeatureProvider that can execute multiple providers using various strategies.

func NewProvider

func NewProvider(evaluationStrategy EvaluationStrategy, options ...Option) (*Provider, error)

NewProvider returns a new multi.Provider that acts as a unified interface of multiple providers for interaction.

func (*Provider) BooleanEvaluation

func (p *Provider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, flatCtx of.FlattenedContext) of.BoolResolutionDetail

BooleanEvaluation evaluates the flag and returns a of.BoolResolutionDetail.

func (*Provider) EvaluationStrategy

func (p *Provider) EvaluationStrategy() string

EvaluationStrategy The name of the currently set EvaluationStrategy.

func (*Provider) EventChannel

func (p *Provider) EventChannel() <-chan of.Event

EventChannel is the channel that all events are emitted on.

func (*Provider) FloatEvaluation

func (p *Provider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, flatCtx of.FlattenedContext) of.FloatResolutionDetail

FloatEvaluation evaluates the flag and returns a of.FloatResolutionDetail.

func (*Provider) Hooks

func (p *Provider) Hooks() []of.Hook

Hooks returns a collection of.Hook instances configured to the provider using WithGlobalHooks.

func (*Provider) Init

func (p *Provider) Init(evalCtx of.EvaluationContext) error

Init will run the initialize method for all internal of.FeatureProvider instances and aggregate any errors.

func (*Provider) InitWithContext

func (p *Provider) InitWithContext(ctx context.Context, evalCtx of.EvaluationContext) error

InitWithContext will run the initialize method for all internal of.FeatureProvider instances and aggregate any errors.

func (*Provider) IntEvaluation

func (p *Provider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, flatCtx of.FlattenedContext) of.IntResolutionDetail

IntEvaluation evaluates the flag and returns an of.IntResolutionDetail.

func (*Provider) Metadata

func (p *Provider) Metadata() of.Metadata

Metadata provides the name "multiprovider" along with the names and types of each internal of.FeatureProvider.

func (*Provider) ObjectEvaluation

func (p *Provider) ObjectEvaluation(ctx context.Context, flag string, defaultValue any, flatCtx of.FlattenedContext) of.InterfaceResolutionDetail

ObjectEvaluation evaluates the flag and returns an of.InterfaceResolutionDetail. For the purposes of evaluation within strategies, the type of the default value is used as the assumed type of the returned responses from each provider. This is especially important when using the StrategyComparison configuration as an internal error will occur if this is not a comparable type unless the WithCustomComparator Option is configured.

func (*Provider) Providers

func (p *Provider) Providers() []NamedProvider

Providers returns slice of providers wrapped in NamedProvider structs.

func (*Provider) Shutdown

func (p *Provider) Shutdown()

Shutdown Shuts down all internal of.FeatureProvider instances and internal event listeners

func (*Provider) ShutdownWithContext

func (p *Provider) ShutdownWithContext(ctx context.Context) error

ShutdownWithContext shuts down all internal of.FeatureProvider instances and internal event listeners

func (*Provider) Status

func (p *Provider) Status() of.State

Status provides the current state of the multi.Provider.

func (*Provider) StringEvaluation

func (p *Provider) StringEvaluation(ctx context.Context, flag string, defaultValue string, flatCtx of.FlattenedContext) of.StringResolutionDetail

StringEvaluation evaluates the flag and returns a of.StringResolutionDetail.

func (*Provider) Track

func (p *Provider) Track(ctx context.Context, trackingEventName string, evaluationContext of.EvaluationContext, details of.TrackingEventDetails)

Track implements the of.Tracker interface by forwarding tracking calls to all internal providers that are in ready state and implement the of.Tracker interface.

type ProviderError

type ProviderError struct {

	// ProviderName is the name of the provider that returned the included error
	ProviderName string
	// contains filtered or unexported fields
}

ProviderError is an error wrapper that includes the provider name.

func (*ProviderError) Error

func (e *ProviderError) Error() string

Error implements the error interface for ProviderError.

func (*ProviderError) Unwrap

func (e *ProviderError) Unwrap() error

Unwrap allows access to the original error, if any.

type StrategyConstructor

type StrategyConstructor func(providers []NamedProvider) StrategyFn[FlagTypes]

StrategyConstructor defines the signature for the function that will be called to retrieve the closure that acts as the custom strategy implementation. This function should return a StrategyFn

type StrategyFn

type StrategyFn[T FlagTypes] func(ctx context.Context, flag string, defaultValue T, flatCtx of.FlattenedContext) of.GenericResolutionDetail[T]

StrategyFn defines the signature for a strategy function.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL