From 3618796e15d542293aaa721045ff943d360d963a Mon Sep 17 00:00:00 2001
From: Stan Hu <stanhu@gmail.com>
Date: Sun, 24 Jul 2016 10:39:28 -0700
Subject: [PATCH] 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
---
 CHANGELOG                         |  3 +++
 app/models/repository.rb          |  2 +-
 lib/repository_cache.rb           |  7 ++++---
 spec/lib/repository_cache_spec.rb | 13 +++++++------
 4 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 698fbf6a160..e9d5f503615 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,9 @@ v 8.11.0 (unreleased)
   - Load project invited groups and members eagerly in ProjectTeam#fetch_members
   - 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)
   - Fix Error 500 when creating Wiki pages with hyphens or spaces
   - Ignore invalid trusted proxies in X-Forwarded-For header
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 793b1cf4989..053e2a9555b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1031,7 +1031,7 @@ class Repository
   private
 
   def cache
-    @cache ||= RepositoryCache.new(path_with_namespace)
+    @cache ||= RepositoryCache.new(path_with_namespace, @project.id)
   end
 
   def head_exists?
diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb
index 8ddc3511293..068a95790c0 100644
--- a/lib/repository_cache.rb
+++ b/lib/repository_cache.rb
@@ -1,14 +1,15 @@
 # Interface to the Redis-backed cache store used by the Repository model
 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
     @backend = backend
+    @project_id = project_id
   end
 
   def cache_key(type)
-    "#{type}:#{namespace}"
+    "#{type}:#{namespace}:#{project_id}"
   end
 
   def expire(key)
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
index 63b5292b098..f227926f39c 100644
--- a/spec/lib/repository_cache_spec.rb
+++ b/spec/lib/repository_cache_spec.rb
@@ -1,33 +1,34 @@
-require_relative '../../lib/repository_cache'
+require 'spec_helper'
 
 describe RepositoryCache, lib: true do
+  let(:project) { create(:project) }
   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
     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
 
   describe '#expire' do
     it 'expires the given key from the cache' do
       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
 
   describe '#fetch' do
     it 'fetches the given key from the cache' do
       cache.fetch(:bar)
-      expect(backend).to have_received(:fetch).with('bar:example')
+      expect(backend).to have_received(:fetch).with("bar:example:#{project.id}")
     end
 
     it 'accepts a block' do
       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
-- 
GitLab