From dbbd137e9fce17eb5698faa65f9bce49a6fcdbbb Mon Sep 17 00:00:00 2001 From: GitLab Duo Date: Thu, 18 Sep 2025 21:53:26 +0000 Subject: [PATCH] Duo Workflow: Resolve issue #260456 --- .../events/cache_invalidation_event.rb | 21 ++++++ .../events/cache_invalidation_event_spec.rb | 66 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/ee/lib/gitlab/geo/log_cursor/events/cache_invalidation_event.rb b/ee/lib/gitlab/geo/log_cursor/events/cache_invalidation_event.rb index dd1f8996e24eed..1d8d0fe89fa2e7 100644 --- a/ee/lib/gitlab/geo/log_cursor/events/cache_invalidation_event.rb +++ b/ee/lib/gitlab/geo/log_cursor/events/cache_invalidation_event.rb @@ -9,6 +9,7 @@ class CacheInvalidationEvent def process result = expire_cache_for_event_key + clear_feature_flag_caches if feature_flag_cache_key? log_cache_invalidation_event(result) end @@ -18,6 +19,26 @@ def expire_cache_for_event_key Rails.cache.delete(event.key) end + def feature_flag_cache_key? + # CacheInvalidationEventStore is only fired from ee/lib/ee/feature.rb + # for feature flag changes, so we can safely assume all cache invalidation + # events are for feature flags and clear the additional cache layers + true + end + + def clear_feature_flag_caches + # Clear the memoized cache layers that are not cleared by Rails.cache.delete + # This ensures feature flag changes propagate immediately to Geo secondaries + + # Clear SafeRequestStore flipper cache if active + if Gitlab::SafeRequestStore.active? + Gitlab::SafeRequestStore.delete(:flipper) + end + + # Clear the @flipper instance variable cache + Feature.instance_variable_set(:@flipper, nil) + end + def log_cache_invalidation_event(expired) log_event( 'Cache invalidation', diff --git a/ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb b/ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb index aa851588de37fc..f77e3df22fed05 100644 --- a/ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb +++ b/ee/spec/lib/gitlab/geo/log_cursor/events/cache_invalidation_event_spec.rb @@ -24,6 +24,12 @@ subject.process end + it 'clears feature flag caches' do + expect(subject).to receive(:clear_feature_flag_caches) + + subject.process + end + it 'logs an info event' do data = { class: described_class.name, @@ -41,4 +47,64 @@ subject.process end end + + describe '#feature_flag_cache_key?' do + it 'returns true for all cache keys' do + expect(subject.send(:feature_flag_cache_key?)).to be true + end + end + + describe '#clear_feature_flag_caches' do + context 'when SafeRequestStore is active' do + before do + allow(Gitlab::SafeRequestStore).to receive(:active?).and_return(true) + end + + it 'clears the SafeRequestStore flipper cache' do + expect(Gitlab::SafeRequestStore).to receive(:delete).with(:flipper) + + subject.send(:clear_feature_flag_caches) + end + + it 'clears the @flipper instance variable' do + expect(Feature).to receive(:instance_variable_set).with(:@flipper, nil) + + subject.send(:clear_feature_flag_caches) + end + end + + context 'when SafeRequestStore is not active' do + before do + allow(Gitlab::SafeRequestStore).to receive(:active?).and_return(false) + end + + it 'does not try to clear SafeRequestStore' do + expect(Gitlab::SafeRequestStore).not_to receive(:delete) + + subject.send(:clear_feature_flag_caches) + end + + it 'still clears the @flipper instance variable' do + expect(Feature).to receive(:instance_variable_set).with(:@flipper, nil) + + subject.send(:clear_feature_flag_caches) + end + end + end + + describe 'integration test' do + it 'clears all feature flag cache layers when processing cache invalidation event' do + # Set up feature flag caches + allow(Gitlab::SafeRequestStore).to receive(:active?).and_return(true) + Gitlab::SafeRequestStore[:flipper] = 'cached_flipper_instance' + Feature.instance_variable_set(:@flipper, 'cached_flipper_instance') + + # Process the cache invalidation event + subject.process + + # Verify all caches are cleared + expect(Gitlab::SafeRequestStore[:flipper]).to be_nil + expect(Feature.instance_variable_get(:@flipper)).to be_nil + end + end end -- GitLab