featbit

package module
v1.1.2 Latest Latest
Warning

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

Go to latest
Published: May 15, 2023 License: MIT Imports: 17 Imported by: 0

README

FeatBit Server-Side SDK for Go

Introduction

This is the Go Server-Side SDK for the 100% open-source feature flags management platform FeatBit.

The FeatBit Server-Side SDK for Go is designed primarily for use in multi-user systems such as web servers and applications.

Data synchronization

We use websocket to make the local data synchronized with the FeatBit server, and then store them in memory by default. Whenever there is any change to a feature flag or its related data, this change will be pushed to the SDK and the average synchronization time is less than 100 ms. Be aware the websocket connection may be interrupted due to internet outage, but it will be resumed automatically once the problem is gone.

If you want to use your own data source, see Offline Mode.

Get Started

Go Server Side SDK is based on go 1.13, so you need to install go 1.13 or above.

Installation
go get github.com/featbit/featbit-go-sdk
Quick Start

Note that the envSecret, streamUrl and eventUrl are required to initialize the SDK.

The following code demonstrates basic usage of the SDK.

package main

import (
	"fmt"
	"github.com/featbit/featbit-go-sdk"
	"github.com/featbit/featbit-go-sdk/interfaces"
)

func main() {
	envSecret := "<replace-with-your-env-secret>"
	streamingUrl := "ws://localhost:5100"
	eventUrl := "http://localhost:5100"

	client, err := featbit.NewFBClient(envSecret, streamingUrl, eventUrl)

	defer func() {
		if client != nil {
			// ensure that the SDK shuts down cleanly and has a chance to deliver events to FeatBit before the program exits
			_ = client.Close()
		}
	}()

	if err == nil && client.IsInitialized() {
		user, _ := interfaces.NewUserBuilder("<replace-with-your-user-key>").UserName("<replace-with-your-user-name>").Build()
		_, ed, _ := client.BoolVariation("<replace-with-your-feature-flag-key>", user, false)
		fmt.Printf("flag %s, returns %s for user %s, reason: %s \n", ed.KeyName, ed.Variation, user.GetKey(), ed.Reason)
	} else {
		fmt.Println("SDK initialization failed")
    }
}
Examples
FBClient

Applications SHOULD instantiate a single FBClient instance for the lifetime of the application. In the case where an application needs to evaluate feature flags from different environments, you may create multiple clients, but they should still be retained for the lifetime of the application rather than created per request or per thread.

Bootstrapping

The bootstrapping is in fact the call of constructor of featbit.FBClient, in which the SDK will be initialized, using streaming from your feature management platform.

The constructor will return when it successfully connects, or when the timeout set by featbit.FBConfig.StartWait(default: 15 seconds) expires, whichever comes first. If it has not succeeded in connecting when the timeout elapses, you will receive the client in an uninitialized state where feature flags will return default values; it will still continue trying to connect in the background unless there has been an net.DNSError or you close the client. You can detect whether initialization has succeeded by calling featbit.FBClient.IsInitialized().

If featbit.FBClient.IsInitialized() returns True, it means the featbit.FBClient has succeeded at some point in connecting to feature flag center, otherwise client has not yet connected to feature flag center, or has permanently failed. In this state, feature flag evaluations will always return default values.

config := featbit.FBConfig{StartWait: 10 * time.Second}
// DO NOT forget to close the client when you don't need it anymore
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)
if err == nil && client.IsInitialized() {
    // the client is ready
}

If you prefer to have the constructor return immediately, and then wait for initialization to finish at some other point, you can use featbit.FBClient.GetDataUpdateStatusProvider(), which will return an implementation of interfaces.DataUpdateStatusProvider. This interface has a WaitForOKState method that will block until the client has successfully connected, or until the timeout expires.

config := featbit.FBConfig{StartWait: 0}
// DO NOT forget to close the client when you don't need it anymore
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)
if err != nil {
    return
}
ok := client.GetDataSourceStatusProvider().WaitForOKState(10 * time.Second)
if ok {
    // the client is ready
}

