Skip to content

Commit 445006b

Browse files
Add seperate method for group exlusion, add bucketToEntityId method
1 parent e827b27 commit 445006b

File tree

1 file changed

+87
-85
lines changed

1 file changed

+87
-85
lines changed

Sources/Implementation/DefaultBucketer.swift

Lines changed: 87 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -37,43 +37,16 @@ class DefaultBucketer: OPTBucketer {
3737
bucketingId: String) -> DecisionResponse<Variation> {
3838
let reasons = DecisionReasons()
3939

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)
7750
}
7851

7952
if !mutexAllowed { return DecisionResponse(result: nil, reasons: reasons) }
@@ -120,53 +93,82 @@ class DefaultBucketer: OPTBucketer {
12093
return DecisionResponse(result: nil, reasons: reasons)
12194
}
12295

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+
}
170172

171173
func bucketToVariation(experiment: ExperimentCore,
172174
bucketingId: String) -> DecisionResponse<Variation> {
@@ -201,7 +203,7 @@ class DefaultBucketer: OPTBucketer {
201203
for bucket in trafficAllocation where bucketValue < bucket.endOfRange {
202204
return bucket.entityId
203205
}
204-
206+
205207
return nil
206208
}
207209

0 commit comments

Comments
 (0)