Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
5171e2f3
G
gitlab-foss
项目概览
李少辉-开发者
/
gitlab-foss
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gitlab-foss
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
5171e2f3
编写于
3月 06, 2018
作者:
A
Alejandro Rodríguez
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Refactor RepositoryCache to make it usable in other classes
上级
35f6efae
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
203 addition
and
178 deletion
+203
-178
app/models/repository.rb
app/models/repository.rb
+2
-72
lib/gitlab/repository_cache.rb
lib/gitlab/repository_cache.rb
+32
-0
lib/gitlab/repository_cache_adapter.rb
lib/gitlab/repository_cache_adapter.rb
+84
-0
lib/repository_cache.rb
lib/repository_cache.rb
+0
-30
spec/lib/gitlab/repository_cache_adapter_spec.rb
spec/lib/gitlab/repository_cache_adapter_spec.rb
+76
-0
spec/lib/gitlab/repository_cache_spec.rb
spec/lib/gitlab/repository_cache_spec.rb
+9
-7
spec/models/repository_spec.rb
spec/models/repository_spec.rb
+0
-69
未找到文件。
app/models/repository.rb
浏览文件 @
5171e2f3
...
...
@@ -16,6 +16,7 @@ class Repository
]
.
freeze
include
Gitlab
::
ShellAdapter
include
Gitlab
::
RepositoryCacheAdapter
attr_accessor
:full_path
,
:disk_path
,
:project
,
:is_wiki
...
...
@@ -57,22 +58,6 @@ class Repository
merge_request_template: :merge_request_template_names
}.
freeze
# Wraps around the given method and caches its output in Redis and an instance
# variable.
#
# This only works for methods that do not take any arguments.
def
self
.
cache_method
(
name
,
fallback:
nil
,
memoize_only:
false
)
original
=
:"_uncached_
#{
name
}
"
alias_method
(
original
,
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
,
memoize_only:
memoize_only
)
do
__send__
(
original
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
def
initialize
(
full_path
,
project
,
disk_path:
nil
,
is_wiki:
false
)
@full_path
=
full_path
@disk_path
=
disk_path
||
full_path
...
...
@@ -302,17 +287,6 @@ class Repository
expire_method_caches
(
CACHED_METHODS
)
end
# Expires the caches of a specific set of methods
def
expire_method_caches
(
methods
)
methods
.
each
do
|
key
|
cache
.
expire
(
key
)
ivar
=
cache_instance_variable_name
(
key
)
remove_instance_variable
(
ivar
)
if
instance_variable_defined?
(
ivar
)
end
end
def
expire_avatar_cache
expire_method_caches
(
%i(avatar)
)
end
...
...
@@ -921,49 +895,6 @@ class Repository
end
end
# Caches the supplied block both in a cache and in an instance variable.
#
# The cache key and instance variable are named the same way as the value of
# the `key` argument.
#
# This method will return `nil` if the corresponding instance variable is also
# set to `nil`. This ensures we don't keep yielding the block when it returns
# `nil`.
#
# key - The name of the key to cache the data in.
# fallback - A value to fall back to in the event of a Git error.
def
cache_method_output
(
key
,
fallback:
nil
,
memoize_only:
false
,
&
block
)
ivar
=
cache_instance_variable_name
(
key
)
if
instance_variable_defined?
(
ivar
)
instance_variable_get
(
ivar
)
else
# If the repository doesn't exist and a fallback was specified we return
# that value inmediately. This saves us Rugged/gRPC invocations.
return
fallback
unless
fallback
.
nil?
||
exists?
begin
value
=
if
memoize_only
yield
else
cache
.
fetch
(
key
,
&
block
)
end
instance_variable_set
(
ivar
,
value
)
rescue
Gitlab
::
Git
::
Repository
::
NoRepository
# Even if the above `#exists?` check passes these errors might still
# occur (for example because of a non-existing HEAD). We want to
# gracefully handle this and not cache anything
fallback
end
end
end
def
cache_instance_variable_name
(
key
)
:"@
#{
key
.
to_s
.
tr
(
'?!'
,
''
)
}
"
end
def
file_on_head
(
type
)
if
head
=
tree
(
:head
)
head
.
blobs
.
find
do
|
blob
|
...
...
@@ -1018,8 +949,7 @@ class Repository
end
def
cache
# TODO: should we use UUIDs here? We could move repositories without clearing this cache
@cache
||=
RepositoryCache
.
new
(
full_path
,
@project
.
id
)
@cache
||=
Gitlab
::
RepositoryCache
.
new
(
self
)
end
def
tags_sorted_by_committed_date
...
...
lib/gitlab/repository_cache.rb
0 → 100644
浏览文件 @
5171e2f3
# Interface to the Redis-backed cache store
module
Gitlab
class
RepositoryCache
attr_reader
:repository
,
:namespace
,
:backend
def
initialize
(
repository
,
backend
=
Rails
.
cache
)
@repository
=
repository
@namespace
=
"
#{
repository
.
full_path
}
:
#{
repository
.
project
.
id
}
"
@backend
=
backend
end
def
cache_key
(
type
)
"
#{
type
}
:
#{
namespace
}
"
end
def
expire
(
key
)
backend
.
delete
(
cache_key
(
key
))
end
def
fetch
(
key
,
&
block
)
backend
.
fetch
(
cache_key
(
key
),
&
block
)
end
def
exist?
(
key
)
backend
.
exist?
(
cache_key
(
key
))
end
def
read
(
key
)
backend
.
read
(
cache_key
(
key
))
end
end
end
lib/gitlab/repository_cache_adapter.rb
0 → 100644
浏览文件 @
5171e2f3
module
Gitlab
module
RepositoryCacheAdapter
extend
ActiveSupport
::
Concern
class_methods
do
# Wraps around the given method and caches its output in Redis and an instance
# variable.
#
# This only works for methods that do not take any arguments.
def
cache_method
(
name
,
fallback:
nil
,
memoize_only:
false
)
original
=
:"_uncached_
#{
name
}
"
alias_method
(
original
,
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
,
memoize_only:
memoize_only
)
do
__send__
(
original
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
end
# RepositoryCache to be used. Should be overridden by the including class
def
cache
raise
NotImplementedError
end
# Caches the supplied block both in a cache and in an instance variable.
#
# The cache key and instance variable are named the same way as the value of
# the `key` argument.
#
# This method will return `nil` if the corresponding instance variable is also
# set to `nil`. This ensures we don't keep yielding the block when it returns
# `nil`.
#
# key - The name of the key to cache the data in.
# fallback - A value to fall back to in the event of a Git error.
def
cache_method_output
(
key
,
fallback:
nil
,
memoize_only:
false
,
&
block
)
ivar
=
cache_instance_variable_name
(
key
)
if
instance_variable_defined?
(
ivar
)
instance_variable_get
(
ivar
)
else
# If the repository doesn't exist and a fallback was specified we return
# that value inmediately. This saves us Rugged/gRPC invocations.
return
fallback
unless
fallback
.
nil?
||
cache
.
repository
.
exists?
begin
value
=
if
memoize_only
yield
else
cache
.
fetch
(
key
,
&
block
)
end
instance_variable_set
(
ivar
,
value
)
rescue
Gitlab
::
Git
::
Repository
::
NoRepository
# Even if the above `#exists?` check passes these errors might still
# occur (for example because of a non-existing HEAD). We want to
# gracefully handle this and not cache anything
fallback
end
end
end
# Expires the caches of a specific set of methods
def
expire_method_caches
(
methods
)
methods
.
each
do
|
key
|
cache
.
expire
(
key
)
ivar
=
cache_instance_variable_name
(
key
)
remove_instance_variable
(
ivar
)
if
instance_variable_defined?
(
ivar
)
end
end
private
def
cache_instance_variable_name
(
key
)
:"@
#{
key
.
to_s
.
tr
(
'?!'
,
''
)
}
"
end
end
end
lib/repository_cache.rb
已删除
100644 → 0
浏览文件 @
35f6efae
# Interface to the Redis-backed cache store used by the Repository model
class
RepositoryCache
attr_reader
:namespace
,
:backend
,
:project_id
def
initialize
(
namespace
,
project_id
,
backend
=
Rails
.
cache
)
@namespace
=
namespace
@backend
=
backend
@project_id
=
project_id
end
def
cache_key
(
type
)
"
#{
type
}
:
#{
namespace
}
:
#{
project_id
}
"
end
def
expire
(
key
)
backend
.
delete
(
cache_key
(
key
))
end
def
fetch
(
key
,
&
block
)
backend
.
fetch
(
cache_key
(
key
),
&
block
)
end
def
exist?
(
key
)
backend
.
exist?
(
cache_key
(
key
))
end
def
read
(
key
)
backend
.
read
(
cache_key
(
key
))
end
end
spec/lib/gitlab/repository_cache_adapter_spec.rb
0 → 100644
浏览文件 @
5171e2f3
require
'spec_helper'
describe
Gitlab
::
RepositoryCacheAdapter
do
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:repository
)
{
project
.
repository
}
let
(
:cache
)
{
repository
.
send
(
:cache
)
}
describe
'#cache_method_output'
,
:use_clean_rails_memory_store_caching
do
let
(
:fallback
)
{
10
}
context
'with a non-existing repository'
do
let
(
:project
)
{
create
(
:project
)
}
# No repository
subject
do
repository
.
cache_method_output
(
:cats
,
fallback:
fallback
)
do
repository
.
cats_call_stub
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'avoids calling the original method'
do
expect
(
repository
).
not_to
receive
(
:cats_call_stub
)
subject
end
end
context
'with a method throwing a non-existing-repository error'
do
subject
do
repository
.
cache_method_output
(
:cats
,
fallback:
fallback
)
do
raise
Gitlab
::
Git
::
Repository
::
NoRepository
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'does not cache the data'
do
subject
expect
(
repository
.
instance_variable_defined?
(
:@cats
)).
to
eq
(
false
)
expect
(
cache
.
exist?
(
:cats
)).
to
eq
(
false
)
end
end
context
'with an existing repository'
do
it
'caches the output'
do
object
=
double
expect
(
object
).
to
receive
(
:number
).
once
.
and_return
(
10
)
2
.
times
do
val
=
repository
.
cache_method_output
(
:cats
)
{
object
.
number
}
expect
(
val
).
to
eq
(
10
)
end
expect
(
repository
.
send
(
:cache
).
exist?
(
:cats
)).
to
eq
(
true
)
expect
(
repository
.
instance_variable_get
(
:@cats
)).
to
eq
(
10
)
end
end
end
describe
'#expire_method_caches'
do
it
'expires the caches of the given methods'
do
expect
(
cache
).
to
receive
(
:expire
).
with
(
:readme
)
expect
(
cache
).
to
receive
(
:expire
).
with
(
:gitignore
)
repository
.
expire_method_caches
(
%i(readme gitignore)
)
end
end
end
spec/lib/repository_cache_spec.rb
→
spec/lib/
gitlab/
repository_cache_spec.rb
浏览文件 @
5171e2f3
require
'spec_helper'
describe
RepositoryCache
do
let
(
:project
)
{
create
(
:project
)
}
describe
Gitlab
::
RepositoryCache
do
let
(
:backend
)
{
double
(
'backend'
).
as_null_object
}
let
(
:cache
)
{
described_class
.
new
(
'example'
,
project
.
id
,
backend
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:repository
)
{
project
.
repository
}
let
(
:namespace
)
{
"
#{
repository
.
full_path
}
:
#{
project
.
id
}
"
}
let
(
:cache
)
{
described_class
.
new
(
repository
,
backend
)
}
describe
'#cache_key'
do
it
'includes the namespace'
do
expect
(
cache
.
cache_key
(
:foo
)).
to
eq
"foo:
example:
#{
project
.
id
}
"
expect
(
cache
.
cache_key
(
:foo
)).
to
eq
"foo:
#{
namespace
}
"
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:
#{
project
.
id
}
"
)
expect
(
backend
).
to
have_received
(
:delete
).
with
(
"foo:
#{
namespace
}
"
)
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:
#{
project
.
id
}
"
)
expect
(
backend
).
to
have_received
(
:fetch
).
with
(
"bar:
#{
namespace
}
"
)
end
it
'accepts a block'
do
p
=
->
{}
cache
.
fetch
(
:baz
,
&
p
)
expect
(
backend
).
to
have_received
(
:fetch
).
with
(
"baz:
example:
#{
project
.
id
}
"
,
&
p
)
expect
(
backend
).
to
have_received
(
:fetch
).
with
(
"baz:
#{
namespace
}
"
,
&
p
)
end
end
end
spec/models/repository_spec.rb
浏览文件 @
5171e2f3
...
...
@@ -2171,15 +2171,6 @@ describe Repository do
end
end
describe
'#expire_method_caches'
do
it
'expires the caches of the given methods'
do
expect_any_instance_of
(
RepositoryCache
).
to
receive
(
:expire
).
with
(
:readme
)
expect_any_instance_of
(
RepositoryCache
).
to
receive
(
:expire
).
with
(
:gitignore
)
repository
.
expire_method_caches
(
%i(readme gitignore)
)
end
end
describe
'#expire_all_method_caches'
do
it
'expires the caches of all methods'
do
expect
(
repository
).
to
receive
(
:expire_method_caches
)
...
...
@@ -2325,66 +2316,6 @@ describe Repository do
end
end
describe
'#cache_method_output'
,
:use_clean_rails_memory_store_caching
do
let
(
:fallback
)
{
10
}
context
'with a non-existing repository'
do
let
(
:project
)
{
create
(
:project
)
}
# No repository
subject
do
repository
.
cache_method_output
(
:cats
,
fallback:
fallback
)
do
repository
.
cats_call_stub
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'avoids calling the original method'
do
expect
(
repository
).
not_to
receive
(
:cats_call_stub
)
subject
end
end
context
'with a method throwing a non-existing-repository error'
do
subject
do
repository
.
cache_method_output
(
:cats
,
fallback:
fallback
)
do
raise
Gitlab
::
Git
::
Repository
::
NoRepository
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'does not cache the data'
do
subject
expect
(
repository
.
instance_variable_defined?
(
:@cats
)).
to
eq
(
false
)
expect
(
repository
.
send
(
:cache
).
exist?
(
:cats
)).
to
eq
(
false
)
end
end
context
'with an existing repository'
do
it
'caches the output'
do
object
=
double
expect
(
object
).
to
receive
(
:number
).
once
.
and_return
(
10
)
2
.
times
do
val
=
repository
.
cache_method_output
(
:cats
)
{
object
.
number
}
expect
(
val
).
to
eq
(
10
)
end
expect
(
repository
.
send
(
:cache
).
exist?
(
:cats
)).
to
eq
(
true
)
expect
(
repository
.
instance_variable_get
(
:@cats
)).
to
eq
(
10
)
end
end
end
describe
'#refresh_method_caches'
do
it
'refreshes the caches of the given types'
do
expect
(
repository
).
to
receive
(
:expire_method_caches
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录