@@ -23,9 +23,26 @@ struct FeatureDecision {
23
23
}
24
24
25
25
class DefaultDecisionService : OPTDecisionService {
26
+ typealias UserProfile = OPTUserProfileService . UPProfile
26
27
28
+ private var _decisionBatchInProgress : Bool = false
29
+
30
+ var decisionBatchInProgress : Bool {
31
+ get {
32
+ return _decisionBatchInProgress
33
+ }
34
+ set {
35
+ // Only save if the value is changing from true to false
36
+ if _decisionBatchInProgress && !newValue {
37
+ saveProfile ( )
38
+ }
39
+ _decisionBatchInProgress = newValue
40
+ }
41
+ }
42
+
27
43
let bucketer : OPTBucketer
28
44
let userProfileService : OPTUserProfileService
45
+ private var userProfile : UserProfile ?
29
46
30
47
// thread-safe lazy logger load (after HandlerRegisterService ready)
31
48
private let threadSafeLogger = ThreadSafeLogger ( )
@@ -88,16 +105,26 @@ class DefaultDecisionService: OPTDecisionService {
88
105
// ---- check if a valid variation is stored in the user profile ----
89
106
let ignoreUPS = ( options ?? [ ] ) . contains ( . ignoreUserProfileService)
90
107
91
- if !ignoreUPS,
92
- let variationId = getVariationIdFromProfile ( userId: userId, experimentId: experimentId) ,
93
- let variation = experiment. getVariation ( id: variationId) {
108
+ if !ignoreUPS {
109
+ if userProfile == nil {
110
+ userProfile = userProfileService. lookup ( userId: userId)
111
+ }
112
+
113
+ if let profile = userProfile {
114
+ if let variationId = getVariationIdFromProfile ( userId: userId, profile: profile, experimentId: experimentId) ,
115
+ let variation = experiment. getVariation ( id: variationId) {
116
+ let info = LogMessage . gotVariationFromUserProfile ( variation. key, experiment. key, userId)
117
+ logger. i ( info)
118
+ reasons. addInfo ( info)
119
+ return DecisionResponse ( result: variation, reasons: reasons)
120
+ }
121
+ } else {
122
+ let info = LogMessage . unableToGetUserProfile ( experiment. key, userId)
123
+ logger. i ( info)
124
+ }
94
125
95
- let info = LogMessage . gotVariationFromUserProfile ( variation. key, experiment. key, userId)
96
- logger. i ( info)
97
- reasons. addInfo ( info)
98
- return DecisionResponse ( result: variation, reasons: reasons)
99
126
}
100
-
127
+
101
128
var bucketedVariation : Variation ?
102
129
// ---- check if the user passes audience targeting before bucketing ----
103
130
let audienceResponse = doesMeetAudienceConditions ( config: config,
@@ -118,7 +145,8 @@ class DefaultDecisionService: OPTDecisionService {
118
145
reasons. addInfo ( info)
119
146
// save to user profile
120
147
if !ignoreUPS {
121
- self . saveProfile ( userId: userId, experimentId: experimentId, variationId: variation. id)
148
+ let buckerUserProfile = userProfile ?? UserProfile ( )
149
+ updateVariation ( userId: userId, profile: buckerUserProfile, experimentId: experimentId, variationId: variation. key)
122
150
}
123
151
} else {
124
152
let info = LogMessage . userNotBucketedIntoVariation ( userId)
@@ -454,9 +482,9 @@ class DefaultDecisionService: OPTDecisionService {
454
482
extension DefaultDecisionService {
455
483
456
484
func getVariationIdFromProfile( userId: String ,
485
+ profile: UserProfile ,
457
486
experimentId: String ) -> String ? {
458
- if let profile = userProfileService. lookup ( userId: userId) ,
459
- let bucketMap = profile [ UserProfileKeys . kBucketMap] as? OPTUserProfileService . UPBucketMap ,
487
+ if let bucketMap = profile [ UserProfileKeys . kBucketMap] as? OPTUserProfileService . UPBucketMap ,
460
488
let experimentMap = bucketMap [ experimentId] ,
461
489
let variationId = experimentMap [ UserProfileKeys . kVariationId] {
462
490
return variationId
@@ -465,22 +493,33 @@ extension DefaultDecisionService {
465
493
}
466
494
}
467
495
468
- func saveProfile( userId: String ,
469
- experimentId: String ,
470
- variationId: String ) {
496
+ func updateVariation( userId: String ,
497
+ profile: UserProfile ,
498
+ experimentId: String ,
499
+ variationId: String ) {
471
500
DefaultDecisionService . upsRMWLock. sync {
472
- var profile = self . userProfileService. lookup ( userId: userId) ?? OPTUserProfileService . UPProfile ( )
473
-
474
- var bucketMap = profile [ UserProfileKeys . kBucketMap] as? OPTUserProfileService . UPBucketMap ?? OPTUserProfileService . UPBucketMap ( )
501
+ var _profile = profile
502
+ var bucketMap = _profile [ UserProfileKeys . kBucketMap] as? OPTUserProfileService . UPBucketMap ?? OPTUserProfileService . UPBucketMap ( )
475
503
bucketMap [ experimentId] = [ UserProfileKeys . kVariationId: variationId]
476
504
477
- profile [ UserProfileKeys . kBucketMap] = bucketMap
478
- profile [ UserProfileKeys . kUserId] = userId
505
+ _profile [ UserProfileKeys . kBucketMap] = bucketMap
506
+ _profile [ UserProfileKeys . kUserId] = userId
479
507
480
- self . userProfileService. save ( userProfile: profile)
508
+ /// Update user profile
509
+ userProfile = _profile
481
510
482
- self . logger. i ( . savedVariationInUserProfile( variationId, experimentId, userId) )
511
+ if !_decisionBatchInProgress {
512
+ saveProfile ( userId: userId, experimentId: experimentId, variationId: variationId)
513
+ }
483
514
}
484
515
}
485
516
517
+ func saveProfile( userId: String ? = nil , experimentId: String ? = nil , variationId: String ? = nil ) {
518
+ guard let profile = userProfile else { return }
519
+
520
+ self . userProfileService. save ( userProfile: profile)
521
+
522
+ self . logger. i ( . savedVariationInUserProfile( variationId ?? " " , experimentId ?? " " , userId ?? " " ) )
523
+ }
524
+
486
525
}
0 commit comments