@@ -37,43 +37,16 @@ class DefaultBucketer: OPTBucketer {
37
37
bucketingId: String ) -> DecisionResponse < Variation > {
38
38
let reasons = DecisionReasons ( )
39
39
40
- var mutexAllowed = true
41
-
42
- // check for mutex
43
-
44
- let group = config. project. groups. filter { $0. getExperiment ( id: experiment. id) != nil } . first
45
-
46
- if let group = group {
47
- switch group. policy {
48
- case . overlapping:
49
- break
50
- case . random:
51
- let decisionResponse = bucketToExperiment ( config: config,
52
- group: group,
53
- bucketingId: bucketingId)
54
- reasons. merge ( decisionResponse. reasons)
55
- if let mutexExperiment = decisionResponse. result {
56
- if mutexExperiment. id == experiment. id {
57
- mutexAllowed = true
58
-
59
- let info = LogMessage . userBucketedIntoExperimentInGroup ( bucketingId, experiment. key, group. id)
60
- logger. i ( info)
61
- reasons. addInfo ( info)
62
- } else {
63
- mutexAllowed = false
64
-
65
- let info = LogMessage . userNotBucketedIntoExperimentInGroup ( bucketingId, experiment. key, group. id)
66
- logger. i ( info)
67
- reasons. addInfo ( info)
68
- }
69
- } else {
70
- mutexAllowed = false
71
-
72
- let info = LogMessage . userNotBucketedIntoAnyExperimentInGroup ( bucketingId, group. id)
73
- logger. i ( info)
74
- reasons. addInfo ( info)
75
- }
76
- }
40
+ // Check mutex rules
41
+ let mutexAllowed = checkMutexRules (
42
+ config: config,
43
+ experiment: experiment,
44
+ bucketingId: bucketingId,
45
+ reasons: reasons
46
+ )
47
+
48
+ if !mutexAllowed {
49
+ return DecisionResponse ( result: nil , reasons: reasons)
77
50
}
78
51
79
52
if !mutexAllowed { return DecisionResponse ( result: nil , reasons: reasons) }
@@ -120,53 +93,82 @@ class DefaultBucketer: OPTBucketer {
120
93
return DecisionResponse ( result: nil , reasons: reasons)
121
94
}
122
95
123
- // func bucketToEntityId(bucketingId: String,
124
- // experiment: Experiment,
125
- // trafficAllocation: [TrafficAllocation],
126
- // group: Group?) -> DecisionResponse<String> {
127
- // let reasons = DecisionReasons()
128
- //
129
- // if let group = group, group.policy == .random {
130
- // let hashId = makeHashIdFromBucketingId(bucketingId: bucketingId, entityId: group.id)
131
- // let bucketValue = self.generateBucketValue(bucketingId: hashId)
132
- //
133
- // var matched = false
134
- // for allocation in group.trafficAllocation {
135
- // if bucketValue < allocation.endOfRange {
136
- // matched = true
137
- // if allocation.entityId != experiment.id {
138
- // let info = LogMessage.userNotBucketedIntoExperimentInGroup(bucketingId, experiment.key, group.id)
139
- // reasons.addInfo(info)
140
- // return DecisionResponse(result: nil, reasons: reasons)
141
- // }
142
- //
143
- // let info = LogMessage.userBucketedIntoExperimentInGroup(bucketingId, experiment.key, group.id)
144
- // reasons.addInfo(info)
145
- // break
146
- // }
147
- // }
148
- //
149
- // if !matched {
150
- // let info = LogMessage.userNotBucketedIntoAnyExperimentInGroup(bucketingId, group.id)
151
- // reasons.addInfo(info)
152
- // return DecisionResponse(result: nil, reasons: reasons)
153
- // }
154
- // }
155
- //
156
- // let hashId = makeHashIdFromBucketingId(bucketingId: bucketingId, entityId: experiment.id)
157
- // let bucketValue = self.generateBucketValue(bucketingId: hashId)
158
- //
159
- // for allocation in trafficAllocation {
160
- // if bucketValue < allocation.endOfRange {
161
- // let info = LogMessage.userBucketedIntoEntity(allocation.entityId)
162
- // reasons.addInfo(info)
163
- // return DecisionResponse(result: allocation.entityId, reasons: reasons)
164
- // }
165
- // }
166
- // let info = LogMessage.userNotBucketedIntoAnyEntity
167
- // reasons.addInfo(info)
168
- // return DecisionResponse(result: nil, reasons: reasons)
169
- // }
96
+ /// Checks if an experiment is allowed to run based on mutex rules
97
+ /// - Parameters:
98
+ /// - config: The project configuration
99
+ /// - experiment: The experiment to check
100
+ /// - bucketingId: The bucketing ID for the user
101
+ /// - reasons: Decision reasons to track the mutex check process
102
+ /// - Returns: A boolean indicating if the experiment is allowed to run
103
+ private func checkMutexRules(
104
+ config: ProjectConfig ,
105
+ experiment: Experiment ,
106
+ bucketingId: String ,
107
+ reasons: DecisionReasons
108
+ ) -> Bool {
109
+ // Find the group containing this experiment
110
+ let group = config. project. groups. filter { $0. getExperiment ( id: experiment. id) != nil } . first
111
+
112
+ guard let group = group else {
113
+ return true // No group found, experiment is allowed
114
+ }
115
+
116
+ switch group. policy {
117
+ case . overlapping:
118
+ return true // Overlapping experiments are always allowed
119
+
120
+ case . random:
121
+ let decisionResponse = bucketToExperiment (
122
+ config: config,
123
+ group: group,
124
+ bucketingId: bucketingId
125
+ )
126
+ reasons. merge ( decisionResponse. reasons)
127
+
128
+ guard let mutexExperiment = decisionResponse. result else {
129
+ let info = LogMessage . userNotBucketedIntoAnyExperimentInGroup ( bucketingId, group. id)
130
+ logger. i ( info)
131
+ reasons. addInfo ( info)
132
+ return false
133
+ }
134
+
135
+ let isAllowed = mutexExperiment. id == experiment. id
136
+ let info = isAllowed
137
+ ? LogMessage . userBucketedIntoExperimentInGroup ( bucketingId, experiment. key, group. id)
138
+ : LogMessage . userNotBucketedIntoExperimentInGroup ( bucketingId, experiment. key, group. id)
139
+
140
+ logger. i ( info)
141
+ reasons. addInfo ( info)
142
+ return isAllowed
143
+ }
144
+ }
145
+
146
+ func bucketToEntityId( config: ProjectConfig ,
147
+ experiment: Experiment ,
148
+ bucketingId: String ,
149
+ trafficAllocation: [ TrafficAllocation ] ) -> DecisionResponse < String > {
150
+
151
+ let reasons = DecisionReasons ( )
152
+
153
+ // Check mutex rules
154
+ let mutexAllowed = checkMutexRules (
155
+ config: config,
156
+ experiment: experiment,
157
+ bucketingId: bucketingId,
158
+ reasons: reasons
159
+ )
160
+
161
+ if !mutexAllowed {
162
+ return DecisionResponse ( result: nil , reasons: reasons)
163
+ }
164
+
165
+ let hashId = makeHashIdFromBucketingId ( bucketingId: bucketingId, entityId: experiment. id)
166
+ let bucketValue = self . generateBucketValue ( bucketingId: hashId)
167
+
168
+ let entityId = allocateTraffic ( trafficAllocation: trafficAllocation, bucketValue: bucketValue)
169
+
170
+ return DecisionResponse ( result: entityId, reasons: reasons)
171
+ }
170
172
171
173
func bucketToVariation( experiment: ExperimentCore ,
172
174
bucketingId: String ) -> DecisionResponse < Variation > {
@@ -201,7 +203,7 @@ class DefaultBucketer: OPTBucketer {
201
203
for bucket in trafficAllocation where bucketValue < bucket. endOfRange {
202
204
return bucket. entityId
203
205
}
204
-
206
+
205
207
return nil
206
208
}
207
209
0 commit comments