@@ -33,12 +33,13 @@ type growthBookData struct {
33
33
context * Context
34
34
forcedFeatureValues map [string ]interface {}
35
35
attributeOverrides Attributes
36
- trackedFeatures sync. Map
37
- trackedExperiments sync. Map
36
+ trackedFeatures map [ string ] interface {}
37
+ trackedExperiments map [ string ] bool
38
38
nextSubscriptionID subscriptionID
39
39
subscriptions map [subscriptionID ]ExperimentCallback
40
- assigned sync. Map
40
+ assigned map [ string ] * Assignment
41
41
ready bool
42
+ finalAttributes Attributes
42
43
}
43
44
44
45
// New creates a new GrowthBook instance.
@@ -96,11 +97,12 @@ func New(context *Context) *GrowthBook {
96
97
context : context ,
97
98
forcedFeatureValues : nil ,
98
99
attributeOverrides : nil ,
99
- trackedFeatures : sync. Map {} ,
100
- trackedExperiments : sync. Map {} ,
100
+ trackedFeatures : make ( map [ string ] interface {}) ,
101
+ trackedExperiments : make ( map [ string ] bool ) ,
101
102
nextSubscriptionID : 1 ,
102
103
subscriptions : make (map [subscriptionID ]ExperimentCallback ),
103
- assigned : sync.Map {},
104
+ assigned : make (map [string ]* Assignment ),
105
+ finalAttributes : nil ,
104
106
}
105
107
gb := & GrowthBook {inner }
106
108
runtime .SetFinalizer (gb , func (gb * GrowthBook ) { repoUnsubscribe (gb ) })
@@ -366,6 +368,16 @@ func (gb *GrowthBook) GetFeatureValue(key string, defaultValue interface{}) inte
366
368
return defaultValue
367
369
}
368
370
371
+ // WithURL sets the URL in a GrowthBook's context.
372
+ func (gb * GrowthBook ) SetFinalAttributes () * GrowthBook {
373
+
374
+ attributes := gb .Attributes ()
375
+ gb .inner .Lock ()
376
+ gb .inner .finalAttributes = attributes
377
+ defer gb .inner .Unlock ()
378
+ return gb
379
+ }
380
+
369
381
// Deprecated: Use EvalFeature instead. Feature returns the result for
370
382
// a feature identified by a string feature key.
371
383
func (gb * GrowthBook ) Feature (key string ) * FeatureResult {
@@ -375,6 +387,8 @@ func (gb *GrowthBook) Feature(key string) *FeatureResult {
375
387
// EvalFeature returns the result for a feature identified by a string
376
388
// feature key.
377
389
func (gb * GrowthBook ) EvalFeature (id string ) * FeatureResult {
390
+ gb .inner .RLock ()
391
+ defer gb .inner .RUnlock ()
378
392
379
393
// Global override.
380
394
if gb .inner .forcedFeatureValues != nil {
@@ -395,7 +409,7 @@ func (gb *GrowthBook) EvalFeature(id string) *FeatureResult {
395
409
for _ , rule := range feature .Rules {
396
410
// If the rule has a condition and the condition does not pass,
397
411
// skip this rule.
398
- if rule .Condition != nil && ! rule .Condition .Eval (gb .Attributes () ) {
412
+ if rule .Condition != nil && ! rule .Condition .Eval (gb .inner . finalAttributes ) {
399
413
logInfo ("Skip rule because of condition" , id , rule )
400
414
continue
401
415
}
@@ -482,7 +496,7 @@ func (gb *GrowthBook) Subscribe(callback ExperimentCallback) func() {
482
496
483
497
// GetAllResults returns a map containing all the latest results from
484
498
// all experiments that have been run, indexed by the experiment key.
485
- func (gb * GrowthBook ) GetAllResults () sync. Map {
499
+ func (gb * GrowthBook ) GetAllResults () map [ string ] * Assignment {
486
500
gb .inner .RLock ()
487
501
defer gb .inner .RUnlock ()
488
502
@@ -496,7 +510,7 @@ func (gb *GrowthBook) ClearSavedResults() {
496
510
gb .inner .Lock ()
497
511
defer gb .inner .Unlock ()
498
512
499
- gb .inner .assigned = sync. Map {}
513
+ gb .inner .assigned = make ( map [ string ] * Assignment )
500
514
}
501
515
502
516
// ClearTrackingData clears out records of calls to the experiment
@@ -505,7 +519,7 @@ func (gb *GrowthBook) ClearTrackingData() {
505
519
gb .inner .Lock ()
506
520
defer gb .inner .Unlock ()
507
521
508
- gb .inner .trackedExperiments = sync. Map {}
522
+ gb .inner .trackedExperiments = make ( map [ string ] bool )
509
523
}
510
524
511
525
// GetAPIInfo gets the hostname and client key for GrowthBook API
@@ -569,10 +583,10 @@ func (gb *GrowthBook) trackFeatureUsage(key string, res *FeatureResult) {
569
583
}
570
584
571
585
// Only track a feature once, unless the assigned value changed.
572
- if saved , ok := gb .inner .trackedFeatures . Load ( key ) ; ok && reflect .DeepEqual (saved , res .Value ) {
586
+ if saved , ok := gb .inner .trackedFeatures [ key ] ; ok && reflect .DeepEqual (saved , res .Value ) {
573
587
return
574
588
}
575
- gb .inner .trackedFeatures . Store ( key , res .Value )
589
+ gb .inner .trackedFeatures [ key ] = res .Value
576
590
577
591
// Fire user-supplied callback
578
592
if gb .inner .context .OnFeatureUsage != nil {
@@ -662,17 +676,17 @@ func (gb *GrowthBook) fireSubscriptions(exp *Experiment, result *Result) {
662
676
// Determine whether the result changed from the last stored result
663
677
// for the experiment.
664
678
changed := false
665
- storedResult , exists := gb .inner .assigned . Load ( exp .Key )
679
+ storedResult , exists := gb .inner .assigned [ exp .Key ]
666
680
if exists {
667
- storedResultAssignment := storedResult .(* Assignment )
668
- if storedResultAssignment .Result .InExperiment != result .InExperiment ||
669
- storedResultAssignment .Result .VariationID != result .VariationID {
681
+ if storedResult .Result .InExperiment != result .InExperiment ||
682
+ storedResult .Result .VariationID != result .VariationID {
670
683
changed = true
671
684
}
672
685
}
673
686
674
687
// Store the experiment result.
675
- gb .inner .assigned .Store (exp .Key , & Assignment {exp , result })
688
+ gb .inner .assigned [exp .Key ] = & Assignment {exp , result }
689
+
676
690
// If the result changed, trigger all subscriptions.
677
691
if changed || ! exists {
678
692
for _ , sub := range gb .inner .subscriptions {
@@ -855,11 +869,11 @@ func (gb *GrowthBook) track(exp *Experiment, result *Result) {
855
869
// experiment.
856
870
key := result .HashAttribute + result .HashValue +
857
871
exp .Key + strconv .Itoa (result .VariationID )
858
- if _ , exists := gb .inner .trackedExperiments . Load ( key ) ; exists {
872
+ if _ , exists := gb .inner .trackedExperiments [ key ] ; exists {
859
873
return
860
874
}
861
875
862
- gb .inner .trackedExperiments . Store ( key , true )
876
+ gb .inner .trackedExperiments [ key ] = true
863
877
gb .inner .context .TrackingCallback (exp , result )
864
878
}
865
879
0 commit comments