@@ -43,7 +43,7 @@ public class DecisionService
43
43
private Bucketer Bucketer ;
44
44
private IErrorHandler ErrorHandler ;
45
45
private UserProfileService UserProfileService ;
46
- private ILogger Logger ;
46
+ private static ILogger Logger ;
47
47
48
48
/// <summary>
49
49
/// Associative array of user IDs to an associative array
@@ -85,9 +85,9 @@ public DecisionService(Bucketer bucketer, IErrorHandler errorHandler,
85
85
/// <summary>
86
86
/// Get a Variation of an Experiment for a user to be allocated into.
87
87
/// </summary>
88
- /// <param name = "experiment" > The Experiment the user will be bucketed into.</param>
89
- /// <param name = "user" > Optimizely user context.
90
- /// <param name = "config" > Project config.</param>
88
+ /// <param name= "experiment"> The Experiment the user will be bucketed into.</param>
89
+ /// <param name= "user"> Optimizely user context.</param>
90
+ /// <param name= "config"> Project config.</param>
91
91
/// <returns>The Variation the user is allocated into.</returns>
92
92
public virtual Result < Variation > GetVariation ( Experiment experiment ,
93
93
OptimizelyUserContext user ,
@@ -100,24 +100,72 @@ ProjectConfig config
100
100
/// <summary>
101
101
/// Get a Variation of an Experiment for a user to be allocated into.
102
102
/// </summary>
103
- /// <param name = "experiment" > The Experiment the user will be bucketed into.</param>
104
- /// <param name = "user" > optimizely user context.
105
- /// <param name = "config" > Project Config.</param>
106
- /// <param name = "options" >An array of decision options.</param>
107
- /// <returns>The Variation the user is allocated into. </returns>
103
+ /// <param name= "experiment"> The Experiment the user will be bucketed into.</param>
104
+ /// <param name= "user">Optimizely user context.</param>
105
+ /// <param name= "config"> Project Config.</param>
106
+ /// <param name= "options">An array of decision options.</param>
107
+ /// <returns></returns>
108
108
public virtual Result < Variation > GetVariation ( Experiment experiment ,
109
109
OptimizelyUserContext user ,
110
110
ProjectConfig config ,
111
111
OptimizelyDecideOption [ ] options
112
112
)
113
113
{
114
114
var reasons = new DecisionReasons ( ) ;
115
- var userId = user . GetUserId ( ) ;
115
+
116
+ var ignoreUps = options . Contains ( OptimizelyDecideOption . IGNORE_USER_PROFILE_SERVICE ) ;
117
+ UserProfileTracker userProfileTracker = null ;
118
+
119
+ if ( UserProfileService != null && ! ignoreUps )
120
+ {
121
+ var userProfile = GetUserProfile ( user . GetUserId ( ) , reasons ) ;
122
+ userProfileTracker = new UserProfileTracker ( userProfile , false ) ;
123
+ }
124
+
125
+ var response = GetVariation ( experiment , user , config , options , userProfileTracker ,
126
+ reasons ) ;
127
+
128
+ if ( UserProfileService != null && ! ignoreUps &&
129
+ userProfileTracker ? . ProfileUpdated == true )
130
+ {
131
+ SaveUserProfile ( userProfileTracker . UserProfile ) ;
132
+ }
133
+
134
+ return response ;
135
+ }
136
+
137
+ /// <summary>
138
+ /// Get a Variation of an Experiment for a user to be allocated into.
139
+ /// </summary>
140
+ /// <param name="experiment">The Experiment the user will be bucketed into.</param>
141
+ /// <param name="user">Optimizely user context.</param>
142
+ /// <param name="config">Project Config.</param>
143
+ /// <param name="options">An array of decision options.</param>
144
+ /// <param name="userProfileTracker">A UserProfileTracker object.</param>
145
+ /// <param name="reasons">Set of reasons for the decision.</param>
146
+ /// <returns>The Variation the user is allocated into.</returns>
147
+ public virtual Result < Variation > GetVariation ( Experiment experiment ,
148
+ OptimizelyUserContext user ,
149
+ ProjectConfig config ,
150
+ OptimizelyDecideOption [ ] options ,
151
+ UserProfileTracker userProfileTracker ,
152
+ DecisionReasons reasons = null
153
+ )
154
+ {
155
+ if ( reasons == null )
156
+ {
157
+ reasons = new DecisionReasons ( ) ;
158
+ }
159
+
116
160
if ( ! ExperimentUtils . IsExperimentActive ( experiment , Logger ) )
117
161
{
162
+ var message = reasons . AddInfo ( $ "Experiment { experiment . Key } is not running.") ;
163
+ Logger . Log ( LogLevel . INFO , message ) ;
118
164
return Result < Variation > . NullResult ( reasons ) ;
119
165
}
120
166
167
+ var userId = user . GetUserId ( ) ;
168
+
121
169
// check if a forced variation is set
122
170
var decisionVariationResult = GetForcedVariation ( experiment . Key , userId , config ) ;
123
171
reasons += decisionVariationResult . DecisionReasons ;
@@ -137,76 +185,41 @@ OptimizelyDecideOption[] options
137
185
return decisionVariationResult ;
138
186
}
139
187
140
- // fetch the user profile map from the user profile service
141
- var ignoreUPS = Array . Exists ( options ,
142
- option => option == OptimizelyDecideOption . IGNORE_USER_PROFILE_SERVICE ) ;
143
-
144
- UserProfile userProfile = null ;
145
- if ( ! ignoreUPS && UserProfileService != null )
188
+ if ( userProfileTracker != null )
146
189
{
147
- try
148
- {
149
- var userProfileMap = UserProfileService . Lookup ( user . GetUserId ( ) ) ;
150
- if ( userProfileMap != null &&
151
- UserProfileUtil . IsValidUserProfileMap ( userProfileMap ) )
152
- {
153
- userProfile = UserProfileUtil . ConvertMapToUserProfile ( userProfileMap ) ;
154
- decisionVariationResult =
155
- GetStoredVariation ( experiment , userProfile , config ) ;
156
- reasons += decisionVariationResult . DecisionReasons ;
157
- if ( decisionVariationResult . ResultObject != null )
158
- {
159
- return decisionVariationResult . SetReasons ( reasons ) ;
160
- }
161
- }
162
- else if ( userProfileMap == null )
163
- {
164
- Logger . Log ( LogLevel . INFO ,
165
- reasons . AddInfo (
166
- "We were unable to get a user profile map from the UserProfileService." ) ) ;
167
- }
168
- else
169
- {
170
- Logger . Log ( LogLevel . ERROR ,
171
- reasons . AddInfo ( "The UserProfileService returned an invalid map." ) ) ;
172
- }
173
- }
174
- catch ( Exception exception )
190
+ decisionVariationResult =
191
+ GetStoredVariation ( experiment , userProfileTracker . UserProfile , config ) ;
192
+ reasons += decisionVariationResult . DecisionReasons ;
193
+ variation = decisionVariationResult . ResultObject ;
194
+ if ( variation != null )
175
195
{
176
- Logger . Log ( LogLevel . ERROR , reasons . AddInfo ( exception . Message ) ) ;
177
- ErrorHandler . HandleError (
178
- new Exceptions . OptimizelyRuntimeException ( exception . Message ) ) ;
196
+ return decisionVariationResult ;
179
197
}
180
198
}
181
199
182
- var filteredAttributes = user . GetAttributes ( ) ;
183
- var doesUserMeetAudienceConditionsResult =
184
- ExperimentUtils . DoesUserMeetAudienceConditions ( config , experiment , user ,
185
- LOGGING_KEY_TYPE_EXPERIMENT , experiment . Key , Logger ) ;
186
- reasons += doesUserMeetAudienceConditionsResult . DecisionReasons ;
187
- if ( doesUserMeetAudienceConditionsResult . ResultObject )
200
+ var decisionMeetAudience = ExperimentUtils . DoesUserMeetAudienceConditions ( config ,
201
+ experiment , user ,
202
+ LOGGING_KEY_TYPE_EXPERIMENT , experiment . Key , Logger ) ;
203
+ reasons += decisionMeetAudience . DecisionReasons ;
204
+ if ( decisionMeetAudience . ResultObject )
188
205
{
189
- // Get Bucketing ID from user attributes.
190
- var bucketingIdResult = GetBucketingId ( userId , filteredAttributes ) ;
206
+ var bucketingIdResult = GetBucketingId ( userId , user . GetAttributes ( ) ) ;
191
207
reasons += bucketingIdResult . DecisionReasons ;
192
208
193
209
decisionVariationResult = Bucketer . Bucket ( config , experiment ,
194
210
bucketingIdResult . ResultObject , userId ) ;
195
211
reasons += decisionVariationResult . DecisionReasons ;
212
+ variation = decisionVariationResult . ResultObject ;
196
213
197
- if ( decisionVariationResult . ResultObject ? . Key != null )
214
+ if ( variation != null )
198
215
{
199
- if ( UserProfileService != null && ! ignoreUPS )
216
+ if ( userProfileTracker != null )
200
217
{
201
- var bucketerUserProfile = userProfile ??
202
- new UserProfile ( userId ,
203
- new Dictionary < string , Decision > ( ) ) ;
204
- SaveVariation ( experiment , decisionVariationResult . ResultObject ,
205
- bucketerUserProfile ) ;
218
+ userProfileTracker . UpdateUserProfile ( experiment , variation ) ;
206
219
}
207
220
else
208
221
{
209
- Logger . Log ( LogLevel . INFO ,
222
+ Logger . Log ( LogLevel . DEBUG ,
210
223
"This decision will not be saved since the UserProfileService is null." ) ;
211
224
}
212
225
}
@@ -720,18 +733,6 @@ public virtual Result<FeatureDecision> GetVariationForFeature(FeatureFlag featur
720
733
new OptimizelyDecideOption [ ] { } ) ;
721
734
}
722
735
723
- private class UserProfileTracker
724
- {
725
- public UserProfile UserProfile { get ; set ; }
726
- public bool ProfileUpdated { get ; set ; }
727
-
728
- public UserProfileTracker ( UserProfile userProfile , bool profileUpdated )
729
- {
730
- UserProfile = userProfile ;
731
- ProfileUpdated = profileUpdated ;
732
- }
733
- }
734
-
735
736
void SaveUserProfile ( UserProfile userProfile )
736
737
{
737
738
if ( UserProfileService == null )
@@ -791,6 +792,40 @@ private UserProfile GetUserProfile(String userId, DecisionReasons reasons)
791
792
return userProfile ;
792
793
}
793
794
795
+ public class UserProfileTracker
796
+ {
797
+ public UserProfile UserProfile { get ; set ; }
798
+ public bool ProfileUpdated { get ; set ; }
799
+
800
+ public UserProfileTracker ( UserProfile userProfile , bool profileUpdated )
801
+ {
802
+ UserProfile = userProfile ;
803
+ ProfileUpdated = profileUpdated ;
804
+ }
805
+
806
+ public void UpdateUserProfile ( Experiment experiment , Variation variation )
807
+ {
808
+ var experimentId = experiment . Id ;
809
+ var variationId = variation . Id ;
810
+ Decision decision ;
811
+ if ( UserProfile . ExperimentBucketMap . ContainsKey ( experimentId ) )
812
+ {
813
+ decision = UserProfile . ExperimentBucketMap [ experimentId ] ;
814
+ decision . VariationId = variationId ;
815
+ }
816
+ else
817
+ {
818
+ decision = new Decision ( variationId ) ;
819
+ }
820
+
821
+ UserProfile . ExperimentBucketMap [ experimentId ] = decision ;
822
+ ProfileUpdated = true ;
823
+
824
+ Logger . Log ( LogLevel . INFO ,
825
+ $ "Updated variation \" { variationId } \" of experiment \" { experimentId } \" for user \" { UserProfile . UserId } \" .") ;
826
+ }
827
+ }
828
+
794
829
public virtual List < Result < FeatureDecision > > GetVariationsForFeatureList (
795
830
List < FeatureFlag > featureFlags ,
796
831
OptimizelyUserContext user ,
@@ -834,22 +869,23 @@ OptimizelyDecideOption[] options
834
869
decisionResult = GetVariationForFeatureRollout ( featureFlag , user , projectConfig ) ;
835
870
reasons += decisionResult . DecisionReasons ;
836
871
837
- if ( decisionResult . ResultObject != null )
872
+ if ( decisionResult . ResultObject == null )
873
+ {
874
+ Logger . Log ( LogLevel . INFO ,
875
+ reasons . AddInfo (
876
+ $ "The user \" { userId } \" is not bucketed into a rollout for feature flag \" { featureFlag . Key } \" .") ) ;
877
+ decisions . Add ( Result < FeatureDecision > . NewResult (
878
+ new FeatureDecision ( null , null , FeatureDecision . DECISION_SOURCE_ROLLOUT ) ,
879
+ reasons ) ) ;
880
+ }
881
+ else
838
882
{
839
883
Logger . Log ( LogLevel . INFO ,
840
884
reasons . AddInfo (
841
885
$ "The user \" { userId } \" is bucketed into a rollout for feature flag \" { featureFlag . Key } \" .") ) ;
842
886
decisions . Add (
843
887
Result < FeatureDecision > . NewResult ( decisionResult . ResultObject , reasons ) ) ;
844
- continue ;
845
888
}
846
-
847
- Logger . Log ( LogLevel . INFO ,
848
- reasons . AddInfo (
849
- $ "The user \" { userId } \" is not bucketed into a rollout for feature flag \" { featureFlag . Key } \" .") ) ;
850
- decisions . Add ( Result < FeatureDecision > . NewResult (
851
- new FeatureDecision ( null , null , FeatureDecision . DECISION_SOURCE_ROLLOUT ) ,
852
- reasons ) ) ;
853
889
}
854
890
855
891
if ( UserProfileService != null && ! ignoreUPS && userProfileTracker ? . ProfileUpdated == true )
0 commit comments