Skip to content

Commit cd1bb98

Browse files
committed
improve feature flag caching
* properly cache nil per context * cache the full lookup in-proc only test plan: * quick regression test of feature flags Change-Id: I0d2435777bcc82d01301af96df81f179366b2fdc Reviewed-on: https://gerrit.instructure.com/28118 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com>
1 parent 6d18b1a commit cd1bb98

3 files changed

Lines changed: 36 additions & 5 deletions

File tree

lib/feature_flags.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ def feature_flag_cache_key(feature)
6565
# (helper method. use lookup_feature_flag to test policy.)
6666
def feature_flag(feature)
6767
self.shard.activate do
68-
Rails.cache.fetch(feature_flag_cache_key(feature)) do
69-
self.feature_flags.where(feature: feature.to_s).first
68+
result = Rails.cache.fetch(feature_flag_cache_key(feature)) do
69+
self.feature_flags.where(feature: feature.to_s).first || :nil
7070
end
71+
result = nil if result == :nil
72+
result
7173
end
7274
end
7375

@@ -99,6 +101,9 @@ def lookup_feature_flag(feature, override_hidden = false)
99101
# inherit the feature definition as a default unless it's a hidden feature
100102
retval = feature_def unless feature_def.hidden? && !is_site_admin && !(is_root_account && override_hidden)
101103

104+
@feature_flag_cache ||= {}
105+
return @feature_flag_cache[feature] if @feature_flag_cache.key?(feature)
106+
102107
# find the highest flag that doesn't allow override,
103108
# or the most specific flag otherwise
104109
account_ids = feature_flag_account_ids
@@ -119,11 +124,11 @@ def lookup_feature_flag(feature, override_hidden = false)
119124
retval = self.feature_flags.build feature: feature, state: 'off'
120125
else
121126
# the feature doesn't exist beneath the root account until the root account opts in
122-
return nil
127+
return @feature_flag_cache[feature] = nil
123128
end
124129
end
125130

126-
retval
131+
@feature_flag_cache[feature] = retval
127132
end
128133

129134
end

spec/lib/feature_flags_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@
7676
t_root_account.lookup_feature_flag('course_feature').context.should eql t_root_account
7777
t_course.feature_enabled?('course_feature').should be_true
7878
t_site_admin.feature_flags.create! feature: 'course_feature', state: 'off'
79+
t_root_account.instance_variable_set(:@feature_flag_cache, nil)
7980
t_root_account.lookup_feature_flag('course_feature').context.should eql t_site_admin
81+
t_course.instance_variable_set(:@feature_flag_cache, nil)
8082
t_course.feature_enabled?('course_feature').should be_false
8183
end
8284
end
@@ -99,6 +101,14 @@
99101
t_sub_account.feature_enabled?('course_feature').should be_false
100102
t_course.feature_enabled?('course_feature').should be_false
101103
end
104+
105+
it "should cache the lookup" do
106+
t_sub_account.feature_flags.create! feature: 'course_feature', state: 'on'
107+
t_root_account.feature_flags.create! feature: 'course_feature', state: 'off'
108+
t_sub_account.lookup_feature_flag('course_feature').context.should eql t_root_account
109+
Account.any_instance.expects(:feature_flag).never
110+
t_sub_account.lookup_feature_flag('course_feature').context.should eql t_root_account
111+
end
102112
end
103113

104114
context "course flags" do
@@ -114,6 +124,7 @@
114124
it "should apply settings at the site admin level" do
115125
t_user.lookup_feature_flag('user_feature').should be_default
116126
t_site_admin.feature_flags.create! feature: 'user_feature', state: 'off'
127+
t_user.instance_variable_set(:@feature_flag_cache, nil)
117128
t_user.lookup_feature_flag('user_feature').context.should eql t_site_admin
118129
t_user.feature_enabled?('user_feature').should be_false
119130
end
@@ -134,6 +145,12 @@
134145
t_sub_account.lookup_feature_flag('root_opt_in_feature').should be_nil
135146
t_course.lookup_feature_flag('root_opt_in_feature').should be_nil
136147
end
148+
149+
it "should cache the nil of the feature beneath the root account" do
150+
t_course.lookup_feature_flag('root_opt_in_feature').should be_nil
151+
Account.any_instance.expects(:feature_flag).never
152+
t_course.lookup_feature_flag('root_opt_in_feature').should be_nil
153+
end
137154
end
138155

139156
context "with site admin feature flag" do
@@ -269,6 +286,15 @@
269286
end
270287
end
271288

289+
it "should cache a nil result" do
290+
enable_cache do
291+
t_root_account.feature_flag('course_feature2')
292+
Rails.cache.should be_exist(t_root_account.feature_flag_cache_key('course_feature2'))
293+
t_root_account.expects(:feature_flags).never
294+
t_root_account.feature_flag('course_feature2')
295+
end
296+
end
297+
272298
it "should invalidate the cache when a feature flag is changed" do
273299
enable_cache do
274300
t_root_account.feature_flag('course_feature')

spec/models/quiz_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,8 +1232,8 @@
12321232
context "draft_state" do
12331233

12341234
it "updates the assignment's workflow state" do
1235+
@course.root_account.enable_feature!(:draft_state)
12351236
@quiz = @course.quizzes.create!(title: 'Test Quiz')
1236-
@quiz.context.root_account.enable_feature!(:draft_state)
12371237
@quiz.publish!
12381238
@quiz.unpublish!
12391239
@quiz.assignment.should_not be_published

0 commit comments

Comments
 (0)