To check if the client is ready is optional. Even if the client is not ready, you can still evaluate feature flags, but the default value will be returned if SDK is not yet initialized.

FBConfig and Components

In most cases, you don't need to care about featbit.FBConfig and the internal components, just initialize SDK like:

client, err := featbit.NewFBClient(envSecret, streamingUrl, eventUrl)

envSecret sdkKey(envSecret) is id of your project in FeatBit feature flag center

streamingURL: URL of your feature management platform to synchronize feature flags, user segments, etc.

eventURL: URL of your feature management platform to send analytics events

StartWait: how long the constructor will block awaiting a successful data sync. Setting this to a zero or negative duration will not block and cause the constructor to return immediately.

Offline: Set whether SDK is offline. when set to true no connection to your feature management platform anymore

featbit.FBConfig provides advanced configuration options for setting the SDK component, or you want to customize the behavior of build-in components.

NetworkFactory: sets the SDK networking configuration, DO NOT change it unless you should set some advanced configuration such as HTTP Proxy, TLS etc.

factories.NetworkBuilder is the default NetworkFactory

factory := factories.NewNetworkBuilder()
factory.ProxyUrl("http://username:password@146.137.9.45:65233")

config := featbit.DefaultFBConfig
config.NetworkFactory = factory
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, *config)
// or
config := featbit.FBConfig{NetworkFactory: factory}
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)

DataStorageFactory sets the implementation of interfaces.DataStorage to be used for holding feature flags and related data received from feature flag center SDK sets the implementation of the data storage, in using factories.InMemoryStorageBuilder by default to instantiate a memory data storage. Developers can customize the data storage to persist received data in redis, mongodb, etc.

DataSynchronizerFactory SDK sets the implementation of the interfaces.DataSynchronizer that receives feature flag data from feature flag center, in using factories.StreamingBuilder by default If Developers would like to know what the implementation is, they can read the GoDoc and source code.

InsightProcessorFactory SDK which sets the implementation of interfaces.InsightProcessor to be used for processing analytics events. using a factory object. The default is factories.InsightProcessorBuilder. If Developers would like to know what the implementation is, they can read the GoDoc and source code.

It's not recommended to change the default factories in the featbit.FBConfig

FBUser

A collection of attributes that can affect flag evaluation, usually corresponding to a user of your application. This object contains built-in properties(key, userName). The key and userName are required. The key must uniquely identify each user; this could be a username or email address for authenticated users, or an ID for anonymous users. The userName is used to search your user quickly. You may also define custom properties with arbitrary names and values.

// FBUser creation
user, err := NewUserBuilder("key").UserName("name").Custom("property", "value").Build()
Evaluation

SDK calculates the value of a feature flag for a given user, and returns a flag value and interfaces.EvalDetail that describes the way that the value was determined.

SDK will initialize all the related data(feature flags, segments etc.) in the bootstrapping and receive the data updates in real time, as mentioned in Bootstrapping.

After initialization, the SDK has all the feature flags in the memory and all evaluation is done locally and synchronously, the average evaluation time is < 10 ms.

SDK supports String, Boolean, and Number and Json as the return type of flag values:

  • Variation(for string)
  • BoolVariation
  • IntVariation
  • DoubleVariation
  • JsonVariation
// be sure that SDK is initialized before evaluation
// DO not forget to close client when you are done with it
if client.isInitialized() {
    // Flag value
    // returns a string variation
    variation, detail, _ := client.Variation("flag key", user, "Not Found")
}

featbit.FBClient.AllLatestFlagsVariations(user) returns all variations for a given user. You can retrieve the flag value or details for a specific flag key:

  • GetStringVariation
  • GetBoolVariation
  • GetIntVariation
  • GetDoubleVariation
  • GetJsonVariation
