Skip to content

Commit c3647ad

Browse files
committed
add comments addressed
1 parent bb221a5 commit c3647ad

File tree

5 files changed

+422
-33
lines changed

5 files changed

+422
-33
lines changed

lib/optimizely/config/datafile_project_config.rb

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,7 @@ def initialize(datafile, logger, error_handler)
142142
@rollout_experiment_id_map = @rollout_experiment_id_map.merge(generate_key_map(exps, 'id'))
143143
end
144144

145-
@feature_flags.each do |flag|
146-
variations = []
147-
get_rules_for_flag(flag).each do |rule|
148-
rule['variations'].each do |rule_variation|
149-
variations.push(rule_variation) if variations.select { |variation| variation['id'] == rule_variation['id'] }
150-
end
151-
end
152-
@flag_variation_map[flag['key']] = variations
153-
end
154-
145+
@flag_variation_map = generate_feature_variation_map(@feature_flags)
155146
@all_experiments = @experiment_id_map.merge(@rollout_experiment_id_map)
156147
@all_experiments.each do |id, exp|
157148
variations = exp.fetch('variations')
@@ -533,6 +524,20 @@ def rollout_experiment?(experiment_id)
533524

534525
private
535526

527+
def generate_feature_variation_map(feature_flags)
528+
flag_variation_map = {}
529+
feature_flags.each do |flag|
530+
variations = []
531+
get_rules_for_flag(flag).each do |rule|
532+
rule['variations'].each do |rule_variation|
533+
variations.push(rule_variation) if variations.select { |variation| variation['id'] == rule_variation['id'] }.empty?
534+
end
535+
end
536+
flag_variation_map[flag['key']] = variations
537+
end
538+
flag_variation_map
539+
end
540+
536541
def generate_key_map(array, key)
537542
# Helper method to generate map from key to hash in array of hashes
538543
#

lib/optimizely/optimizely_user_context.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class OptimizelyUserContext
2929
ForcedDecision = Struct.new(:flag_key, :rule_key)
3030
def initialize(optimizely_client, user_id, user_attributes)
3131
@attr_mutex = Mutex.new
32+
@forced_decision_mutex = Mutex.new
3233
@optimizely_client = optimizely_client
3334
@user_id = user_id
3435
@user_attributes = user_attributes.nil? ? {} : user_attributes.clone
@@ -37,7 +38,7 @@ def initialize(optimizely_client, user_id, user_attributes)
3738

3839
def clone
3940
user_context = OptimizelyUserContext.new(@optimizely_client, @user_id, user_attributes)
40-
user_context.instance_variable_set('@forced_decisions', @forced_decisions.dup) unless @forced_decisions.empty?
41+
@forced_decision_mutex.synchronize { user_context.instance_variable_set('@forced_decisions', @forced_decisions.dup) unless @forced_decisions.empty? }
4142
user_context
4243
end
4344

@@ -104,19 +105,18 @@ def set_forced_decision(flag_key, rule_key, variation_key)
104105
return false if flag_key.empty? || flag_key.nil?
105106

106107
forced_decision_key = ForcedDecision.new(flag_key, rule_key)
107-
@forced_decisions[forced_decision_key] = variation_key
108+
@forced_decision_mutex.synchronize { @forced_decisions[forced_decision_key] = variation_key }
108109

109110
true
110111
end
111112

112113
def find_forced_decision(flag_key, rule_key = nil)
113114
return nil if @forced_decisions.empty?
114115

116+
variation_key = nil
115117
forced_decision_key = ForcedDecision.new(flag_key, rule_key)
116-
variation_key = @forced_decisions[forced_decision_key]
117-
return variation_key if variation_key
118-
119-
nil
118+
@forced_decision_mutex.synchronize { variation_key = @forced_decisions[forced_decision_key] }
119+
variation_key
120120
end
121121

