experiments

package
v0.9.17 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: BSD-3-Clause Imports: 16 Imported by: 1

Documentation

Overview

Package experiments provides implementation of experiments config parsing and other experiments related features according to baseplate spec.

For a starting point, see Experiments type and NewExperiments function,

Package experiments has been deprecated in favor of reddit-go/decider internal package. Use Choose() in lieu of Variant(), which also enables optional auto-exposure.

Deprecated: baseplate.go/experiments is deprecated. Instead, use reddit-go/decider (internal).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AllNode

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

AllNode evaluates to true if all child nodes returns true.

func (*AllNode) Evaluate

func (n *AllNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns true if all child nodes returns true.

type AnyNode

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

AnyNode evaluates to true if at least one child node returns true.

func (*AnyNode) Evaluate

func (n *AnyNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns true if at least one child node returns true.

type ComparisonNode

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

ComparisonNode is a non-equality comparison operators (gt, ge, lt, le).

Expects as input the input node as well as an operator (from the operator module). Operator must be one that expects two inputs ( ie: gt, ge, lt, le, eq, ne).

func NewComparisonNode

func NewComparisonNode(inputs map[string]interface{}, comparer less) (*ComparisonNode, error)

NewComparisonNode parses the underlying input into an ComparisonNode.

func (*ComparisonNode) Evaluate

func (n *ComparisonNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns true if the comparison holds true and false otherwise.

type EqualNode

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

EqualNode is used to determine whether an attribute equals a single value or a value in a list.

A full EqualNode in a targeting tree configuration looks like this:

{
   EQ: {
        field: <field_name>
        value: <accepted_value>
    }
}

The expected input to this constructor from the above example would be::

{
    field: <field_name>,
    value: <accepted_value>
}

func (*EqualNode) Evaluate

func (n *EqualNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns true if the given attribute has the expected value.

type EventLogger

type EventLogger interface {
	Log(ctx context.Context, event ExperimentEvent) error
}

EventLogger provides an interface for experiment events to be logged.

type Experiment

type Experiment struct {
	ExperimentVersion int                          `json:"experiment_version"`
	ShuffleVersion    int                          `json:"shuffle_version"`
	BucketVal         string                       `json:"bucket_val"`
	Variants          []Variant                    `json:"variants"`
	BucketSeed        string                       `json:"bucket_seed"`
	Targeting         json.RawMessage              `json:"targeting"`
	Overrides         []map[string]json.RawMessage `json:"overrides"`
}

Experiment represents the experiment and configures the available variants.

type ExperimentConfig

type ExperimentConfig struct {
	// ID is the experiment identifier and should be unique for each experiment.
	ID int `json:"id"`
	// Name is the experiment name and should be unique for each experiment.
	Name string `json:"name"`
	// Owner is the group or individual that owns this experiment.
	Owner string `json:"owner"`
	// Enabled if set to false will disable the experiment and calls to Variant
	// will always returns an empty string.
	Enabled *bool `json:"enabled"`
	// Version is the string to identify the specific version of the
	// experiment.
	Version string `json:"version"`
	// Type specifies the type of experiment to run. If this value is not
	// recognized, the experiment will be considered disabled.
	Type string `json:"type"`
	// StartTimestamp is a float of seconds since the epoch of date and time
	// when you want the experiment to start. If an experiment has not been
	// started yet, it is considered disabled.
	StartTimestamp timebp.TimestampSecondF `json:"start_ts"`
	// StopTimestamp is a float of seconds since the epoch of date and time when
	// you want the experiment to stop. Once an experiment is stopped, it is
	// considered disabled.
	StopTimestamp timebp.TimestampSecondF `json:"stop_ts"`
	// Experiment is the specific experiment.
	Experiment Experiment `json:"experiment"`
}

ExperimentConfig holds the information for the experiment plus additional data around the experiment.

type ExperimentEvent

type ExperimentEvent struct {
	// ID uniquely identifies the experiment event. If you pass in uuid.Nil the
	// logger handling this event should generate a UUID v4 (optional).
	ID uuid.UUID
	// CorrelationID are used to track events across different services (optional).
	CorrelationID uuid.UUID
	// DeviceID unique identifies the device this experiment is being logged
	// from (optional).
	DeviceID uuid.UUID
	// Experiment is the experiment of the applied treatment (required).
	Experiment *ExperimentConfig
	// VariantName is the type of bucket that is being applied (required).
	VariantName string
	// UserID identifies the user who is being exposed to the experimental
	// treatment (optional).
	UserID string
	// LoggedIn indiciates whether the user is authenticated (optional).
	LoggedIn *bool
	// CookieCreatedAt is the timestamp when the cookie for the user has been
	// generated (optional).
	CookieCreatedAt time.Time
	// OAuthClientID is the id of the OAuth client (optional).
	OAuthClientID string
	// ClientTimestamp is the time when the experiment has been applied. If
	// this is not provided the logger should generate a timestamp (optional).
	ClientTimestamp time.Time
	// AppName if any specifies the application (optional).
	AppName string
	// SessionID is the id of the session (optional).
	SessionID string
	// IsOverride should be true if the variant shown was due to an override
	// rather than bucketing (required).
	IsOverride bool
	// EventType is the type of the experiment event. Will be set to EXPOSE
	// (optional).
	EventType string
}

ExperimentEvent is the playload used by Expose to log whether a user has been exposed to an experimental treatment.

type Experiments

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

Experiments offers access to the experiment framework with automatic refresh when there are change.

This experiments client allows access to the experiments cached on disk by the experiment configuration fetcher daemon. It will automatically reload the cache when changed.

func NewExperiments

func NewExperiments(ctx context.Context, path string, eventLogger EventLogger, logger log.Wrapper) (*Experiments, error)

NewExperiments returns a new instance of the experiments clients. The path points to the experiments file that will be parsed.

Context should come with a timeout otherwise this might block forever, i.e. if the path never becomes available.

func (*Experiments) Expose

func (e *Experiments) Expose(ctx context.Context, experimentName string, event ExperimentEvent) error

Expose logs an event to indicate that a user has been exposed to an experimental treatment.

func (*Experiments) Variant

func (e *Experiments) Variant(name string, args map[string]interface{}, bucketingEventOverride bool) (string, error)

Variant determines the variant, if any, of this experiment is active.

All arguments needed for bucketing, targeting, and variant overrides should be passed in as arguments. The parameter names are determined by the specific implementation of the Experiment interface.

Returns the name of the enabled variant as a string if any variant is enabled. If no variant is enabled returns an empty string.

This function might return MissingBucketKeyError as the error. Caller usually want to check for that and handle it differently from other errors. See its documentation for more details.

type MissingBucketKeyError added in v0.3.1

type MissingBucketKeyError struct {
	ExperimentName string
	ArgsKey        string
}

MissingBucketKeyError is a special error returned by Variant functions, to indicate that the bucket key from the args map is missing.

This error is usually considered "normal", the caller might still want to log it, but usually don't need to send it to sentry.

func (MissingBucketKeyError) Error added in v0.3.1

func (e MissingBucketKeyError) Error() string

type MultiVariantSet

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

MultiVariantSet is designed to handle more than two total treatments.

MultiVariantSets are not designed to support changes in variant sizes without rebucketing.

func NewMultiVariantSet

func NewMultiVariantSet(variants []Variant, buckets int) (*MultiVariantSet, error)

NewMultiVariantSet returns a new instance of MultiVariantSet based on the given variants and number of buckets.

func (*MultiVariantSet) ChooseVariant

func (v *MultiVariantSet) ChooseVariant(bucket int) string

ChooseVariant deterministically chooses a variant. Every call with the same bucket on one instance will result in the same answer.

type NotNode

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

NotNode is a boolean 'not' operator and negates the child node.

func NewNotNode

func NewNotNode(inputNodes map[string]interface{}) (*NotNode, error)

NewNotNode parses the underlying input into an NotNode.

func (*NotNode) Evaluate

func (n *NotNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns the negation of the child's evaluation.

type OverrideNode

type OverrideNode struct {
	ReturnValue bool
}

OverrideNode is an override to the targeting and can always return true or false.

func NewOverrideNode

func NewOverrideNode(inputNode interface{}) *OverrideNode

NewOverrideNode parses the underlying input into an OverrideNode.

func (*OverrideNode) Evaluate

func (n *OverrideNode) Evaluate(inputs map[string]interface{}) bool

Evaluate returns the configured boolean return value.

type RangeVariantSet

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

RangeVariantSet is designed to take fixed bucket ranges.

This VariantSet allows manually setting bucketing ranges. It takes in a variant name, then the range of buckets in that should be assigned to that variant. This enables user-defined bucketing algorithms, as well as simplifies the ability to adjust range sizes in special circumstances.

func NewRangeVariantSet

func NewRangeVariantSet(variants []Variant, buckets int) (*RangeVariantSet, error)

NewRangeVariantSet returns a new instance of RangeVariantSet based on the given variants and number of buckets.

func (*RangeVariantSet) ChooseVariant

func (v *RangeVariantSet) ChooseVariant(bucket int) string

ChooseVariant deterministically choose a variant. Every call with the same bucket on one instance will result in the same answer

type RolloutVariantSet

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

RolloutVariantSet is designed for feature rollouts and takes a single variant.

Changing the size of the variant will minimize the treatment of bucketed users. Those users going from no treatment to the provided treatment (or vice versa) are limited to the change in the provided treatment size. For instance, going from 45% to 55% will result in only the new 10% of users changing treatments. The initial 45% will not change. Conversely, going from 55% to 45% will result in only 10% of users losing the treatment.

func NewRolloutVariantSet

func NewRolloutVariantSet(variants []Variant, buckets int) (*RolloutVariantSet, error)

NewRolloutVariantSet returns a new instance of RolloutVariantSet based on the given variants and number of buckets.

func (*RolloutVariantSet) ChooseVariant

func (v *RolloutVariantSet) ChooseVariant(bucket int) string

ChooseVariant deterministically choose a percentage-based variant. Every call with the same bucket and variants will result in the same answer.

type SimpleExperiment

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

SimpleExperiment is a basic experiment choosing from a set of variants.

func NewSimpleExperiment

func NewSimpleExperiment(experiment *ExperimentConfig) (*SimpleExperiment, error)

NewSimpleExperiment returns a new instance of SimpleExperiment. Default values if not otherwise provided by the ExperimentConfig will be assumed.

func (*SimpleExperiment) UniqueID

func (e *SimpleExperiment) UniqueID(bucketVals map[string]string) string

UniqueID returns a unique ID for the experiment.

func (*SimpleExperiment) Variant

func (e *SimpleExperiment) Variant(args map[string]interface{}) (string, error)

Variant determines the variant, if any, is active. Bucket calculation is determined based on the bucketVal.

This function might return MissingBucketKeyError as the error. Caller usually want to check for that and handle it differently from other errors. See its documentation for more details.

type SingleVariantSet

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

SingleVariantSet Variant Set designed to handle two total treatments.

This variant set allows adjusting the sizes of variants without changing treatments, where possible. When not possible (eg: switching from a 60/40 distribution to a 40/60 distribution), this will minimize changing treatments (in the above case, only those buckets between the 40th and 60th percentile of the bucketing range will see a change in treatment).

func NewSingleVariantSet

func NewSingleVariantSet(variants []Variant, buckets int) (*SingleVariantSet, error)

NewSingleVariantSet returns a new instance of SingleVariantSet based on the given variants and number of buckets.

func (*SingleVariantSet) ChooseVariant

func (v *SingleVariantSet) ChooseVariant(bucket int) string

ChooseVariant deterministically chooses a variant. Every call with the same bucket on one instance will result in the same answer.

type Targeting

type Targeting interface {
	Evaluate(inputs map[string]interface{}) bool
}

Targeting is the common interface to implement experiment targeting. Evaluated whether the provided input matches the expected values.

func NewAllNode

func NewAllNode(input interface{}) (Targeting, error)

NewAllNode parses the underlying input into an AllNode.

func NewAnyNode

func NewAnyNode(input interface{}) (Targeting, error)

NewAnyNode parses the underlying input into an AnyNode.

func NewEqualNode

func NewEqualNode(inputNodes map[string]interface{}) (Targeting, error)

NewEqualNode parses the underlying input into an EqualNode.

func NewTargeting

func NewTargeting(targetingConfig []byte) (Targeting, error)

NewTargeting parses the given targeting configuration into a Targeting.

type TargetingNodeError

type TargetingNodeError string

TargetingNodeError is returned when there was an inconsistency in the targeting due to operator mismatch or violation of their properties in the input.

func (TargetingNodeError) Error

func (cause TargetingNodeError) Error() string

type UnknownExperimentError

type UnknownExperimentError string

UnknownExperimentError is returned if the configured experiment is not known.

func (UnknownExperimentError) Error

func (name UnknownExperimentError) Error() string

type UnknownTargetingOperatorError

type UnknownTargetingOperatorError string

UnknownTargetingOperatorError is returned when the parsed operator is not known.

func (UnknownTargetingOperatorError) Error

func (operator UnknownTargetingOperatorError) Error() string

type Variant

type Variant struct {
	Name       string  `json:"name"`
	Size       float64 `json:"size"`
	RangeStart float64 `json:"range_start"`
	RangeEnd   float64 `json:"range_end"`
}

Variant is a single variant that belongs to a set of variants and determines a bucket by name and size. Either size is set or range start and range end.

type VariantSet

type VariantSet interface {
	ChooseVariant(bucket int) string
	// contains filtered or unexported methods
}

VariantSet is the base interface for variant sets. A variant set contains a set of experimental variants, as well as their distributions. It is used by experiments to track which bucket a variant is assigned to.

func FromExperimentType

func FromExperimentType(experimentType string, variants []Variant, buckets int) (VariantSet, error)

FromExperimentType maps the experimentType to a concrete type implementing VariantSet and returns an error for any unknown type.

type VariantValidationError

type VariantValidationError string

VariantValidationError is used when the provided variants are not consistent with the chosen variant set.

func (VariantValidationError) Error

func (cause VariantValidationError) Error() string

Jump to

Keyboard shortcuts

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