diff --git a/pkg/client/factory.go b/pkg/client/factory.go index c05f70a2..050b0904 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -22,6 +22,7 @@ import ( "errors" "time" + "github.com/optimizely/go-sdk/v2/pkg/cache" "github.com/optimizely/go-sdk/v2/pkg/cmab" "github.com/optimizely/go-sdk/v2/pkg/config" "github.com/optimizely/go-sdk/v2/pkg/decide" @@ -37,6 +38,28 @@ import ( "github.com/optimizely/go-sdk/v2/pkg/utils" ) +// CmabConfig holds CMAB configuration options exposed at the client level. +// This provides a stable public API while allowing internal cmab.Config to change. +type CmabConfig struct { + CacheSize int + CacheTTL time.Duration + HTTPTimeout time.Duration + Cache cache.CacheWithRemove // Custom cache implementation (Redis, etc.) +} + +// toCmabConfig converts client-level CmabConfig to internal cmab.Config +func (c *CmabConfig) toCmabConfig() *cmab.Config { + if c == nil { + return nil + } + return &cmab.Config{ + CacheSize: c.CacheSize, + CacheTTL: c.CacheTTL, + HTTPTimeout: c.HTTPTimeout, + Cache: c.Cache, + } +} + // OptimizelyFactory is used to customize and construct an instance of the OptimizelyClient. type OptimizelyFactory struct { SDKKey string @@ -54,7 +77,7 @@ type OptimizelyFactory struct { overrideStore decision.ExperimentOverrideStore userProfileService decision.UserProfileService notificationCenter notification.Center - cmabConfig *cmab.Config + cmabConfig *CmabConfig // ODP segmentsCacheSize int @@ -163,7 +186,7 @@ func (f *OptimizelyFactory) Client(clientOptions ...OptionFunc) (*OptimizelyClie } // Add CMAB config option if provided if f.cmabConfig != nil { - experimentServiceOptions = append(experimentServiceOptions, decision.WithCmabConfig(f.cmabConfig)) + experimentServiceOptions = append(experimentServiceOptions, decision.WithCmabConfig(f.cmabConfig.toCmabConfig())) } compositeExperimentService := decision.NewCompositeExperimentService(f.SDKKey, experimentServiceOptions...) compositeService := decision.NewCompositeService(f.SDKKey, decision.WithCompositeExperimentService(compositeExperimentService)) @@ -327,7 +350,7 @@ func WithTracer(tracer tracing.Tracer) OptionFunc { } // WithCmabConfig sets the CMAB configuration options -func WithCmabConfig(cmabConfig *cmab.Config) OptionFunc { +func WithCmabConfig(cmabConfig *CmabConfig) OptionFunc { return func(f *OptimizelyFactory) { f.cmabConfig = cmabConfig } diff --git a/pkg/client/factory_test.go b/pkg/client/factory_test.go index fe878ec4..d6e35860 100644 --- a/pkg/client/factory_test.go +++ b/pkg/client/factory_test.go @@ -28,7 +28,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/optimizely/go-sdk/v2/pkg/cache" - "github.com/optimizely/go-sdk/v2/pkg/cmab" "github.com/optimizely/go-sdk/v2/pkg/config" "github.com/optimizely/go-sdk/v2/pkg/decide" "github.com/optimizely/go-sdk/v2/pkg/decision" @@ -461,13 +460,10 @@ func TestStaticClientError(t *testing.T) { func TestFactoryWithCmabConfig(t *testing.T) { factory := OptimizelyFactory{} - cmabConfig := cmab.Config{ + cmabConfig := CmabConfig{ CacheSize: 100, CacheTTL: time.Minute, HTTPTimeout: 30 * time.Second, - RetryConfig: &cmab.RetryConfig{ - MaxRetries: 5, - }, } // Test the option function @@ -477,19 +473,14 @@ func TestFactoryWithCmabConfig(t *testing.T) { assert.Equal(t, 100, factory.cmabConfig.CacheSize) assert.Equal(t, time.Minute, factory.cmabConfig.CacheTTL) assert.Equal(t, 30*time.Second, factory.cmabConfig.HTTPTimeout) - assert.NotNil(t, factory.cmabConfig.RetryConfig) - assert.Equal(t, 5, factory.cmabConfig.RetryConfig.MaxRetries) } func TestFactoryCmabConfigPassedToDecisionService(t *testing.T) { // Test that CMAB config is correctly passed to decision service when creating client - cmabConfig := cmab.Config{ + cmabConfig := CmabConfig{ CacheSize: 200, CacheTTL: 2 * time.Minute, HTTPTimeout: 20 * time.Second, - RetryConfig: &cmab.RetryConfig{ - MaxRetries: 3, - }, } factory := OptimizelyFactory{ @@ -501,7 +492,6 @@ func TestFactoryCmabConfigPassedToDecisionService(t *testing.T) { assert.Equal(t, &cmabConfig, factory.cmabConfig) assert.Equal(t, 200, factory.cmabConfig.CacheSize) assert.Equal(t, 2*time.Minute, factory.cmabConfig.CacheTTL) - assert.NotNil(t, factory.cmabConfig.RetryConfig) } func TestFactoryOptionFunctions(t *testing.T) { @@ -512,19 +502,19 @@ func TestFactoryOptionFunctions(t *testing.T) { WithSegmentsCacheSize(100)(factory) WithSegmentsCacheTimeout(5 * time.Second)(factory) WithOdpDisabled(true)(factory) - WithCmabConfig(&cmab.Config{CacheSize: 50})(factory) + WithCmabConfig(&CmabConfig{CacheSize: 50})(factory) // Verify options were set assert.Equal(t, "test_token", factory.DatafileAccessToken) assert.Equal(t, 100, factory.segmentsCacheSize) assert.Equal(t, 5*time.Second, factory.segmentsCacheTimeout) assert.True(t, factory.odpDisabled) - assert.Equal(t, &cmab.Config{CacheSize: 50}, factory.cmabConfig) + assert.Equal(t, &CmabConfig{CacheSize: 50}, factory.cmabConfig) } func TestWithCmabConfigOption(t *testing.T) { factory := &OptimizelyFactory{} - testConfig := cmab.Config{ + testConfig := CmabConfig{ CacheSize: 200, CacheTTL: 2 * time.Minute, } @@ -534,13 +524,10 @@ func TestWithCmabConfigOption(t *testing.T) { func TestClientWithCmabConfig(t *testing.T) { // Test client creation with non-empty CMAB config (tests reflect.DeepEqual path) - cmabConfig := cmab.Config{ + cmabConfig := CmabConfig{ CacheSize: 200, CacheTTL: 5 * time.Minute, HTTPTimeout: 30 * time.Second, - RetryConfig: &cmab.RetryConfig{ - MaxRetries: 5, - }, } factory := OptimizelyFactory{ @@ -559,7 +546,7 @@ func TestClientWithCmabConfig(t *testing.T) { func TestClientWithEmptyCmabConfig(t *testing.T) { // Test client creation with empty CMAB config (tests reflect.DeepEqual returns true) - emptyCmabConfig := cmab.Config{} + emptyCmabConfig := CmabConfig{} factory := OptimizelyFactory{ SDKKey: "test_sdk_key", diff --git a/pkg/cmab/client.go b/pkg/cmab/client.go index bfb7c9fa..588e518e 100644 --- a/pkg/cmab/client.go +++ b/pkg/cmab/client.go @@ -35,7 +35,7 @@ var CMABPredictionEndpoint = "https://prediction.cmab.optimizely.com/predict/%s" const ( // DefaultMaxRetries is the default number of retries for CMAB requests - DefaultMaxRetries = 3 + DefaultMaxRetries = 1 // DefaultInitialBackoff is the default initial backoff duration DefaultInitialBackoff = 100 * time.Millisecond // DefaultMaxBackoff is the default maximum backoff duration diff --git a/pkg/cmab/service_test.go b/pkg/cmab/service_test.go index 141051ef..7c13edf8 100644 --- a/pkg/cmab/service_test.go +++ b/pkg/cmab/service_test.go @@ -845,7 +845,6 @@ func (s *CmabServiceTestSuite) TestGetDecisionApiError() { s.mockClient.AssertExpectations(s.T()) } - // TestLockStripingDistribution verifies that different user/rule combinations // use different locks to allow for better concurrency func TestLockStripingDistribution(t *testing.T) {