Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStan Hu <stanhu@gmail.com>2019-07-02 21:19:30 +0300
committerStan Hu <stanhu@gmail.com>2019-07-04 02:22:50 +0300
commit2db7c5762b41acbcbd71bc4bd5a8aa3d90e1a383 (patch)
treefbb54b0e73b09c9ff52500cc466374c5be153a86 /spec/lib/feature_spec.rb
parent62e52ac6a8130c080f498ee2f76ef50b8c209f0f (diff)
Cache Flipper feature flags in L1 and L2 caches
In https://gitlab.com/gitlab-com/gl-infra/production/issues/928, we saw a significant amount of network traffic and CPU usage due to Redis checking feature flags via Flipper. Since these flags are hit with every request, the overhead becomes significant. To alleviate Redis overhead, we now cache the data in the following way: * L1: A thread-local memory store for 1 minute * L2: Redis for 1 hour
Diffstat (limited to 'spec/lib/feature_spec.rb')
-rw-r--r--spec/lib/feature_spec.rb62
1 files changed, 62 insertions, 0 deletions
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 403e0785d1b..127463a57e8 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -144,6 +144,68 @@ describe Feature do
expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
end
+ it { expect(described_class.l1_cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend) }
+ it { expect(described_class.l2_cache_backend).to eq(Rails.cache) }
+
+ it 'caches the status in L1 and L2 caches',
+ :request_store, :use_clean_rails_memory_store_caching do
+ described_class.enable(:enabled_feature_flag)
+ flipper_key = "flipper/v1/feature/enabled_feature_flag"
+
+ expect(described_class.l2_cache_backend)
+ .to receive(:fetch)
+ .once
+ .with(flipper_key, expires_in: 1.hour)
+ .and_call_original
+
+ expect(described_class.l1_cache_backend)
+ .to receive(:fetch)
+ .once
+ .with(flipper_key, expires_in: 1.minute)
+ .and_call_original
+
+ 2.times do
+ expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
+ end
+ end
+
+ context 'cached feature flag', :request_store do
+ let(:flag) { :some_feature_flag }
+
+ before do
+ described_class.flipper.memoize = false
+ described_class.enabled?(flag)
+ end
+
+ it 'caches the status in L1 cache for the first minute' do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).not_to receive(:fetch)
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(0)
+ end
+
+ it 'caches the status in L2 cache after 2 minutes' do
+ Timecop.travel 2.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(0)
+ end
+ end
+
+ it 'fetches the status after an hour' do
+ Timecop.travel 61.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(1)
+ end
+ end
+ end
+
context 'with an individual actor' do
CustomActor = Struct.new(:flipper_id)