122122
# Returns the forced decision for a given flag and an optional rule.
@@ -143,12 +143,14 @@ def remove_forced_decision(flag_key, rule_key = nil)
143143
return false if @optimizely_client&.get_optimizely_config.nil?
144144

145145
forced_decision_key = ForcedDecision.new(flag_key, rule_key)
146-
if @forced_decisions.key?(forced_decision_key)
147-
@forced_decisions.delete(forced_decision_key)
148-
return true
146+
deleted = false
147+
@forced_decision_mutex.synchronize do
148+
if @forced_decisions.key?(forced_decision_key)
149+
@forced_decisions.delete(forced_decision_key)
150+
deleted = true
151+
end
149152
end
150-
151-
false
153+
deleted
152154
end
153155

154156
# Removes all forced decisions bound to this user context.
@@ -158,7 +160,7 @@ def remove_forced_decision(flag_key, rule_key = nil)
158160
def remove_all_forced_decision
159161
return false if @optimizely_client&.get_optimizely_config.nil?
160162

161-
@forced_decisions.clear
163+
@forced_decision_mutex.synchronize { @forced_decisions.clear }
162164
true
163165
end
164166

spec/config/datafile_project_config_spec.rb

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
describe Optimizely::DatafileProjectConfig do
2424
let(:config_body) { OptimizelySpec::VALID_CONFIG_BODY }
2525
let(:config_body_JSON) { OptimizelySpec::VALID_CONFIG_BODY_JSON }
26+
let(:decision_JSON) { OptimizelySpec::DECIDE_FORCED_DECISION_JSON }
2627
let(:error_handler) { Optimizely::NoOpErrorHandler.new }
2728
let(:logger) { Optimizely::NoOpLogger.new }
2829
let(:config) { Optimizely::DatafileProjectConfig.new(config_body_JSON, logger, error_handler) }
@@ -1073,4 +1074,56 @@
10731074
expect(config.rollout_experiment?('177771')).to eq(false)
10741075
end
10751076
end
1077+
1078+
describe '#feature variation map' do
1079+
let(:config) { Optimizely::DatafileProjectConfig.new(decision_JSON, logger, error_handler) }
1080+
1081+
it 'should return valid flag variation map without duplicates' do
1082+
# variation '3324490634' is repeated in datafile but should appear once in map
1083+
expected_feature_variation_map = {
1084+
'feature_1' => [{
1085+
'variables' => [],
1086+
'featureEnabled' => true,
1087+
'id' => '10389729780',
1088+
'key' => 'a'
1089+
}, {
1090+
'variables' => [],
1091+
'id' => '10416523121',
1092+
'key' => 'b',
1093+
'featureEnabled' => false
1094+
}, {
1095+
'featureEnabled' => true,
1096+
'id' => '3324490633',
1097+
'key' => '3324490633',
1098+
'variables' => []
1099+
}, {
1100+
'featureEnabled' => true,
1101+
'id' => '3324490634',
1102+
'key' => '3324490634',
1103+
'variables' => []
1104+
}, {
1105+
'featureEnabled' => true,
1106+
'id' => '3324490562',
1107+
'key' => '3324490562',
1108+
'variables' => []
1109+
}, {
1110+
'variables' => [],
1111+
'id' => '18257766532',
1112+
'key' => '18257766532',
1113+
'featureEnabled' => true
1114+
}], 'feature_2' => [{
1115+
'variables' => [],
1116+
'featureEnabled' => true,
1117+
'id' => '10418551353',
1118+
'key' => 'variation_with_traffic'
1119+
}, {
1120+
'variables' => [],
1121+
'featureEnabled' => false,
1122+
'id' => '10418510624',
1123+
'key' => 'variation_no_traffic'
1124+
}], 'feature_3' => []
1125+
}
1126+
expect(config.send(:generate_feature_variation_map, config.feature_flags)).to eq(expected_feature_variation_map)
1127+
end
1128+
end
10761129
end

0 commit comments

Comments
 (0)