-
Notifications
You must be signed in to change notification settings - Fork 28
feat: Duplicate experiment key issue with multi feature flag #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please review the logic, you can extract experiment_key_map from the experiment_id_map, other way will cause issues.
@@ -117,6 +119,8 @@ def initialize(datafile, logger, error_handler) | |||
@audience_id_map = @audience_id_map.merge(generate_key_map(@typed_audiences, 'id')) unless @typed_audiences.empty? | |||
@variation_id_map = {} | |||
@variation_key_map = {} | |||
@variation_id_map_by_experiment_id = {} | |||
@variation_key_map_by_experiment_id = {} | |||
@variation_id_to_variable_usage_map = {} | |||
@variation_id_to_experiment_map = {} | |||
@experiment_key_map.each_value do |exp| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't do it using experiment_map, do it using experiment_id
@@ -132,9 +136,9 @@ def initialize(datafile, logger, error_handler) | |||
@rollout_experiment_key_map = {} | |||
@rollout_id_map.each_value do |rollout| | |||
exps = rollout.fetch('experiments') | |||
@rollout_experiment_key_map = @rollout_experiment_key_map.merge(generate_key_map(exps, 'key')) | |||
@rollout_experiment_key_map = @rollout_experiment_key_map.merge(generate_key_map(exps, 'id')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add all of your logics based on experiment_ids, not on experiment_keys.
@@ -736,7 +736,7 @@ | |||
'177776' => config_body['rollouts'][0]['experiments'][2], | |||
'177774' => config_body['rollouts'][1]['experiments'][0], | |||
'177779' => config_body['rollouts'][1]['experiments'][1], | |||
'rollout_exp_with_diff_id_and_key' => config_body['rollouts'][1]['experiments'][2] | |||
'177780' => config_body['rollouts'][1]['experiments'][2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this change is needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM, Just update headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes the delivery rule-key conflict. I still see potential issue with experiment key conflicts. I believe they'll covered by new FSC test cases.
@@ -304,17 +371,17 @@ def get_variation_id_from_key(experiment_key, variation_key) | |||
nil | |||
end | |||
|
|||
def get_whitelisted_variations(experiment_key) | |||
def get_whitelisted_variations(experiment_id) | |||
# Retrieves whitelisted variations for a given experiment Key | |||
# | |||
# experiment_key - String Key representing the experiment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix this comment
lib/optimizely/decision_service.rb
Outdated
@@ -347,29 +346,30 @@ def get_forced_variation(project_config, experiment_key, user_id) | |||
end | |||
|
|||
experiment_to_variation_map = @forced_variation_map[user_id] | |||
# to be corrected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason of this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to change experiment-key to -id based.
For log and decision messages, please keep all 'experiment-key'as is (instead of using 'experiment-id' which is not helpful to users).
lib/optimizely.rb
Outdated
@@ -1095,11 +1097,11 @@ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabl | |||
@event_processor.process(user_event) | |||
return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive? | |||
|
|||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.") | |||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_id}'.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep experiment_key in log
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @jaeopt on this. Also adding experiment_id can actually help too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having both will be helpful, but for consistency with all other SDKs, let's stick to "key" only in logs and messages
lib/optimizely/decision_service.rb
Outdated
unless project_config.experiment_running?(experiment) | ||
message = "Experiment '#{experiment_key}' is not running." | ||
message = "Experiment '#{experiment_id}' is not running." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep experiment_key for log and message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or Keep both.
lib/optimizely/decision_service.rb
Outdated
@@ -108,7 +107,7 @@ def get_variation(project_config, experiment_key, user_id, attributes = nil, dec | |||
user_meets_audience_conditions, reasons_received = Audience.user_meets_audience_conditions?(project_config, experiment, attributes, @logger) | |||
decide_reasons.push(*reasons_received) | |||
unless user_meets_audience_conditions | |||
message = "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'." | |||
message = "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_id}'." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep experiment_key in all messages here and others
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Agree! An i think you should keep both id and key in the messages
lib/optimizely.rb
Outdated
@@ -1095,11 +1097,11 @@ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabl | |||
@event_processor.process(user_event) | |||
return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive? | |||
|
|||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.") | |||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_id}'.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @jaeopt on this. Also adding experiment_id can actually help too
@variation_id_map[exp['key']] = generate_key_map(variations, 'id') | ||
@variation_key_map[exp['key']] = generate_key_map(variations, 'key') | ||
@variation_id_map_by_experiment_id[id] = generate_key_map(variations, 'id') | ||
@variation_key_map_by_experiment_id[id] = generate_key_map(variations, 'key') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These variable names are really confusing. Looking at variation_id_map
, it looks like its a map of variations with variation ids as keys but looks like the key is the experiment id which contains all the variations for that experiment. We might want to consider refactoring it later for better and more intuitive naming. Or if possible and if it makes sense to you , can you rename these variables to reflect more of what they actually are. variation_id_map
can probably be variation_map_by_experiment_id
etc. You can certainly think of a better variable name or can also leave it like that to refactor it later, i wont block the review on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's talk offline.
@@ -281,6 +302,52 @@ def get_variation_from_id(experiment_key, variation_id) | |||
nil | |||
end | |||
|
|||
def get_variation_from_id_by_experiment_id(experiment_id, variation_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can variation ids duplicate across experiments? why do we need experiment_id
if we have variation_id
. shouldn't variation_id
be enought to uniquely identify and fetch a variation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes as far as I know variation Ids are unique but I was following this method def get_variation_from_id(experiment_key, variation_id)
nil | ||
end | ||
|
||
def get_variation_id_from_key_by_experiment_id(experiment_id, variation_key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense because variation_key
can probably duplicate across experiments.
return experiment['forcedVariations'] if experiment | ||
|
||
@logger.log Logger::ERROR, "Experiment key '#{experiment_key}' is not in datafile." | ||
@logger.log Logger::ERROR, "Experiment key '#{experiment_id}' is not in datafile." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be helpful to keep both experiment_id
and experiment_key
in the log
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cant use experiment key here as we are no longer sending key in get_whitelisted_variations
instead we are sending the ID. If the given ID is wrong we cannot get an experiment and hence we can not get the key.
lib/optimizely/decision_service.rb
Outdated
unless project_config.experiment_running?(experiment) | ||
message = "Experiment '#{experiment_key}' is not running." | ||
message = "Experiment '#{experiment_id}' is not running." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or Keep both.
lib/optimizely/decision_service.rb
Outdated
@@ -108,7 +107,7 @@ def get_variation(project_config, experiment_key, user_id, attributes = nil, dec | |||
user_meets_audience_conditions, reasons_received = Audience.user_meets_audience_conditions?(project_config, experiment, attributes, @logger) | |||
decide_reasons.push(*reasons_received) | |||
unless user_meets_audience_conditions | |||
message = "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_key}'." | |||
message = "User '#{user_id}' does not meet the conditions to be in experiment '#{experiment_id}'." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Agree! An i think you should keep both id and key in the messages
lib/optimizely/decision_service.rb
Outdated
decide_reasons.push(*reasons_received) | ||
|
||
next unless variation_id | ||
|
||
variation = project_config.variation_id_map[experiment_key][variation_id] | ||
variation = project_config.variation_id_map_by_experiment_id[experiment_id][variation_id] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why accessing it directly from the map when you have an accessor method?
@variation_key_map[key] = generate_key_map(variations, 'key') | ||
@variation_id_map[exp['key']] = generate_key_map(variations, 'id') | ||
@variation_key_map[exp['key']] = generate_key_map(variations, 'key') | ||
@variation_id_map_by_experiment_id[id] = generate_key_map(variations, 'id') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need it by experiment id? i assume variation ids are unique? if yes, then a simple map with variation ids as keys and variations as values would suffice? It was probably required in case of variation keys because keys can be reused across the experiments and you needed to specify which experiment to look into, but for variation id, i am not sure if experiment info is needed at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not see the last reviews addressed yet. Added some more comments.
# | ||
# Returns the variation or nil if not found | ||
|
||
variation_key_map_by_experiment_id = @variation_key_map_by_experiment_id[experiment_id] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
variation_key_map_by_experiment_id = @variation_key_map_by_experiment_id[experiment_id] | |
variation_key_map = @variation_key_map_by_experiment_id[experiment_id] |
same name as the map will be confusing
|
||
variation_key_map_by_experiment_id = @variation_key_map_by_experiment_id[experiment_id] | ||
if variation_key_map_by_experiment_id | ||
variation = variation_key_map_by_experiment_id[variation_key] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
variation = variation_key_map_by_experiment_id[variation_key] | |
variation = variation_key_map[variation_key] |
lib/optimizely.rb
Outdated
@@ -1095,11 +1097,11 @@ def send_impression(config, experiment, variation_key, flag_key, rule_key, enabl | |||
@event_processor.process(user_event) | |||
return unless @notification_center.notification_count(NotificationCenter::NOTIFICATION_TYPES[:ACTIVATE]).positive? | |||
|
|||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_key}'.") | |||
@logger.log(Logger::INFO, "Activating user '#{user_id}' in experiment '#{experiment_id}'.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having both will be helpful, but for consistency with all other SDKs, let's stick to "key" only in logs and messages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Summary
TestPlan
https://travis-ci.com/github/optimizely/fullstack-sdk-compatibility-suite/jobs/523753247