// be sure that SDK is initialized before evaluation
// DO not forget to close client when you are done with it
if client.isInitialized() {
    // get all variations for a given user in your project 
    allState, _ := client.AllLatestFlagsVariations(user)
    variation, detail, _ := allState.GetStringVariation("flag key", "Not Found")
}

Note that if evaluation called before Go SDK client initialized, you set the wrong flag key/user for the evaluation or the related feature flag is not found, SDK will return the default value you set. interfaces.EvalDetail will explain the details of the latest evaluation including error raison.

Offline Mode

In some situations, you might want to stop making remote calls to FeatBit. Here is how:

config := featbit.DefaultFBConfig
featbit.Offline = true
featbit.StartWait = 1 * time.Millisecond
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, *config)
// or
config := FBConfig{Offline: true, StartWait: 1 * time.Millisecond}
client, err := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)

When you put the SDK in offline mode, no insight message is sent to the server and all feature flag evaluations return fallback values because there are no feature flags or segments available. If you want to use your own data source, SDK allows users to populate feature flags and segments data from a JSON string. Here is an example: fbclient_test_data.json.

The format of the data in flags and segments is defined by FeatBit and is subject to change. Rather than trying to construct these objects yourself, it's simpler to request existing flags directly from the FeatBit server in JSON format and use this output as the starting point for your file. Here's how:

# replace http://localhost:5100 with your evaluation server url
curl -H "Authorization: <your-env-secret>" http://localhost:5100/api/public/sdk/server/latest-all > featbit-bootstrap.json

Then you can use this file to initialize the SDK in offline mode:

// first load data from file and then 
ok, _ := client.InitializeFromExternalJson(string(jsonBytes))
Experiments (A/B/n Testing)

We support automatic experiments for page-views and clicks, you just need to set your experiment on FeatBit platform, then you should be able to see the result in near real time after the experiment is started.

In case you need more control over the experiment data sent to our server, we offer a method to send custom event.

// for the percentage experiment
client.TrackPercentageMetric(user, eventName)
// for the numeric experiment
client.TrackNumericMetric(user, eventName, numericValue)

Make sure featbit.FBClient.TrackPercentageMetric() or featbit.FBClient.TrackNumericMetric() is called after the related feature flag is called, otherwise the custom event may not be included into the experiment result.

Getting support

  • If you have a specific question about using this sdk, we encourage you to ask it in our slack.
  • If you encounter a bug or would like to request a feature, submit an issue.

See Also

Documentation

Index

Constants

View Source
const (
	ExptKeyPrefix          = "expt"
	ReasonFlagOff          = "flag off"
	ReasonTargetMatch      = "target match"
	ReasonRuleMatch        = "rule match"
	ReasonFallthrough      = "fall through all rules"
	ReasonClientNotReady   = "client not ready"
	ReasonFlagNotFound     = "flag not found"
	ReasonWrongType        = "wrong type"
	ReasonUserNotSpecified = "user not specified"
	ReasonError            = "error in evaluation"
	FlagNameUnknown        = "flag Name unknown"
	ThanClause             = "Than"
	GeClause               = "BiggerEqualThan"
	GtClause               = "BiggerThan"
	LeClause               = "LessEqualThan"
	LtClause               = "LessThan"
	EqClause               = "Equal"
	NeqClause              = "NotEqual"
	ContainsClause         = "Contains"
	NotContainClause       = "NotContain"
	IsOneOfClause          = "IsOneOf"
	NotOneOfClause         = "NotOneOf"
	StartsWithClause       = "StartsWith"
	EndsWithClause         = "EndsWith"
	IsTrueClause           = "IsTrue"
	IsFalseClause          = "IsFalse"
	MatchRegexClause       = "MatchRegex"
	NotMatchRegexClause    = "NotMatchRegex"
	IsInSegmentClause      = "User is in segment"
	NotInSegmentClause     = "User is not in segment"
	FlagJsonType           = "json"
	FlagBoolType           = "boolean"
	FlagNumericType        = "number"
	FlagStringType         = "string"
)
View Source
const (
	INFO = iota
	WARN
	ERROR

	TRACE = -2
	DEBUG = -1
)

