Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
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
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
150 additions
and
121 deletions
+150
-121
app/controllers/admin/application_settings_controller.rb
app/controllers/admin/application_settings_controller.rb
+2
-6
app/controllers/admin/projects_controller.rb
app/controllers/admin/projects_controller.rb
+1
-1
app/workers/repository_check/batch_worker.rb
app/workers/repository_check/batch_worker.rb
+63
-0
app/workers/repository_check/clear_worker.rb
app/workers/repository_check/clear_worker.rb
+17
-0
app/workers/repository_check/single_repository_worker.rb
app/workers/repository_check/single_repository_worker.rb
+36
-0
app/workers/repository_check_worker.rb
app/workers/repository_check_worker.rb
+0
-61
app/workers/single_repository_check_worker.rb
app/workers/single_repository_check_worker.rb
+0
-34
config/initializers/1_settings.rb
config/initializers/1_settings.rb
+1
-1
spec/features/admin/admin_uses_repository_checks_spec.rb
spec/features/admin/admin_uses_repository_checks_spec.rb
+11
-16
spec/workers/repository_check/batch_worker_spec.rb
spec/workers/repository_check/batch_worker_spec.rb
+2
-2
spec/workers/repository_check/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
...
@@ -20,18 +20,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
end
def
clear_repository_check_states
def
clear_repository_check_states
Project
.
update_all
(
RepositoryCheck
::
ClearWorker
.
perform_async
last_repository_check_failed:
false
,
last_repository_check_at:
nil
)
redirect_to
(
redirect_to
(
admin_application_settings_path
,
admin_application_settings_path
,
notice:
'
All repository check states were cleared
.'
notice:
'
Started asynchronous removal of all repository check states
.'
)
)
end
end
private
private
def
set_application_setting
def
set_application_setting
...
...
app/controllers/admin/projects_controller.rb
View file @
0f602be9
...
@@ -32,7 +32,7 @@ class Admin::ProjectsController < Admin::ApplicationController
...
@@ -32,7 +32,7 @@ class Admin::ProjectsController < Admin::ApplicationController
end
end
def
repository_check
def
repository_check
SingleRepositoryCheck
Worker
.
perform_async
(
@project
.
id
)
RepositoryCheck
::
SingleRepository
Worker
.
perform_async
(
@project
.
id
)
redirect_to
(
redirect_to
(
admin_namespace_project_path
(
@project
.
namespace
,
@project
),
admin_namespace_project_path
(
@project
.
namespace
,
@project
),
...
...
app/workers/repository_check/batch_worker.rb
0 → 100644
View file @
0f602be9
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
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 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
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/repository_check/single_repository_worker.rb
0 → 100644
View file @
0f602be9
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
app/workers/repository_check_worker.rb
deleted
100644 → 0
View file @
9a30d3b5
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
)
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_settings
# 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
end
end
end
app/workers/single_repository_check_worker.rb
deleted
100644 → 0
View file @
9a30d3b5
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
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 * * *'
...
@@ -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
[
'stuck_ci_builds_worker'
][
'job_class'
]
=
'StuckCiBuildsWorker'
Settings
.
cron_jobs
[
'repository_check_worker'
]
||=
Settingslogic
.
new
({})
Settings
.
cron_jobs
[
'repository_check_worker'
]
||=
Settingslogic
.
new
({})
Settings
.
cron_jobs
[
'repository_check_worker'
][
'cron'
]
||=
'20 * * * *'
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'
]
||=
Settingslogic
.
new
({})
Settings
.
cron_jobs
[
'admin_email_worker'
][
'cron'
]
||=
'0 0 * * *'
Settings
.
cron_jobs
[
'admin_email_worker'
][
'cron'
]
||=
'0 0 * * *'
Settings
.
cron_jobs
[
'admin_email_worker'
][
'job_class'
]
=
'AdminEmailWorker'
Settings
.
cron_jobs
[
'admin_email_worker'
][
'job_class'
]
=
'AdminEmailWorker'
...
...
spec/features/admin/admin_uses_repository_checks_spec.rb
View file @
0f602be9
require
'rails_helper'
require
'rails_helper'
feature
'Admin uses repository checks'
,
feature:
true
do
feature
'Admin uses repository checks'
,
feature:
true
do
before
do
before
{
login_as
:admin
}
login_as
:admin
end
scenario
'to trigger a single check'
do
scenario
'to trigger a single check'
do
project
=
create
(
:empty_project
)
project
=
create
(
:empty_project
)
...
@@ -17,7 +15,12 @@ feature 'Admin uses repository checks', feature: true do
...
@@ -17,7 +15,12 @@ feature 'Admin uses repository checks', feature: true do
end
end
scenario
'to see a single failed repository check'
do
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
page
.
within
(
'.alert'
)
do
expect
(
page
.
text
).
to
match
(
/Last repository check \(.* ago\) failed/
)
expect
(
page
.
text
).
to
match
(
/Last repository check \(.* ago\) failed/
)
...
@@ -25,24 +28,16 @@ feature 'Admin uses repository checks', feature: true do
...
@@ -25,24 +28,16 @@ feature 'Admin uses repository checks', feature: true do
end
end
scenario
'to clear all repository checks'
,
js:
true
do
scenario
'to clear all repository checks'
,
js:
true
do
project
=
broken_project
visit
admin_application_settings_path
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
end
def
visit_admin_project_page
(
project
)
def
visit_admin_project_page
(
project
)
visit
admin_namespace_project_path
(
project
.
namespace
,
project
)
visit
admin_namespace_project_path
(
project
.
namespace
,
project
)
end
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
end
spec/workers/repository_check_worker_spec.rb
→
spec/workers/repository_check
/batch
_worker_spec.rb
View file @
0f602be9
require
'spec_helper'
require
'spec_helper'
describe
RepositoryCheckWorker
do
describe
RepositoryCheck
::
Batch
Worker
do
subject
{
RepositoryCheckWorker
.
new
}
subject
{
described_class
.
new
}
it
'prefers projects that have never been checked'
do
it
'prefers projects that have never been checked'
do
projects
=
create_list
(
:project
,
3
)
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