Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

The availability of features may depend on your plan type. Contact your Customer Success Manager if you have any questions.

Dev guideRecipesAPI Reference
Dev guideAPI ReferenceUser GuideLegal TermsGitHubDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Initialize the Go SDK

How to initialize the Optimizely Feature Experimentation Go SDK in your application.

To initialize the OptimizelyClient that exposes API methods like Decide, you need either the SDK key or a hard-coded JSON datafile.

Basic initialization with SDK Key

import optly "github.com/optimizely/go-sdk" // for v2: "github.com/optimizely/go-sdk/v2"

// Instantiates a client that syncs the datafile in the background
optlyClient, err := optly.Client("SDK_KEY_HERE")
if err != nil{
	// handle error
}

Advanced configuration

Static client instance

If you do not want the client to periodically sync the datafile in the background, you can instantiate a static client. This is useful for using the SDK in short-lived environments, such as Lambda functions. You can use the OptimizelyFactory for these more advanced use cases:

import "github.com/optimizely/go-sdk/pkg/client" // for v2: "github.com/optimizely/go-sdk/v2/pkg/client"

optimizelyFactory := &client.OptimizelyFactory{
          Datafile: []byte("DATAFILE_JSON_STRING_HERE"),
}

// Instantiate a static client (no datafile polling)
staticOptlyClient, err := optimizelyFactory.StaticClient()
if err != nil {
	// handle error
}

If you want the SDK to initialize and perform just a one-time remote datafile fetch you can pass in the SDK key instead and instantiate a static client:

import "github.com/optimizely/go-sdk/pkg/client" // for v2: "github.com/optimizely/go-sdk/v2/pkg/client"

optimizelyFactory := &client.OptimizelyFactory{
          SDKKey: "[SDK_KEY_HERE]",
}

// Instantiate a static client that will pull down the datafile one time
staticOptlyClient, err := optimizelyFactory.StaticClient()
if err != nil {
	// handle error
}

Custom initialization

You can further customize the top-level components of the SDKs:

  • Event Processor
  • Config Manager
  • ODP Manager
  • Default Decide Options
  • Decision Service using the OptimizelyFactory
import (
  "time"
 
  "github.com/optimizely/go-sdk/pkg/client" // for v2: "github.com/optimizely/go-sdk/v2/pkg/client" 
)

optimizelyFactory := &client.OptimizelyFactory{
		SDKKey: "[SDK_KEY_HERE]",
	}

datafilePollingInterval := 2 * time.Second
eventBatchSize := 20
eventQueueSize := 1500
eventFlushInterval := 10 * time.Second
defaultDecideOptions := []decide.OptimizelyDecideOptions{
	decide.IgnoreUserProfileService,
}

// Instantiate a client with custom configuration
optimizelyClient, err := optimizelyFactory.Client(
  client.WithPollingConfigManager(datafilePollingInterval, nil),
  client.WithBatchEventProcessor(
    eventBatchSize,
    eventQueueSize,
    eventFlushInterval,
  ),
  client.WithDefaultDecideOptions(defaultDecideOptions),
)
if err != nil {
	// handle error
}

For more details on configuring the event batching, see Event Batching. For more details on configuring default decide options, see Decide methods.

Dispose of the client

For effective resource management with the Optimizely Go SDK, you must properly close the Optimizely client instance when it is no longer needed. This is done by calling .Close().

The .Close() method ensures that the processes and queues associated with the instance are properly released. This is essential for preventing memory leaks and ensuring that the application runs efficiently, especially in environments where resources are limited or in applications that create and dispose of many instances over their lifecycle.

See Close Optimizely Feature Experimentation Go SDK on application exit.

ODPManager

ODPManager contains all the logic supporting Real-Time Segments for Feature Experimentation-related features, including audience segments.

The Go SDK enables the Real-Time Segments for Feature Experimentation methods by default. But, the methods do nothing unless you have enabled and configured the Real-Time Segments for Feature Experimentation.

To explicitly disable Real-Time Segments for Feature Experimentation, pass WithOdpDisabled(true) as clientOption while initiating OptimizelyClient.

The following settings are optionally configurable when the Go SDK is initialized:

  • ODP SegmentsCache size – WithSegmentsCacheSize
    • Default – 10,000
    • Set to 0 to disable caching.
  • ODP SegmentsCache timeout (in seconds) – WithSegmentsCacheTimeout
    • Default – 600 secs (10 minutes)
    • Set to 0 to disable timeout (never expires).
  • ODP enable – WithOdpDisabled
    • Default – false (enabled)
    • The Go SDK returns or logs an odpNotEnabled error when ODP is disabled and its features are requested.

See Customize ODPSegmentManager.

ODPSegmentManager

This module provides an interface to the remote Optimizely Data Platform (ODP) server for audience segment mappings.

It fetches all qualified segments for the given user context and returns true if the qualified segments array in the user context is updated.

It also manages a segment's cache shared for all user contexts. The cache is in memory (not persistent), which is reset when the device is rebooted or the app is terminated.

ODPEventManager

This module provides an interface to the remote ODP server for events.