Variables

This section is empty.

Functions

This section is empty.

Types

type FBClient

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

func MakeCustomFBClient

func MakeCustomFBClient(envSecret string, streamingUrl string, eventUrl string, config FBConfig) (*FBClient, error)

MakeCustomFBClient creates a new client instance that connects to your feature flag center with the custom configuration.

The FBConfig allows customization of all SDK properties; some of these are represented directly as fields in FBConfig, while others are set by builder methods on a more specific configuration object. See FBConfig for details.

Unless it is configured to be offline with FBConfig.Offline, the client will begin attempting to connect to feature flag center as soon as you call this constructor. The constructor will return when it successfully connects, or when the timeout set by the FBConfig.StartWait parameter expires, whichever comes first.

If the timeout(15s) elapsed without a successful connection, it still returns a client instance-- in an initializing state, where feature flags will return default values-- and the error value is initializationTimeout. In this case, it will still continue trying to connect in the background.

If there was an unexpected error such that it cannot succeed by retrying-- for instance, the envSecret key is invalid or an DNS error-- it will return a client instance in an uninitialized state, and the error value is initializationFailed.

The way to monitor the client's status, use FBClient.IsInitialized or FBClient.GetDataUpdateStatusProvider.

client, _ := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)

if !client.IsInitialized() {
    // do whatever is appropriate if initialization has timed out
}

If you set FBConfig.StartWait to zero, the function will return immediately after creating the client instance, and do any further initialization in the background.

client, _ := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)

// later...
ok := client.GetDataSourceStatusProvider().WaitForOKState(10 * time.Second)
if !ok {
    // do whatever is appropriate if initialization has timed out
}

The only time it returns nil instead of a client instance is if the client cannot be created at all due to an invalid configuration. This is rare, but could happen if for example you specified a custom TLS certificate file that did not load a valid certificate, you inputted an invalid env secret key, etc...

func NewFBClient

func NewFBClient(envSecret string, streamingUrl string, eventUrl string) (*FBClient, error)

NewFBClient creates a new client instance that connects to your feature flag center with the default configuration. For advanced configuration options, use MakeCustomFBClient. Calling NewFBClient is exactly equivalent to calling MakeCustomClient with the config parameter set to a default value.

Unless it is configured to be offline with FBConfig.Offline, the client will begin attempting to connect to feature flag center as soon as you call this constructor. The constructor will return when it successfully connects, or when the timeout set by the FBConfig.StartWait parameter expires, whichever comes first.

If the timeout(15s) elapsed without a successful connection, it still returns a client instance-- in an initializing state, where feature flags will return default values-- and the error value is initializationTimeout. In this case, it will still continue trying to connect in the background.

If there was an unexpected error such that it cannot succeed by retrying-- for instance, the envSecret key is invalid or an DNS error-- it will return a client instance in an uninitialized state, and the error value is initializationFailed.

The way to monitor the client's status, use FBClient.IsInitialized or FBClient.GetDataUpdateStatusProvider.

client, _ := featbit.NewFBClient(envSecret, streamingUrl, eventUrl)

if !client.IsInitialized() {
    // do whatever is appropriate if initialization has timed out
}

If you set FBConfig.StartWait to zero, the function will return immediately after creating the client instance, and do any further initialization in the background.

client, _ := featbit.MakeCustomFBClient(envSecret, streamingUrl, eventUrl, config)

// later...
ok := client.GetDataSourceStatusProvider().WaitForOKState(10 * time.Second)
if !ok {
    // do whatever is appropriate if initialization has timed out
}

