multi

package
v2.0.0-...-55f9335 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: 13 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 (
 "go.openfeature.dev/openfeature/v2"
 "go.openfeature.dev/openfeature/v2/providers/inmemory"
 "go.openfeature.dev/openfeature/v2/providers/multi"
)

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

openfeature.SetNamedProviderAndWait(context.TODO(), "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(resolutions ResolutionIterator[T], defaultValue T, fallbackEvaluator FallbackEvaluator[T]) openfeature.GenericResolutionDetail[T]

Where:

ResolutionIterator[T FlagTypes] = iter.Seq2[string, openfeature.GenericResolutionDetail[T]]
FallbackEvaluator[T FlagTypes] = func(fallbackProvider openfeature.FeatureProvider) openfeature.GenericResolutionDetail[T]

The strategy function receives:

  • resolutions: An iterator of provider names and their resolution results
  • defaultValue: The default value to return if strategy fails
  • fallbackEvaluator: A function to evaluate the fallback provider if needed

The StrategyConstructor type is used to create your custom strategy:

type StrategyConstructor func() StrategyFn[FlagTypes]

Build your custom strategy like this:

option := multi.WithCustomStrategy(func() StrategyFn[openfeature.FlagTypes] {
 return func(resolutions ResolutionIterator[openfeature.FlagTypes], defaultValue openfeature.FlagTypes, fallbackEvaluator FallbackEvaluator[openfeature.FlagTypes]) *openfeature.GenericResolutionDetail[openfeature.FlagTypes] {
  // Iterate through provider resolutions
  for name, resolution := range resolutions {
   // Your custom logic here
   // ...
  }
  // Return selected resolution or use fallbackEvaluator if needed
  return resolution
    }
})

It is highly recommended to use the provided exposed function BuildDefaultResult when building your custom strategy.

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 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.
	ErrAggregationNotAllowed = errors.New(errAggregationNotAllowedText)
)

Functions

func BuildDefaultResult

func BuildDefaultResult[R of.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.

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 FallbackEvaluator

type FallbackEvaluator[T of.FlagTypes] = func(fallbackProvider of.FeatureProvider) *of.GenericResolutionDetail[T]

FallbackEvaluator evaluates the fallback provider when the strategy needs it.

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.

func WithRunModeParallel

func WithRunModeParallel() Option

WithRunModeParallel configures the run mode to evaluate providers in parallel.

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(ctx context.Context) error

Init 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.ObjectResolutionDetail

ObjectEvaluation evaluates the flag and returns an of.ObjectResolutionDetail. 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(ctx context.Context) error

Shutdown 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 ResolutionIterator

type ResolutionIterator[T of.FlagTypes] = iter.Seq2[string, *of.GenericResolutionDetail[T]]

ResolutionIterator provides an iterator over provider names and their resolution results.

type StrategyConstructor

type StrategyConstructor func() StrategyFn[of.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 of.FlagTypes] func(resolutions ResolutionIterator[T], defaultValue T, fallbackEvaluator FallbackEvaluator[T]) *of.GenericResolutionDetail[T]

StrategyFn defines the signature for a strategy function that processes resolutions from multiple providers. Parameters:

  • resolutions: An iterator of provider names and their resolution results
  • defaultValue: The default value to return if strategy fails
  • fallbackEvaluator: A function to evaluate the fallback provider if needed

Returns: The final resolution detail selected by the strategy

Jump to

Keyboard shortcuts

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