It queues all pending events (persistent) and sends them (in batches of up to 10) to the ODP server when all resources are available, including network connection and ODP public key (in the SDK's datafile).

📘

Note

The Go SDK will try to dispatch all events (stored in a persistent queue and retried on recoverable errors), but completion is not guaranteed.

import (
	"github.com/optimizely/go-sdk/pkg/odp"         // for v2: "github.com/optimizely/go-sdk/v2/pkg/odp"
	"github.com/optimizely/go-sdk/pkg/odp/event"   // for v2: "github.com/optimizely/go-sdk/v2/pkg/odp/event"
	"github.com/optimizely/go-sdk/pkg/odp/segment" // for v2: "github.com/optimizely/go-sdk/v2/pkg/odp/segment"
)

// You must configure Real-Time Segments for Feature Experimentation
// before being able to calling ODPApiManager, ODPEventManager, and ODPSegmentManager.
sdkKey := `<YOUR_SDK_KEY>`
defaultEventApiManager := event.NewEventAPIManager(sdkKey, nil)
odpEventManager := event.NewBatchEventManager(event.WithAPIManager(defaultEventApiManager))
defaultSegmentApiManager := segment.NewSegmentAPIManager(sdkKey, nil)
odpSegmentManager := segment.NewSegmentManager(sdkKey, segment.WithAPIManager(defaultSegmentApiManager))

odpManager := odp.NewOdpManager(sdkKey, 
		false,
		odp.WithEventManager(odpEventManager),     // Optional
		odp.WithSegmentManager(odpSegmentManager), // Optional
)

Customize EventApiManager

APIManager is an interface, and its implementation requires the user to provide the functionality of the following methods:

// APIManager represents the event API manager.
type APIManager interface {
	SendOdpEvents(apiKey, apiHost string, events []Event) (canRetry bool, err error)
}

Moreover, you can provide eventDispatchTimeout in the constructor of DefaultEventAPIManager.

eventDispatchTimeoutMillis := 20000 * time.Millisecond
defaultEventApiManager := event.NewEventAPIManager(sdkKey, utils.NewHTTPRequester(logging.GetLogger(sdkKey, "EventAPIManager"), utils.Timeout(eventDispatchTimeoutMillis)))

Customize SegmentAPIManager

APIManager is an interface, and its implementation requires the user to provide the functionality of the following methods:

// APIManager represents the segment API manager.
type APIManager interface {
	FetchQualifiedSegments(apiKey, apiHost, userID string, segmentsToCheck []string) ([]string, error)
}

You can provide a segmentFetchTimeoutMillis in the constructor of DefaultSegmentAPIManager.

segmentFetchTimeoutMillis := 20000 * time.Millisecond
defaultSegmentApiManager := segment.NewSegmentAPIManager(sdkKey, utils.NewHTTPRequester(logging.GetLogger(sdkKey, "SegmentAPIManager"), utils.Timeout(segmentFetchTimeoutMillis)))

Customize ODPEventManager

You can provide a custom queueSize and flushInterval (if set to 0, then batchSize is set to 1. Otherwise, batchSize is the default, 10, in the constructor).

queueSize := 20000
// Note: if this is set to 0 then batchSize will be set to 1, otherwise batchSize will be default, which is 10.
flushIntervalInMillis := 10000 * time.Millisecond // 10,000 msecs = 10 secs 

odpEventManager := event.NewBatchEventManager(
	event.WithAPIManager(defaultEventApiManager),
	event.WithQueueSize(queueSize),
	event.WithFlushInterval(flushIntervalInMillis),
)is

Customize ODPSegmentManager

You can provide a cacheSize and a cacheTimeout in the constructor, but these values are ignored if you provide a custom cache in the ODPManager. By default, SegmentManager is using LRUCache.

cacheSize := 600
cacheTimeoutInSeconds := 600 * time.Second // 10 mins = 600 secs
odpSegmentManager := segment.NewSegmentManager(
	sdkKey,
	segment.WithAPIManager(defaultSegmentApiManager),
	segment.WithSegmentsCacheSize(cacheSize),
	segment.WithSegmentsCacheTimeout(cacheTimeoutInSeconds),
)

// Second method to set custom cache size and timeout using odpManager
odpManager := odp.NewOdpManager(
	sdkKey, false,
	odp.WithSegmentsCacheSize(cacheSize),
	odp.WithSegmentsCacheTimeout(cacheTimeoutInSeconds),
)

Custom cache

You can use the custom cache, which is used in storing the FetchQualifiedSegments results. To provide a custom cache, you have to implement the Cache interface.

// Cache is used for caching ODP segments
type Cache interface {
	Save(key string, value interface{})
	Lookup(key string) interface{}
	Reset()
}

Here is an example of customCache implementation:

type CustomSegmentsCache struct {
	Cache map[string]interface{}
	lock  sync.Mutex
}

func (c *CustomSegmentsCache) Save(key string, value interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.Cache[key] = value
}

func (c *CustomSegmentsCache) Lookup(key string) interface{} {
	c.lock.Lock()
	defer c.lock.Unlock()
	return c.Cache[key]
}

func (c *CustomSegmentsCache) Reset() {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.Cache = map[string]interface{}{}
}

Pass this customCache object to OdpManager using its builder option and do not pass SegmentManager object:

customCache := &CustomSegmentsCache{
	Cache: map[string]interface{}{},
}

// Note: To use custom Cache user should not pass SegmentManager 
odpManager = odp.NewOdpManager(
	sdkKey, 
	false,
	odp.WithSegmentsCache(customCache),
)

Use authenticated datafile in a secure environment

You can fetch the Optimizely Feature Experimentation datafile from an authenticated endpoint using a server-side (only) Optimizely Feature Experimentation SDK, like the Go SDK.

To use an authenticated datafile, download your environment's access token from the Optimizely app in Settings > Environments. Select your secure environment, and copy the Datafile access token. The following example shows how to initialize the Optimizely client using an access token and sdk_key, enabling the client to fetch the authenticated datafile and complete initialization.

// fetch the datafile from an authenticated endpoint
accessToken := `<YOUR_DATAFILE_ACCESS_TOKEN>`
sdkKey := `<YOUR_SDK_KEY>`
factory := client.OptimizelyFactory{SDKKey: sdkKey}
optimizelyClient, err := factory.Client(client.WithDatafileAccessToken(accessToken))
if err != nil {
	// handle error
}