The only time it returns nil instead of a client instance is if the client cannot be created at all due to an invalid configuration. This is rare, but could happen if for example you specified a custom TLS certificate file that did not load a valid certificate, you inputted an invalid env secret key, etc...

func (*FBClient) AllLatestFlagsVariations

func (client *FBClient) AllLatestFlagsVariations(user FBUser) (AllFlagState, error)

AllLatestFlagsVariations returns a list of all feature flags value with details for a given user, including the reason describes the way the value was determined.

The return type AllFlagState could be used as a cache that provides the flag value to a client side sdk or a front-end app. See more details in AllFlagState.

This method does not send insight events back to feature flag center. See interfaces.AllFlagState

func (*FBClient) BoolVariation

func (client *FBClient) BoolVariation(featureFlagKey string, user FBUser, defaultValue bool) (bool, EvalDetail, error)

BoolVariation calculates the value of a feature flag for a given user, return a bool variation for the given user, or defaultValue if the flag is disabled or an error occurs; the details that explains how the flag value is explained and the error if any.

The method sends insight events back to feature flag center

func (*FBClient) Close

func (client *FBClient) Close() error

Close shuts down the FBClient. After calling this, the FBClient should no longer be used. The method will block until all pending events (if any) been sent.

func (*FBClient) DoubleVariation

func (client *FBClient) DoubleVariation(featureFlagKey string, user FBUser, defaultValue float64) (float64, EvalDetail, error)

DoubleVariation calculates the value of a feature flag for a given user, return a float variation for the given user, or defaultValue if the flag is disabled or an error occurs; the details that explains how the flag value is explained and the error if any.

The method sends insight events back to feature flag center

func (*FBClient) Flush

func (client *FBClient) Flush() error

Flush tells the FBClient that all pending events (if any) should be delivered as soon as possible. Flushing is asynchronous, so this method will return before it is complete. However, if you call Close(), events are guaranteed to be sent before that method returns.

func (*FBClient) GetDataUpdateStatusProvider

func (client *FBClient) GetDataUpdateStatusProvider() DataUpdateStatusProvider

GetDataUpdateStatusProvider returns an interface for tracking the status of the interfaces.DataSynchronizer.

The data synchronizer is the component that the SDK uses to get feature flags, segments such as a streaming connection. The interfaces.DataUpdateStatusProvider has methods for checking whether the interfaces.DataSynchronizer is currently operational and tracking changes in this status.

The interfaces.DataUpdateStatusProvider is recommended to use when SDK starts in asynchronous mode

func (*FBClient) Identify

func (client *FBClient) Identify(user FBUser) error

Identify register a FBUser

func (*FBClient) InitializeFromExternalJson

func (client *FBClient) InitializeFromExternalJson(jsonStr string) (bool, error)

InitializeFromExternalJson initializes FeatBit client in the offline mode

Return false if the json can't be parsed or client is not in the offline mode

func (*FBClient) IntVariation

func (client *FBClient) IntVariation(featureFlagKey string, user FBUser, defaultValue int) (int, EvalDetail, error)

IntVariation calculates the value of a feature flag for a given user, return an int variation for the given user, or defaultValue if the flag is disabled or an error occurs; the details that explains how the flag value is explained and the error if any.

The method sends insight events back to feature flag center

func (*FBClient) IsFlagKnown

func (client *FBClient) IsFlagKnown(featureFlagKey string) bool

IsFlagKnown returns true if feature flag is registered in the feature flag center, false if any error or flag is not existed

func (*FBClient) IsInitialized

func (client *FBClient) IsInitialized() bool

func (*FBClient) JsonVariation

func (client *FBClient) JsonVariation(featureFlagKey string, user FBUser, defaultValue interface{}) (interface{}, EvalDetail, error)

JsonVariation calculates the value of a feature flag for a given user, return a json object variation for the given user, or defaultValue if the flag is disabled or an error occurs; the details that explains how the flag value is explained and the error if any.

The method sends insight events back to feature flag center

