Skip to content

Commit 98ff2b6

Browse files
Duplicate experiment Key issue with multi feature flag (#347)
* Changing lookup of experiment in decision service to use experiment ID instead of key to resolve bug * [BUGFIX] - Update to bucketer and added proper ID map to project_config. * Switch to generate experiments_key_map from experiment_id_map rather than the other way around. * Updated with unit tests. * Change line break before operator to avoid W504 Flake8 issue. * Flake8 changes to follow best practice and ignore the W504
1 parent bc98adc commit 98ff2b6

File tree

4 files changed

+79
-10
lines changed

4 files changed

+79
-10
lines changed

.flake8

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[flake8]
22
# E722 - do not use bare 'except'
3-
ignore = E722
3+
# W504 - Either W503 (Line break after Operand) or W503 (
4+
# Line break before operand needs to be ignored for line lengths
5+
# greater than max-line-length. Best practice shows W504
6+
ignore = E722, W504
47
exclude = optimizely/lib/pymmh3.py,*virtualenv*
58
max-line-length = 120

optimizely/bucketer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def bucket(self, project_config, experiment, user_id, bucketing_id):
142142
variation_id = self.find_bucket(project_config, bucketing_id,
143143
experiment.id, experiment.trafficAllocation)
144144
if variation_id:
145-
variation = project_config.get_variation_from_id(experiment.key, variation_id)
145+
variation = project_config.get_variation_from_id_by_experiment_id(experiment.id, variation_id)
146146
return variation, decide_reasons
147147

148148
else:

optimizely/project_config.py

+49-8
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def __init__(self, datafile, logger, error_handler):
6868

6969
# Utility maps for quick lookup
7070
self.group_id_map = self._generate_key_map(self.groups, 'id', entities.Group)
71-
self.experiment_key_map = self._generate_key_map(self.experiments, 'key', entities.Experiment)
71+
self.experiment_id_map = self._generate_key_map(self.experiments, 'id', entities.Experiment)
7272
self.event_key_map = self._generate_key_map(self.events, 'key', entities.Event)
7373
self.attribute_key_map = self._generate_key_map(self.attributes, 'key', entities.Attribute)
7474

@@ -84,27 +84,36 @@ def __init__(self, datafile, logger, error_handler):
8484
self.rollout_id_map = self._generate_key_map(self.rollouts, 'id', entities.Layer)
8585
for layer in self.rollout_id_map.values():
8686
for experiment in layer.experiments:
87-
self.experiment_key_map[experiment['key']] = entities.Experiment(**experiment)
87+
self.experiment_id_map[experiment['id']] = entities.Experiment(**experiment)
8888

8989
self.audience_id_map = self._deserialize_audience(self.audience_id_map)
9090
for group in self.group_id_map.values():
91-
experiments_in_group_key_map = self._generate_key_map(group.experiments, 'key', entities.Experiment)
92-
for experiment in experiments_in_group_key_map.values():
91+
experiments_in_group_id_map = self._generate_key_map(group.experiments, 'id', entities.Experiment)
92+
for experiment in experiments_in_group_id_map.values():
9393
experiment.__dict__.update({'groupId': group.id, 'groupPolicy': group.policy})
94-
self.experiment_key_map.update(experiments_in_group_key_map)
94+
self.experiment_id_map.update(experiments_in_group_id_map)
9595

96-
self.experiment_id_map = {}
96+
self.experiment_key_map = {}
9797
self.variation_key_map = {}
9898
self.variation_id_map = {}
9999
self.variation_variable_usage_map = {}
100-
for experiment in self.experiment_key_map.values():
101-
self.experiment_id_map[experiment.id] = experiment
100+
self.variation_id_map_by_experiment_id = {}
101+
self.variation_key_map_by_experiment_id = {}
102+
103+
for experiment in self.experiment_id_map.values():
104+
self.experiment_key_map[experiment.key] = experiment
102105
self.variation_key_map[experiment.key] = self._generate_key_map(
103106
experiment.variations, 'key', entities.Variation
104107
)
108+
105109
self.variation_id_map[experiment.key] = {}
110+
self.variation_id_map_by_experiment_id[experiment.id] = {}
111+
self.variation_key_map_by_experiment_id[experiment.id] = {}
112+
106113
for variation in self.variation_key_map.get(experiment.key).values():
107114
self.variation_id_map[experiment.key][variation.id] = variation
115+
self.variation_id_map_by_experiment_id[experiment.id][variation.id] = variation
116+
self.variation_key_map_by_experiment_id[experiment.id][variation.key] = variation
108117
self.variation_variable_usage_map[variation.id] = self._generate_key_map(
109118
variation.variables, 'id', entities.Variation.VariableUsage
110119
)
@@ -557,3 +566,35 @@ def is_feature_experiment(self, experiment_id):
557566
"""
558567

559568
return experiment_id in self.experiment_feature_map
569+
570+
def get_variation_from_id_by_experiment_id(self, experiment_id, variation_id):
571+
""" Gets variation from variation id and specific experiment id
572+
573+
Returns:
574+
The variation for the experiment id and variation id
575+
or empty dict if not found
576+
"""
577+
if (experiment_id in self.variation_id_map_by_experiment_id and
578+
variation_id in self.variation_id_map_by_experiment_id[experiment_id]):
579+
return self.variation_id_map_by_experiment_id[experiment_id][variation_id]
580+
581+
self.logger.error('Variation with id "%s" not defined in the datafile for experiment "%s".',
582+
variation_id, experiment_id)
583+
584+
return {}
585+
586+
def get_variation_from_key_by_experiment_id(self, experiment_id, variation_key):
587+
""" Gets variation from variation key and specific experiment id
588+
589+
Returns:
590+
The variation for the experiment id and variation key
591+
or empty dict if not found
592+
"""
593+
if (experiment_id in self.variation_key_map_by_experiment_id and
594+
variation_key in self.variation_key_map_by_experiment_id[experiment_id]):
595+
return self.variation_key_map_by_experiment_id[experiment_id][variation_key]
596+
597+
self.logger.error('Variation with key "%s" not defined in the datafile for experiment "%s".',
598+
variation_key, experiment_id)
599+
600+
return {}

tests/test_config.py

+25
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_init(self):
4141
self.config_dict['groups'][0]['trafficAllocation'],
4242
)
4343
}
44+
4445
expected_experiment_key_map = {
4546
'test_experiment': entities.Experiment(
4647
'111127',
@@ -1213,3 +1214,27 @@ def test_is_feature_experiment(self):
12131214

12141215
self.assertStrictFalse(project_config.is_feature_experiment(experiment.id))
12151216
self.assertStrictTrue(project_config.is_feature_experiment(feature_experiment.id))
1217+
1218+
def test_get_variation_from_id_by_experiment_id(self):
1219+
1220+
opt_obj = optimizely.Optimizely(json.dumps(self.config_dict))
1221+
project_config = opt_obj.config_manager.get_config()
1222+
1223+
experiment_id = '111127'
1224+
variation_id = '111128'
1225+
1226+
variation = project_config.get_variation_from_id_by_experiment_id(experiment_id, variation_id)
1227+
1228+
self.assertIsInstance(variation, entities.Variation)
1229+
1230+
def test_get_variation_from_key_by_experiment_id(self):
1231+
1232+
opt_obj = optimizely.Optimizely(json.dumps(self.config_dict))
1233+
project_config = opt_obj.config_manager.get_config()
1234+
1235+
experiment_id = '111127'
1236+
variation_key = 'control'
1237+
1238+
variation = project_config.get_variation_from_key_by_experiment_id(experiment_id, variation_key)
1239+
1240+
self.assertIsInstance(variation, entities.Variation)

0 commit comments

Comments
 (0)