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
}
Updated 7 months ago