1
1
/*
2
- * Copyright 2017-2022, Optimizely
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * https://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
2
+ * Copyright 2017-2022, 2024 Optimizely
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
16
17
17
using System ;
18
18
using System . Collections . Generic ;
@@ -43,6 +43,22 @@ public class DecisionService
43
43
private IErrorHandler ErrorHandler ;
44
44
private UserProfileService UserProfileService ;
45
45
private ILogger Logger ;
46
+ private UserProfile _userProfile ;
47
+
48
+ private bool _decisionBatchInProgress = false ;
49
+
50
+ public bool DecisionBatchInProgress
51
+ {
52
+ get => _decisionBatchInProgress ;
53
+ set
54
+ {
55
+ _decisionBatchInProgress = value ;
56
+ if ( ! _decisionBatchInProgress )
57
+ {
58
+ SaveToUserProfileService ( ) ;
59
+ }
60
+ }
61
+ }
46
62
47
63
/// <summary>
48
64
/// Associative array of user IDs to an associative array
@@ -60,10 +76,10 @@ public class DecisionService
60
76
/// <summary>
61
77
/// Initialize a decision service for the Optimizely client.
62
78
/// </summary>
63
- /// <param name = "bucketer" > Base bucketer to allocate new users to an experiment.</param>
64
- /// <param name = "errorHandler" > The error handler of the Optimizely client.</param>
65
- /// <param name = "userProfileService" ></ param >
66
- /// < param name= "logger" > UserProfileService implementation for storing user info.</param>
79
+ /// <param name= "bucketer"> Base bucketer to allocate new users to an experiment.</param>
80
+ /// <param name= "errorHandler"> The error handler of the Optimizely client.</param>
81
+ /// <param name= "userProfileService">The injected implementation providing control over the bucketing.</ param >
82
+ /// < param name="logger"> UserProfileService implementation for storing user info.</param>
67
83
public DecisionService ( Bucketer bucketer , IErrorHandler errorHandler ,
68
84
UserProfileService userProfileService , ILogger logger
69
85
)
@@ -85,7 +101,7 @@ public DecisionService(Bucketer bucketer, IErrorHandler errorHandler,
85
101
/// Get a Variation of an Experiment for a user to be allocated into.
86
102
/// </summary>
87
103
/// <param name = "experiment" > The Experiment the user will be bucketed into.</param>
88
- /// <param name = "user" > Optimizely user context.
104
+ /// <param name = "user" > Optimizely user context.</param>
89
105
/// <param name = "config" > Project config.</param>
90
106
/// <returns>The Variation the user is allocated into.</returns>
91
107
public virtual Result < Variation > GetVariation ( Experiment experiment ,
@@ -99,10 +115,10 @@ ProjectConfig config
99
115
/// <summary>
100
116
/// Get a Variation of an Experiment for a user to be allocated into.
101
117
/// </summary>
102
- /// <param name = "experiment" > The Experiment the user will be bucketed into.</param>
103
- /// <param name = "user" > optimizely user context.
104
- /// <param name = "config" > Project Config.</param>
105
- /// <param name = "options" >An array of decision options.</param>
118
+ /// <param name= "experiment"> The Experiment the user will be bucketed into.</param>
119
+ /// <param name= "user"> optimizely user context.</param>
120
+ /// <param name= "config"> Project Config.</param>
121
+ /// <param name= "options">An array of decision options.</param>
106
122
/// <returns>The Variation the user is allocated into.</returns>
107
123
public virtual Result < Variation > GetVariation ( Experiment experiment ,
108
124
OptimizelyUserContext user ,
@@ -140,35 +156,42 @@ OptimizelyDecideOption[] options
140
156
var ignoreUPS = Array . Exists ( options ,
141
157
option => option == OptimizelyDecideOption . IGNORE_USER_PROFILE_SERVICE ) ;
142
158
143
- UserProfile userProfile = null ;
159
+
144
160
if ( ! ignoreUPS && UserProfileService != null )
145
161
{
146
162
try
147
163
{
148
- var userProfileMap = UserProfileService . Lookup ( user . GetUserId ( ) ) ;
149
- if ( userProfileMap != null &&
150
- UserProfileUtil . IsValidUserProfileMap ( userProfileMap ) )
164
+ if ( _userProfile == null )
165
+ {
166
+ var userProfileMap = UserProfileService . Lookup ( user . GetUserId ( ) ) ;
167
+ if ( userProfileMap != null &&
168
+ UserProfileUtil . IsValidUserProfileMap ( userProfileMap ) )
169
+ {
170
+ _userProfile = UserProfileUtil . ConvertMapToUserProfile ( userProfileMap ) ;
171
+ }
172
+ else if ( userProfileMap == null )
173
+ {
174
+ Logger . Log ( LogLevel . INFO ,
175
+ reasons . AddInfo (
176
+ "We were unable to get a user profile map from the UserProfileService." ) ) ;
177
+ }
178
+ else
179
+ {
180
+ Logger . Log ( LogLevel . ERROR ,
181
+ reasons . AddInfo ( "The UserProfileService returned an invalid map." ) ) ;
182
+ }
183
+ }
184
+
185
+ if ( _userProfile != null )
151
186
{
152
- userProfile = UserProfileUtil . ConvertMapToUserProfile ( userProfileMap ) ;
153
187
decisionVariationResult =
154
- GetStoredVariation ( experiment , userProfile , config ) ;
188
+ GetStoredVariation ( experiment , _userProfile , config ) ;
155
189
reasons += decisionVariationResult . DecisionReasons ;
156
190
if ( decisionVariationResult . ResultObject != null )
157
191
{
158
192
return decisionVariationResult . SetReasons ( reasons ) ;
159
193
}
160
194
}
161
- else if ( userProfileMap == null )
162
- {
163
- Logger . Log ( LogLevel . INFO ,
164
- reasons . AddInfo (
165
- "We were unable to get a user profile map from the UserProfileService." ) ) ;
166
- }
167
- else
168
- {
169
- Logger . Log ( LogLevel . ERROR ,
170
- reasons . AddInfo ( "The UserProfileService returned an invalid map." ) ) ;
171
- }
172
195
}
173
196
catch ( Exception exception )
174
197
{
@@ -197,11 +220,7 @@ OptimizelyDecideOption[] options
197
220
{
198
221
if ( UserProfileService != null && ! ignoreUPS )
199
222
{
200
- var bucketerUserProfile = userProfile ??
201
- new UserProfile ( userId ,
202
- new Dictionary < string , Decision > ( ) ) ;
203
- SaveVariation ( experiment , decisionVariationResult . ResultObject ,
204
- bucketerUserProfile ) ;
223
+ SaveVariation ( experiment , decisionVariationResult . ResultObject ) ;
205
224
}
206
225
else
207
226
{
@@ -454,12 +473,9 @@ ProjectConfig config
454
473
/// <summary>
455
474
/// Save a { @link Variation } of an { @link Experiment } for a user in the {@link UserProfileService}.
456
475
/// </summary>
457
- /// <param name = "experiment" > The experiment the user was buck</param>
458
- /// <param name = "variation" > The Variation to save.</param>
459
- /// <param name = "userProfile" > instance of the user information.</param>
460
- public void SaveVariation ( Experiment experiment , Variation variation ,
461
- UserProfile userProfile
462
- )
476
+ /// <param name="experiment">The experiment the user was buck</param>
477
+ /// <param name="variation">The Variation to save.</param>
478
+ public void SaveVariation ( Experiment experiment , Variation variation )
463
479
{
464
480
//only save if the user has implemented a user profile service
465
481
if ( UserProfileService == null )
@@ -468,28 +484,58 @@ UserProfile userProfile
468
484
}
469
485
470
486
Decision decision ;
471
- if ( userProfile . ExperimentBucketMap . ContainsKey ( experiment . Id ) )
487
+ if ( _userProfile . ExperimentBucketMap . ContainsKey ( experiment . Id ) )
472
488
{
473
- decision = userProfile . ExperimentBucketMap [ experiment . Id ] ;
489
+ decision = _userProfile . ExperimentBucketMap [ experiment . Id ] ;
474
490
decision . VariationId = variation . Id ;
475
491
}
476
492
else
477
493
{
478
494
decision = new Decision ( variation . Id ) ;
479
495
}
480
496
481
- userProfile . ExperimentBucketMap [ experiment . Id ] = decision ;
497
+ _userProfile . ExperimentBucketMap [ experiment . Id ] = decision ;
498
+
499
+ if ( ! _decisionBatchInProgress )
500
+ {
501
+ SaveToUserProfileService ( experiment , variation ) ;
502
+ }
503
+ }
482
504
505
+ private void SaveToUserProfileService ( Experiment experiment = null ,
506
+ Variation variation = null
507
+ )
508
+ {
509
+ var useSpecificLogEntry = experiment != null && variation != null &&
510
+ ! string . IsNullOrEmpty ( _userProfile ? . UserId ) ;
511
+
483
512
try
484
513
{
485
- UserProfileService . Save ( userProfile . ToMap ( ) ) ;
486
- Logger . Log ( LogLevel . INFO ,
487
- $ "Saved variation \" { variation . Id } \" of experiment \" { experiment . Id } \" for user \" { userProfile . UserId } \" .") ;
514
+ if ( _userProfile != null )
515
+ {
516
+ UserProfileService . Save ( _userProfile . ToMap ( ) ) ;
517
+ if ( useSpecificLogEntry )
518
+ {
519
+ Logger . Log ( LogLevel . INFO ,
520
+ $ "Saved variation \" { variation . Id } \" of experiment \" { experiment . Id } \" for user \" { _userProfile . UserId } \" .") ;
521
+ }
522
+ else
523
+ {
524
+ Logger . Log ( LogLevel . INFO , "Saved user profile after batch decision." ) ;
525
+ }
526
+ }
488
527
}
489
528
catch ( Exception exception )
490
529
{
491
- Logger . Log ( LogLevel . ERROR ,
492
- $ "Failed to save variation \" { variation . Id } \" of experiment \" { experiment . Id } \" for user \" { userProfile . UserId } \" .") ;
530
+ if ( useSpecificLogEntry )
531
+ {
532
+ Logger . Log ( LogLevel . ERROR ,
533
+ $ "Failed to save variation \" { variation . Id } \" of experiment \" { experiment . Id } \" for user \" { _userProfile . UserId } \" .") ;
534
+ }
535
+ else
536
+ {
537
+ Logger . Log ( LogLevel . ERROR , "Failed to save user profile after batch decision." ) ;
538
+ }
493
539
ErrorHandler . HandleError (
494
540
new Exceptions . OptimizelyRuntimeException ( exception . Message ) ) ;
495
541
}
0 commit comments