Skip to content
Snippets Groups Projects
Commit 3618796e authored by Stan Hu's avatar Stan Hu
Browse files

Use project ID in repository cache to prevent stale data from persisting across projects

We have a number of bugs caused by cache keys not being flushed
properly during deletion of a project. Add the project ID to ensure
this never happens.

Closes #20027
parent 83180110
No related branches found
No related tags found
No related merge requests found
Loading
@@ -9,6 +9,9 @@ v 8.11.0 (unreleased)
Loading
@@ -9,6 +9,9 @@ v 8.11.0 (unreleased)
- Load project invited groups and members eagerly in ProjectTeam#fetch_members - Load project invited groups and members eagerly in ProjectTeam#fetch_members
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska) - Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
   
v 8.10.2 (unreleased)
- Use project ID in repository cache to prevent stale data from persisting across projects
v 8.10.1 (unreleased) v 8.10.1 (unreleased)
- Fix Error 500 when creating Wiki pages with hyphens or spaces - Fix Error 500 when creating Wiki pages with hyphens or spaces
- Ignore invalid trusted proxies in X-Forwarded-For header - Ignore invalid trusted proxies in X-Forwarded-For header
Loading
Loading
Loading
@@ -1031,7 +1031,7 @@ class Repository
Loading
@@ -1031,7 +1031,7 @@ class Repository
private private
   
def cache def cache
@cache ||= RepositoryCache.new(path_with_namespace) @cache ||= RepositoryCache.new(path_with_namespace, @project.id)
end end
   
def head_exists? def head_exists?
Loading
Loading
# Interface to the Redis-backed cache store used by the Repository model # Interface to the Redis-backed cache store used by the Repository model
class RepositoryCache class RepositoryCache
attr_reader :namespace, :backend attr_reader :namespace, :backend, :project_id
   
def initialize(namespace, backend = Rails.cache) def initialize(namespace, project_id, backend = Rails.cache)
@namespace = namespace @namespace = namespace
@backend = backend @backend = backend
@project_id = project_id
end end
   
def cache_key(type) def cache_key(type)
"#{type}:#{namespace}" "#{type}:#{namespace}:#{project_id}"
end end
   
def expire(key) def expire(key)
Loading
Loading
require_relative '../../lib/repository_cache' require 'spec_helper'
   
describe RepositoryCache, lib: true do describe RepositoryCache, lib: true do
let(:project) { create(:project) }
let(:backend) { double('backend').as_null_object } let(:backend) { double('backend').as_null_object }
let(:cache) { RepositoryCache.new('example', backend) } let(:cache) { RepositoryCache.new('example', project.id, backend) }
   
describe '#cache_key' do describe '#cache_key' do
it 'includes the namespace' do it 'includes the namespace' do
expect(cache.cache_key(:foo)).to eq 'foo:example' expect(cache.cache_key(:foo)).to eq "foo:example:#{project.id}"
end end
end end
   
describe '#expire' do describe '#expire' do
it 'expires the given key from the cache' do it 'expires the given key from the cache' do
cache.expire(:foo) cache.expire(:foo)
expect(backend).to have_received(:delete).with('foo:example') expect(backend).to have_received(:delete).with("foo:example:#{project.id}")
end end
end end
   
describe '#fetch' do describe '#fetch' do
it 'fetches the given key from the cache' do it 'fetches the given key from the cache' do
cache.fetch(:bar) cache.fetch(:bar)
expect(backend).to have_received(:fetch).with('bar:example') expect(backend).to have_received(:fetch).with("bar:example:#{project.id}")
end end
   
it 'accepts a block' do it 'accepts a block' do
p = -> {} p = -> {}
   
cache.fetch(:baz, &p) cache.fetch(:baz, &p)
expect(backend).to have_received(:fetch).with('baz:example', &p) expect(backend).to have_received(:fetch).with("baz:example:#{project.id}", &p)
end end
end end
end end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment