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
0f602be9
Commit
0f602be9
authored
Apr 13, 2016
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Clear repository check columns asynchronously
parent
9a30d3b5
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
144 additions
and
114 deletions
+144
-114
application_settings_controller.rb
app/controllers/admin/application_settings_controller.rb
+2
-6
projects_controller.rb
app/controllers/admin/projects_controller.rb
+1
-1
batch_worker.rb
app/workers/repository_check/batch_worker.rb
+59
-57
clear_worker.rb
app/workers/repository_check/clear_worker.rb
+18
-0
single_repository_worker.rb
app/workers/repository_check/single_repository_worker.rb
+33
-31
1_settings.rb
config/initializers/1_settings.rb
+1
-1
admin_uses_repository_checks_spec.rb
spec/features/admin/admin_uses_repository_checks_spec.rb
+11
-16
batch_worker_spec.rb
spec/workers/repository_check/batch_worker_spec.rb
+2
-2
clear_worker_spec.rb
spec/workers/repository_check/clear_worker_spec.rb
+17
-0
No files found.
app/controllers/admin/application_settings_controller.rb
View file @
0f602be9
...
...
@@ -20,18 +20,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def
clear_repository_check_states
Project
.
update_all
(
last_repository_check_failed:
false
,
last_repository_check_at:
nil
)
RepositoryCheck
::
ClearWorker
.
perform_async
redirect_to
(
admin_application_settings_path
,
notice:
'
All repository check states were cleared
.'
notice:
'
Started asynchronous removal of all repository check states
.'
)
end
private
def
set_application_setting
...
...
app/controllers/admin/projects_controller.rb
View file @
0f602be9
...
...
@@ -32,7 +32,7 @@ class Admin::ProjectsController < Admin::ApplicationController
end
def
repository_check
SingleRepositoryCheck
Worker
.
perform_async
(
@project
.
id
)
RepositoryCheck
::
SingleRepository
Worker
.
perform_async
(
@project
.
id
)
redirect_to
(
admin_namespace_project_path
(
@project
.
namespace
,
@project
),
...
...
app/workers/repository_check_worker.rb
→
app/workers/repository_check
/batch
_worker.rb
View file @
0f602be9
class
RepositoryCheckWorker
include
Sidekiq
::
Worker
RUN_TIME
=
3600
sidekiq_options
retry:
false
def
perform
start
=
Time
.
now
# This loop will break after a little more than one hour ('a little
# more' because `git fsck` may take a few minutes), or if it runs out of
# projects to check. By default sidekiq-cron will start a new
# RepositoryCheckWorker each hour so that as long as there are repositories to
# check, only one (or two) will be checked at a time.
project_ids
.
each
do
|
project_id
|
break
if
Time
.
now
-
start
>=
RUN_TIME
break
unless
current_settings
.
repository_checks_enabled
next
unless
try_obtain_lease
(
project_id
)
SingleRepositoryCheckWorker
.
new
.
perform
(
project_id
)
module
RepositoryCheck
class
BatchWorker
include
Sidekiq
::
Worker
RUN_TIME
=
3600
sidekiq_options
retry:
false
def
perform
start
=
Time
.
now
# This loop will break after a little more than one hour ('a little
# more' because `git fsck` may take a few minutes), or if it runs out of
# projects to check. By default sidekiq-cron will start a new
# RepositoryCheckWorker each hour so that as long as there are repositories to
# check, only one (or two) will be checked at a time.
project_ids
.
each
do
|
project_id
|
break
if
Time
.
now
-
start
>=
RUN_TIME
break
unless
current_settings
.
repository_checks_enabled
next
unless
try_obtain_lease
(
project_id
)
SingleRepositoryWorker
.
new
.
perform
(
project_id
)
end
end
end
private
# Project.find_each does not support WHERE clauses and
# Project.find_in_batches does not support ordering. So we just build an
# array of ID's. This is OK because we do it only once an hour, because
# getting ID's from Postgres is not terribly slow, and because no user
# has to sit and wait for this query to finish.
def
project_ids
limit
=
10_000
never_checked_projects
=
Project
.
where
(
'last_repository_check_at IS NULL'
).
limit
(
limit
).
pluck
(
:id
)
old_check_projects
=
Project
.
where
(
'last_repository_check_at < ?'
,
1
.
week
.
ago
).
reorder
(
'last_repository_check_at ASC'
).
limit
(
limit
).
pluck
(
:id
)
never_checked_projects
+
old_check_projects
end
def
try_obtain_lease
(
id
)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
# super slow we definitely do not want to run it twice in parallel.
Gitlab
::
ExclusiveLease
.
new
(
"project_repository_check:
#{
id
}
"
,
timeout:
24
.
hours
).
try_obtain
end
def
current_setting
s
# No caching of the settings! If we cache them and an admin disables
# this feature, an active RepositoryCheckWorker would keep going for up
# to 1 hour after the feature was disabled.
if
Rails
.
env
.
test?
Gitlab
::
CurrentSettings
.
fake_application_settings
else
ApplicationSetting
.
current
private
# Project.find_each does not support WHERE clauses and
# Project.find_in_batches does not support ordering. So we just build an
# array of ID's. This is OK because we do it only once an hour, because
# getting ID's from Postgres is not terribly slow, and because no user
# has to sit and wait for this query to finish.
def
project_ids
limit
=
10_000
never_checked_projects
=
Project
.
where
(
'last_repository_check_at IS NULL'
).
limit
(
limit
).
pluck
(
:id
)
old_check_projects
=
Project
.
where
(
'last_repository_check_at < ?'
,
1
.
week
.
ago
).
reorder
(
'last_repository_check_at ASC'
).
limit
(
limit
).
pluck
(
:id
)
never_checked_projects
+
old_check_projects
end
def
try_obtain_lease
(
id
)
# Use a 24-hour timeout because on servers/projects where 'git fsck' is
# super slow we definitely do not want to run it twice in parallel.
Gitlab
::
ExclusiveLease
.
new
(
"project_repository_check:
#{
id
}
"
,
timeout:
24
.
hours
).
try_obtain
end
def
current_settings
# No caching of the settings! If we cache them and an admin disable
s
# this feature, an active RepositoryCheckWorker would keep going for up
# to 1 hour after the feature was disabled.
if
Rails
.
env
.
test?
Gitlab
::
CurrentSettings
.
fake_application_settings
else
ApplicationSetting
.
current
end
end
end
end
app/workers/repository_check/clear_worker.rb
0 → 100644
View file @
0f602be9
module
RepositoryCheck
class
ClearWorker
include
Sidekiq
::
Worker
sidekiq_options
retry:
false
def
perform
# Do batched updates because these updates will be slow and locking
Project
.
select
(
:id
).
find_in_batches
(
batch_size:
1000
)
do
|
batch
|
Project
.
where
(
id:
batch
.
map
(
&
:id
)).
update_all
(
last_repository_check_failed:
nil
,
last_repository_check_at:
nil
,
)
end
end
end
end
\ No newline at end of file
app/workers/
single_repository_check
_worker.rb
→
app/workers/
repository_check/single_repository
_worker.rb
View file @
0f602be9
class
SingleRepositoryCheckWorker
include
Sidekiq
::
Worker
sidekiq_options
retry:
false
def
perform
(
project_id
)
project
=
Project
.
find
(
project_id
)
project
.
update_columns
(
last_repository_check_failed:
!
check
(
project
),
last_repository_check_at:
Time
.
now
,
)
end
private
def
check
(
project
)
# Use 'map do', not 'all? do', to prevent short-circuiting
[
project
.
repository
,
project
.
wiki
.
repository
].
map
do
|
repository
|
git_fsck
(
repository
.
path_to_repo
)
end
.
all?
end
def
git_fsck
(
path
)
cmd
=
%W(nice git --git-dir=
#{
path
}
fsck)
output
,
status
=
Gitlab
::
Popen
.
popen
(
cmd
)
if
status
.
zero?
true
else
Gitlab
::
RepositoryCheckLogger
.
error
(
"command failed:
#{
cmd
.
join
(
' '
)
}
\n
#{
output
}
"
)
false
module
RepositoryCheck
class
SingleRepositoryWorker
include
Sidekiq
::
Worker
sidekiq_options
retry:
false
def
perform
(
project_id
)
project
=
Project
.
find
(
project_id
)
project
.
update_columns
(
last_repository_check_failed:
!
check
(
project
),
last_repository_check_at:
Time
.
now
,
)
end
private
def
check
(
project
)
# Use 'map do', not 'all? do', to prevent short-circuiting
[
project
.
repository
,
project
.
wiki
.
repository
].
map
do
|
repository
|
git_fsck
(
repository
.
path_to_repo
)
end
.
all?
end
def
git_fsck
(
path
)
cmd
=
%W(nice git --git-dir=
#{
path
}
fsck)
output
,
status
=
Gitlab
::
Popen
.
popen
(
cmd
)
if
status
.
zero?
true
else
Gitlab
::
RepositoryCheckLogger
.
error
(
"command failed:
#{
cmd
.
join
(
' '
)
}
\n
#{
output
}
"
)
false
end
end
end
end
config/initializers/1_settings.rb
View file @
0f602be9
...
...
@@ -241,7 +241,7 @@ Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *'
Settings
.
cron_jobs
[
'stuck_ci_builds_worker'
][
'job_class'
]
=
'StuckCiBuildsWorker'
Settings
.
cron_jobs
[
'repository_check_worker'
]
||=
Settingslogic
.
new
({})
Settings
.
cron_jobs
[
'repository_check_worker'
][
'cron'
]
||=
'20 * * * *'
Settings
.
cron_jobs
[
'repository_check_worker'
][
'job_class'
]
=
'RepositoryCheckWorker'
Settings
.
cron_jobs
[
'repository_check_worker'
][
'job_class'
]
=
'RepositoryCheck
::Batch
Worker'
Settings
.
cron_jobs
[
'admin_email_worker'
]
||=
Settingslogic
.
new
({})
Settings
.
cron_jobs
[
'admin_email_worker'
][
'cron'
]
||=
'0 0 * * *'
Settings
.
cron_jobs
[
'admin_email_worker'
][
'job_class'
]
=
'AdminEmailWorker'
...
...
spec/features/admin/admin_uses_repository_checks_spec.rb
View file @
0f602be9
require
'rails_helper'
feature
'Admin uses repository checks'
,
feature:
true
do
before
do
login_as
:admin
end
before
{
login_as
:admin
}
scenario
'to trigger a single check'
do
project
=
create
(
:empty_project
)
...
...
@@ -17,7 +15,12 @@ feature 'Admin uses repository checks', feature: true do
end
scenario
'to see a single failed repository check'
do
visit_admin_project_page
(
broken_project
)
project
=
create
(
:empty_project
)
project
.
update_columns
(
last_repository_check_failed:
true
,
last_repository_check_at:
Time
.
now
,
)
visit_admin_project_page
(
project
)
page
.
within
(
'.alert'
)
do
expect
(
page
.
text
).
to
match
(
/Last repository check \(.* ago\) failed/
)
...
...
@@ -25,24 +28,16 @@ feature 'Admin uses repository checks', feature: true do
end
scenario
'to clear all repository checks'
,
js:
true
do
project
=
broken_project
visit
admin_application_settings_path
expect
(
RepositoryCheck
::
ClearWorker
).
to
receive
(
:perform_async
)
click_link
'Clear all repository checks'
# pop-up should be auto confirmed
click_link
'Clear all repository checks'
expect
(
p
roject
.
reload
.
last_repository_check_failed
).
to
eq
(
false
)
expect
(
p
age
).
to
have_content
(
'Started asynchronous removal of all repository check states.'
)
end
def
visit_admin_project_page
(
project
)
visit
admin_namespace_project_path
(
project
.
namespace
,
project
)
end
def
broken_project
project
=
create
(
:empty_project
)
project
.
update_columns
(
last_repository_check_failed:
true
,
last_repository_check_at:
Time
.
now
,
)
project
end
end
spec/workers/repository_check_worker_spec.rb
→
spec/workers/repository_check
/batch
_worker_spec.rb
View file @
0f602be9
require
'spec_helper'
describe
RepositoryCheckWorker
do
subject
{
RepositoryCheckWorker
.
new
}
describe
RepositoryCheck
::
Batch
Worker
do
subject
{
described_class
.
new
}
it
'prefers projects that have never been checked'
do
projects
=
create_list
(
:project
,
3
)
...
...
spec/workers/repository_check/clear_worker_spec.rb
0 → 100644
View file @
0f602be9
require
'spec_helper'
describe
RepositoryCheck
::
ClearWorker
do
it
'clears repository check columns'
do
project
=
create
(
:empty_project
)
project
.
update_columns
(
last_repository_check_failed:
true
,
last_repository_check_at:
Time
.
now
,
)
described_class
.
new
.
perform
project
.
reload
expect
(
project
.
last_repository_check_failed
).
to
be_nil
expect
(
project
.
last_repository_check_at
).
to
be_nil
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