func (*FBClient) TrackNumericMetric

func (client *FBClient) TrackNumericMetric(user FBUser, eventName string, metricValue float64) error

TrackNumericMetric reports that a user has performed an event, and associates it with a metric value. This value is used by the experimentation feature in numeric custom metrics.

The eventName normally corresponds to the event Name of a metric that you have created through the experiment dashboard in the feature flag center

func (*FBClient) TrackNumericMetrics

func (client *FBClient) TrackNumericMetrics(user FBUser, metrics map[string]float64) error

TrackNumericMetrics reports that a user tracks that a user performed a series of events with metric values. These values are used by the experimentation feature in numeric custom metrics.

The eventName normally corresponds to the event Name of a metric that you have created through the experiment dashboard in the feature flag center

func (*FBClient) TrackPercentageMetric

func (client *FBClient) TrackPercentageMetric(user FBUser, eventName string) error

TrackPercentageMetric reports that a user has performed an event, and associates it with a default value. This value is used by the experimentation feature in percentage custom metrics.

The eventName normally corresponds to the event Name of a metric that you have created through the experiment dashboard in the feature flag center

func (*FBClient) TrackPercentageMetrics

func (client *FBClient) TrackPercentageMetrics(user FBUser, eventNames ...string) error

TrackPercentageMetrics reports that a user tracks that a user performed a series of events with default values. These values are used by the experimentation feature in percentage custom metrics.

The eventName normally corresponds to the event Name of a metric that you have created through the experiment dashboard in the feature flag center

func (*FBClient) Variation

func (client *FBClient) Variation(featureFlagKey string, user FBUser, defaultValue string) (string, EvalDetail, error)

Variation calculates the value of a feature flag for a given user, return a string variation for the given user, or defaultValue if the flag is disabled or an error occurs; the details that explains how the flag value is explained and the error if any.

The method sends insight events back to feature flag center

type FBConfig

type FBConfig struct {
	// Offline whether SDK is offline
	Offline bool
	// StartWait how long the constructor will block awaiting a successful data sync
	//
	// Setting this to a zero or negative duration will not block and cause the constructor to return immediately.
	StartWait time.Duration
	// NetworkFactory a factory object which sets the SDK networking configuration Depending on the implementation,
	// the factory may be a builder that allows you to set other configuration options as well.
	NetworkFactory NetworkFactory
	// DataStorageFactory a factory object which sets the implementation of interfaces.DataStorage to be used for holding feature flags and
	// related data received from feature flag center. Depending on the implementation, the factory may be a builder that
	// allows you to set other configuration options as well.
	DataStorageFactory DataStorageFactory
	// DataSynchronizerFactory a factory object which sets the implementation of the interfaces.DataSynchronizer that receives feature flag data
	// from feature flag center.
	//
	// Depending on the implementation, the factory may be a builder that allows you to set other configuration options as well.
	DataSynchronizerFactory DataSynchronizerFactory
	// InsightProcessorFactory a factory object which sets the implementation of interfaces.InsightProcessor to be used for processing analytics events.
	//
	// Depending on the implementation, the factory may be a builder that allows you to set other configuration options as well.
	InsightProcessorFactory InsightProcessorFactory
	// LogLevel FeaBit log level
	LogLevel int
}

FBConfig exposes advanced configuration options for the FBClient

config = FBConfig{Offline: true}
var DefaultFBConfig *FBConfig = &FBConfig{
	Offline:                 false,
	StartWait:               15 * time.Second,
	NetworkFactory:          factories.NewNetworkBuilder(),
	DataStorageFactory:      factories.NewInMemoryStorageBuilder(),
	DataSynchronizerFactory: factories.NewStreamingBuilder(),
	InsightProcessorFactory: factories.NewInsightProcessorBuilder(),
	LogLevel:                INFO,
}

DefaultFBConfig FeatBit default configuration

Jump to

Keyboard shortcuts

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