BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
8631779b
Commit
8631779b
authored
Apr 07, 2017
by
Kamil Trzciński
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'bvl-fix-project-ci-status-cache' into 'master'
Fix invalidating Project build status cache to often See merge request !10313
parents
46aadc5c
9eded57d
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
79 additions
and
47 deletions
+79
-47
pipeline.rb
app/models/ci/pipeline.rb
+0
-6
project.rb
app/models/project.rb
+1
-1
expire_pipeline_cache_service.rb
app/services/ci/expire_pipeline_cache_service.rb
+2
-0
project.rb
features/steps/shared/project.rb
+2
-1
project_pipeline_status.rb
lib/gitlab/cache/ci/project_pipeline_status.rb
+27
-10
projects_spec.rb
spec/features/dashboard/projects_spec.rb
+7
-4
ci_status_helper_spec.rb
spec/helpers/ci_status_helper_spec.rb
+5
-1
projects_helper_spec.rb
spec/helpers/projects_helper_spec.rb
+1
-1
project_pipeline_status_spec.rb
spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+25
-8
pipeline_spec.rb
spec/models/ci/pipeline_spec.rb
+1
-14
project_spec.rb
spec/models/project_spec.rb
+1
-1
expire_pipeline_cache_service_spec.rb
spec/services/ci/expire_pipeline_cache_service_spec.rb
+7
-0
No files found.
app/models/ci/pipeline.rb
View file @
8631779b
...
@@ -31,7 +31,6 @@ module Ci
...
@@ -31,7 +31,6 @@ module Ci
validate
:valid_commit_sha
,
unless: :importing?
validate
:valid_commit_sha
,
unless: :importing?
after_create
:keep_around_commits
,
unless: :importing?
after_create
:keep_around_commits
,
unless: :importing?
after_create
:refresh_build_status_cache
state_machine
:status
,
initial: :created
do
state_machine
:status
,
initial: :created
do
event
:enqueue
do
event
:enqueue
do
...
@@ -351,7 +350,6 @@ module Ci
...
@@ -351,7 +350,6 @@ module Ci
when
'manual'
then
block
when
'manual'
then
block
end
end
end
end
refresh_build_status_cache
end
end
def
predefined_variables
def
predefined_variables
...
@@ -393,10 +391,6 @@ module Ci
...
@@ -393,10 +391,6 @@ module Ci
.
fabricate!
.
fabricate!
end
end
def
refresh_build_status_cache
Ci
::
PipelineStatus
.
new
(
project
,
sha:
sha
,
status:
status
).
store_in_cache_if_needed
end
private
private
def
pipeline_data
def
pipeline_data
...
...
app/models/project.rb
View file @
8631779b
...
@@ -1182,7 +1182,7 @@ class Project < ActiveRecord::Base
...
@@ -1182,7 +1182,7 @@ class Project < ActiveRecord::Base
end
end
def
pipeline_status
def
pipeline_status
@pipeline_status
||=
Ci
::
PipelineStatus
.
load_for_project
(
self
)
@pipeline_status
||=
Gitlab
::
Cache
::
Ci
::
Project
PipelineStatus
.
load_for_project
(
self
)
end
end
def
mark_import_as_failed
(
error_message
)
def
mark_import_as_failed
(
error_message
)
...
...
app/services/ci/expire_pipeline_cache_service.rb
View file @
8631779b
...
@@ -10,6 +10,8 @@ module Ci
...
@@ -10,6 +10,8 @@ module Ci
store
.
touch
(
commit_pipelines_path
)
if
pipeline
.
commit
store
.
touch
(
commit_pipelines_path
)
if
pipeline
.
commit
store
.
touch
(
new_merge_request_pipelines_path
)
store
.
touch
(
new_merge_request_pipelines_path
)
merge_requests_pipelines_paths
.
each
{
|
path
|
store
.
touch
(
path
)
}
merge_requests_pipelines_paths
.
each
{
|
path
|
store
.
touch
(
path
)
}
Gitlab
::
Cache
::
Ci
::
ProjectPipelineStatus
.
update_for_pipeline
(
@pipeline
)
end
end
private
private
...
...
features/steps/shared/project.rb
View file @
8631779b
...
@@ -251,7 +251,8 @@ module SharedProject
...
@@ -251,7 +251,8 @@ module SharedProject
step
'project "Shop" has CI build'
do
step
'project "Shop" has CI build'
do
project
=
Project
.
find_by
(
name:
"Shop"
)
project
=
Project
.
find_by
(
name:
"Shop"
)
create
:ci_pipeline
,
project:
project
,
sha:
project
.
commit
.
sha
,
ref:
'master'
,
status:
'skipped'
pipeline
=
create
:ci_pipeline
,
project:
project
,
sha:
project
.
commit
.
sha
,
ref:
'master'
pipeline
.
skip
end
end
step
'I should see last commit with CI status'
do
step
'I should see last commit with CI status'
do
...
...
app/models/ci/
pipeline_status.rb
→
lib/gitlab/cache/ci/project_
pipeline_status.rb
View file @
8631779b
# This class is not backed by a table in the main database.
# This class is not backed by a table in the main database.
# It loads the latest Pipeline for the HEAD of a repository, and caches that
# It loads the latest Pipeline for the HEAD of a repository, and caches that
# in Redis.
# in Redis.
module
Ci
module
Gitlab
class
PipelineStatus
module
Cache
attr_accessor
:sha
,
:status
,
:project
,
:loaded
module
Ci
class
ProjectPipelineStatus
attr_accessor
:sha
,
:status
,
:ref
,
:project
,
:loaded
delegate
:commit
,
to: :project
delegate
:commit
,
to: :project
...
@@ -13,9 +15,17 @@ module Ci
...
@@ -13,9 +15,17 @@ module Ci
end
end
end
end
def
initialize
(
project
,
sha:
nil
,
status:
nil
)
def
self
.
update_for_pipeline
(
pipeline
)
new
(
pipeline
.
project
,
sha:
pipeline
.
sha
,
status:
pipeline
.
status
,
ref:
pipeline
.
ref
).
store_in_cache_if_needed
end
def
initialize
(
project
,
sha:
nil
,
status:
nil
,
ref:
nil
)
@project
=
project
@project
=
project
@sha
=
sha
@sha
=
sha
@ref
=
ref
@status
=
status
@status
=
status
end
end
...
@@ -29,37 +39,42 @@ module Ci
...
@@ -29,37 +39,42 @@ module Ci
if
has_cache?
if
has_cache?
load_from_cache
load_from_cache
else
else
load_from_commi
t
load_from_projec
t
store_in_cache
store_in_cache
end
end
self
.
loaded
=
true
self
.
loaded
=
true
end
end
def
load_from_commi
t
def
load_from_projec
t
return
unless
commit
return
unless
commit
self
.
sha
=
commit
.
sha
self
.
sha
=
commit
.
sha
self
.
status
=
commit
.
status
self
.
status
=
commit
.
status
self
.
ref
=
project
.
default_branch
end
end
# We only cache the status for the HEAD commit of a project
# We only cache the status for the HEAD commit of a project
# This status is rendered in project lists
# This status is rendered in project lists
def
store_in_cache_if_needed
def
store_in_cache_if_needed
return
unless
sha
return
delete_from_cache
unless
commit
return
delete_from_cache
unless
commit
store_in_cache
if
commit
.
sha
==
self
.
sha
return
unless
sha
return
unless
ref
if
commit
.
sha
==
sha
&&
project
.
default_branch
==
ref
store_in_cache
end
end
end
def
load_from_cache
def
load_from_cache
Gitlab
::
Redis
.
with
do
|
redis
|
Gitlab
::
Redis
.
with
do
|
redis
|
self
.
sha
,
self
.
status
=
redis
.
hmget
(
cache_key
,
:sha
,
:status
)
self
.
sha
,
self
.
status
,
self
.
ref
=
redis
.
hmget
(
cache_key
,
:sha
,
:status
,
:ref
)
end
end
end
end
def
store_in_cache
def
store_in_cache
Gitlab
::
Redis
.
with
do
|
redis
|
Gitlab
::
Redis
.
with
do
|
redis
|
redis
.
mapped_hmset
(
cache_key
,
{
sha:
sha
,
status:
status
})
redis
.
mapped_hmset
(
cache_key
,
{
sha:
sha
,
status:
status
,
ref:
ref
})
end
end
end
end
...
@@ -83,4 +98,6 @@ module Ci
...
@@ -83,4 +98,6 @@ module Ci
"projects/
#{
project
.
id
}
/build_status"
"projects/
#{
project
.
id
}
/build_status"
end
end
end
end
end
end
end
end
spec/features/dashboard/projects_spec.rb
View file @
8631779b
...
@@ -7,7 +7,6 @@ RSpec.describe 'Dashboard Projects', feature: true do
...
@@ -7,7 +7,6 @@ RSpec.describe 'Dashboard Projects', feature: true do
before
do
before
do
project
.
team
<<
[
user
,
:developer
]
project
.
team
<<
[
user
,
:developer
]
login_as
user
login_as
user
visit
dashboard_projects_path
end
end
it
'shows the project the user in a member of in the list'
do
it
'shows the project the user in a member of in the list'
do
...
@@ -15,15 +14,19 @@ RSpec.describe 'Dashboard Projects', feature: true do
...
@@ -15,15 +14,19 @@ RSpec.describe 'Dashboard Projects', feature: true do
expect
(
page
).
to
have_content
(
'awesome stuff'
)
expect
(
page
).
to
have_content
(
'awesome stuff'
)
end
end
describe
"with a pipeline"
do
describe
"with a pipeline"
,
redis:
true
do
let
(
:pipeline
)
{
create
(
:ci_pipeline
,
:success
,
project:
project
,
sha:
project
.
commit
.
sha
)
}
let
!
(
:pipeline
)
{
create
(
:ci_pipeline
,
project:
project
,
sha:
project
.
commit
.
sha
)
}
before
do
before
do
pipeline
# Since the cache isn't updated when a new pipeline is created
# we need the pipeline to advance in the pipeline since the cache was created
# by visiting the login page.
pipeline
.
succeed
end
end
it
'shows that the last pipeline passed'
do
it
'shows that the last pipeline passed'
do
visit
dashboard_projects_path
visit
dashboard_projects_path
expect
(
page
).
to
have_xpath
(
"//a[@href='
#{
pipelines_namespace_project_commit_path
(
project
.
namespace
,
project
,
project
.
commit
)
}
']"
)
expect
(
page
).
to
have_xpath
(
"//a[@href='
#{
pipelines_namespace_project_commit_path
(
project
.
namespace
,
project
,
project
.
commit
)
}
']"
)
end
end
end
end
...
...
spec/helpers/ci_status_helper_spec.rb
View file @
8631779b
...
@@ -19,7 +19,11 @@ describe CiStatusHelper do
...
@@ -19,7 +19,11 @@ describe CiStatusHelper do
describe
"#pipeline_status_cache_key"
do
describe
"#pipeline_status_cache_key"
do
it
"builds a cache key for pipeline status"
do
it
"builds a cache key for pipeline status"
do
pipeline_status
=
Ci
::
PipelineStatus
.
new
(
build
(
:project
),
sha:
"123abc"
,
status:
"success"
)
pipeline_status
=
Gitlab
::
Cache
::
Ci
::
ProjectPipelineStatus
.
new
(
build
(
:project
),
sha:
"123abc"
,
status:
"success"
)
expect
(
helper
.
pipeline_status_cache_key
(
pipeline_status
)).
to
eq
(
"pipeline-status/123abc-success"
)
expect
(
helper
.
pipeline_status_cache_key
(
pipeline_status
)).
to
eq
(
"pipeline-status/123abc-success"
)
end
end
end
end
...
...
spec/helpers/projects_helper_spec.rb
View file @
8631779b
...
@@ -63,7 +63,7 @@ describe ProjectsHelper do
...
@@ -63,7 +63,7 @@ describe ProjectsHelper do
end
end
end
end
describe
"#project_list_cache_key"
do
describe
"#project_list_cache_key"
,
redis:
true
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:project
)
{
create
(
:project
)
}
it
"includes the namespace"
do
it
"includes the namespace"
do
...
...
spec/
models/ci/
pipeline_status_spec.rb
→
spec/
lib/gitlab/cache/ci/project_
pipeline_status_spec.rb
View file @
8631779b
require
'spec_helper'
require
'spec_helper'
describe
Ci
::
PipelineStatus
do
describe
Gitlab
::
Cache
::
Ci
::
Project
PipelineStatus
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:pipeline_status
)
{
described_class
.
new
(
project
)
}
let
(
:pipeline_status
)
{
described_class
.
new
(
project
)
}
...
@@ -12,6 +12,20 @@ describe Ci::PipelineStatus do
...
@@ -12,6 +12,20 @@ describe Ci::PipelineStatus do
end
end
end
end
describe
'.update_for_pipeline'
do
it
'refreshes the cache if nescessary'
do
pipeline
=
build_stubbed
(
:ci_pipeline
,
sha:
'123456'
,
status:
'success'
)
fake_status
=
double
expect
(
described_class
).
to
receive
(
:new
).
with
(
pipeline
.
project
,
sha:
'123456'
,
status:
'success'
,
ref:
'master'
).
and_return
(
fake_status
)
expect
(
fake_status
).
to
receive
(
:store_in_cache_if_needed
)
described_class
.
update_for_pipeline
(
pipeline
)
end
end
describe
'#has_status?'
do
describe
'#has_status?'
do
it
"is false when the status wasn't loaded yet"
do
it
"is false when the status wasn't loaded yet"
do
expect
(
pipeline_status
.
has_status?
).
to
be_falsy
expect
(
pipeline_status
.
has_status?
).
to
be_falsy
...
@@ -41,14 +55,14 @@ describe Ci::PipelineStatus do
...
@@ -41,14 +55,14 @@ describe Ci::PipelineStatus do
it
'loads the status from the project commit when there is no cache'
do
it
'loads the status from the project commit when there is no cache'
do
allow
(
pipeline_status
).
to
receive
(
:has_cache?
).
and_return
(
false
)
allow
(
pipeline_status
).
to
receive
(
:has_cache?
).
and_return
(
false
)
expect
(
pipeline_status
).
to
receive
(
:load_from_
commi
t
)
expect
(
pipeline_status
).
to
receive
(
:load_from_
projec
t
)
pipeline_status
.
load_status
pipeline_status
.
load_status
end
end
it
'stores the status in the cache when it loading it from the project'
do
it
'stores the status in the cache when it loading it from the project'
do
allow
(
pipeline_status
).
to
receive
(
:has_cache?
).
and_return
(
false
)
allow
(
pipeline_status
).
to
receive
(
:has_cache?
).
and_return
(
false
)
allow
(
pipeline_status
).
to
receive
(
:load_from_
commi
t
)
allow
(
pipeline_status
).
to
receive
(
:load_from_
projec
t
)
expect
(
pipeline_status
).
to
receive
(
:store_in_cache
)
expect
(
pipeline_status
).
to
receive
(
:store_in_cache
)
...
@@ -70,14 +84,15 @@ describe Ci::PipelineStatus do
...
@@ -70,14 +84,15 @@ describe Ci::PipelineStatus do
end
end
end
end
describe
"#load_from_
commi
t"
do
describe
"#load_from_
projec
t"
do
let!
(
:pipeline
)
{
create
(
:ci_pipeline
,
:success
,
project:
project
,
sha:
project
.
commit
.
sha
)
}
let!
(
:pipeline
)
{
create
(
:ci_pipeline
,
:success
,
project:
project
,
sha:
project
.
commit
.
sha
)
}
it
'reads the status from the pipeline for the commit'
do
it
'reads the status from the pipeline for the commit'
do
pipeline_status
.
load_from_
commi
t
pipeline_status
.
load_from_
projec
t
expect
(
pipeline_status
.
status
).
to
eq
(
'success'
)
expect
(
pipeline_status
.
status
).
to
eq
(
'success'
)
expect
(
pipeline_status
.
sha
).
to
eq
(
project
.
commit
.
sha
)
expect
(
pipeline_status
.
sha
).
to
eq
(
project
.
commit
.
sha
)
expect
(
pipeline_status
.
ref
).
to
eq
(
project
.
default_branch
)
end
end
it
"doesn't fail for an empty project"
do
it
"doesn't fail for an empty project"
do
...
@@ -108,10 +123,11 @@ describe Ci::PipelineStatus do
...
@@ -108,10 +123,11 @@ describe Ci::PipelineStatus do
build_status
=
described_class
.
load_for_project
(
project
)
build_status
=
described_class
.
load_for_project
(
project
)
build_status
.
store_in_cache_if_needed
build_status
.
store_in_cache_if_needed
sha
,
status
=
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
hmget
(
"projects/
#{
project
.
id
}
/build_status"
,
:sha
,
:status
)
}
sha
,
status
,
ref
=
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
hmget
(
"projects/
#{
project
.
id
}
/build_status"
,
:sha
,
:status
,
:ref
)
}
expect
(
sha
).
not_to
be_nil
expect
(
sha
).
not_to
be_nil
expect
(
status
).
not_to
be_nil
expect
(
status
).
not_to
be_nil
expect
(
ref
).
not_to
be_nil
end
end
it
"doesn't store the status in redis when the sha is not the head of the project"
do
it
"doesn't store the status in redis when the sha is not the head of the project"
do
...
@@ -126,14 +142,15 @@ describe Ci::PipelineStatus do
...
@@ -126,14 +142,15 @@ describe Ci::PipelineStatus do
it
"deletes the cache if the repository doesn't have a head commit"
do
it
"deletes the cache if the repository doesn't have a head commit"
do
empty_project
=
create
(
:empty_project
)
empty_project
=
create
(
:empty_project
)
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
mapped_hmset
(
"projects/
#{
empty_project
.
id
}
/build_status"
,
{
sha:
"sha"
,
status:
"pending"
})
}
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
mapped_hmset
(
"projects/
#{
empty_project
.
id
}
/build_status"
,
{
sha:
"sha"
,
status:
"pending"
,
ref:
'master'
})
}
other_status
=
described_class
.
new
(
empty_project
,
sha:
"123456"
,
status:
"failed"
)
other_status
=
described_class
.
new
(
empty_project
,
sha:
"123456"
,
status:
"failed"
)
other_status
.
store_in_cache_if_needed
other_status
.
store_in_cache_if_needed
sha
,
status
=
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
hmget
(
"projects/
#{
empty_project
.
id
}
/build_status"
,
:sha
,
:status
)
}
sha
,
status
,
ref
=
Gitlab
::
Redis
.
with
{
|
redis
|
redis
.
hmget
(
"projects/
#{
empty_project
.
id
}
/build_status"
,
:sha
,
:status
,
:ref
)
}
expect
(
sha
).
to
be_nil
expect
(
sha
).
to
be_nil
expect
(
status
).
to
be_nil
expect
(
status
).
to
be_nil
expect
(
ref
).
to
be_nil
end
end
end
end
...
...
spec/models/ci/pipeline_spec.rb
View file @
8631779b
...
@@ -375,7 +375,7 @@ describe Ci::Pipeline, models: true do
...
@@ -375,7 +375,7 @@ describe Ci::Pipeline, models: true do
end
end
end
end
describe
'pipeline
ETag
caching'
do
describe
'pipeline caching'
do
it
'executes ExpirePipelinesCacheService'
do
it
'executes ExpirePipelinesCacheService'
do
expect_any_instance_of
(
Ci
::
ExpirePipelineCacheService
).
to
receive
(
:execute
).
with
(
pipeline
)
expect_any_instance_of
(
Ci
::
ExpirePipelineCacheService
).
to
receive
(
:execute
).
with
(
pipeline
)
...
@@ -1079,19 +1079,6 @@ describe Ci::Pipeline, models: true do
...
@@ -1079,19 +1079,6 @@ describe Ci::Pipeline, models: true do
end
end
end
end
describe
'#update_status'
do
let
(
:pipeline
)
{
create
(
:ci_pipeline
,
sha:
'123456'
)
}
it
'updates the cached status'
do
fake_status
=
double
# after updating the status, the status is set to `skipped` for this pipeline's builds
expect
(
Ci
::
PipelineStatus
).
to
receive
(
:new
).
with
(
pipeline
.
project
,
sha:
'123456'
,
status:
'skipped'
).
and_return
(
fake_status
)
expect
(
fake_status
).
to
receive
(
:store_in_cache_if_needed
)
pipeline
.
update_status
end
end
describe
'notifications when pipeline success or failed'
do
describe
'notifications when pipeline success or failed'
do
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:project
)
{
create
(
:project
,
:repository
)
}
...
...
spec/models/project_spec.rb
View file @
8631779b
...
@@ -1874,7 +1874,7 @@ describe Project, models: true do
...
@@ -1874,7 +1874,7 @@ describe Project, models: true do
describe
'#pipeline_status'
do
describe
'#pipeline_status'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:project
)
{
create
(
:project
)
}
it
'builds a pipeline status'
do
it
'builds a pipeline status'
do
expect
(
project
.
pipeline_status
).
to
be_a
(
Ci
::
PipelineStatus
)
expect
(
project
.
pipeline_status
).
to
be_a
(
Gitlab
::
Cache
::
Ci
::
Project
PipelineStatus
)
end
end
it
'hase a loaded pipeline status'
do
it
'hase a loaded pipeline status'
do
...
...
spec/services/ci/expire_pipeline_cache_service_spec.rb
View file @
8631779b
...
@@ -16,5 +16,12 @@ describe Ci::ExpirePipelineCacheService, services: true do
...
@@ -16,5 +16,12 @@ describe Ci::ExpirePipelineCacheService, services: true do
subject
.
execute
(
pipeline
)
subject
.
execute
(
pipeline
)
end
end
it
'updates the cached status for a project'
do
expect
(
Gitlab
::
Cache
::
Ci
::
ProjectPipelineStatus
).
to
receive
(
:update_for_pipeline
).
with
(
pipeline
)
subject
.
execute
(
pipeline
)
end
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment