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
47e6f1d6
Commit
47e6f1d6
authored
Jun 16, 2021
by
Grzegorz Bizon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor builds queue builder to avoid mutations
parent
e9368df2
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
113 additions
and
92 deletions
+113
-92
app/services/ci/register_job_service.rb
app/services/ci/register_job_service.rb
+12
-10
lib/gitlab/ci/queue/builder.rb
lib/gitlab/ci/queue/builder.rb
+101
-82
No files found.
app/services/ci/register_job_service.rb
View file @
47e6f1d6
...
@@ -105,6 +105,7 @@ module Ci
...
@@ -105,6 +105,7 @@ module Ci
def
each_build
(
params
,
&
blk
)
def
each_build
(
params
,
&
blk
)
queue
=
::
Gitlab
::
Ci
::
Queue
::
Builder
.
new
(
runner
)
queue
=
::
Gitlab
::
Ci
::
Queue
::
Builder
.
new
(
runner
)
builds
=
begin
if
runner
.
instance_type?
if
runner
.
instance_type?
queue
.
builds_for_shared_runner
queue
.
builds_for_shared_runner
elsif
runner
.
group_type?
elsif
runner
.
group_type?
...
@@ -112,21 +113,22 @@ module Ci
...
@@ -112,21 +113,22 @@ module Ci
else
else
queue
.
builds_for_project_runner
queue
.
builds_for_project_runner
end
end
end
# pick builds that does not have other tags than runner's one
# pick builds that does not have other tags than runner's one
queue
.
builds_matching_tag_ids
(
runner
.
tags
.
ids
)
builds
=
queue
.
builds_matching_tag_ids
(
builds
,
runner
.
tags
.
ids
)
# pick builds that have at least one tag
# pick builds that have at least one tag
unless
runner
.
run_untagged?
unless
runner
.
run_untagged?
queue
.
builds_with_any_tags
builds
=
queue
.
builds_with_any_tags
(
builds
)
end
end
# pick builds that older than specified age
# pick builds that older than specified age
if
params
.
key?
(
:job_age
)
if
params
.
key?
(
:job_age
)
queue
.
builds_queued_before
(
params
[
:job_age
].
seconds
.
ago
)
builds
=
queue
.
builds_queued_before
(
builds
,
params
[
:job_age
].
seconds
.
ago
)
end
end
build_ids
=
retrieve_queue
(
->
{
queue
.
build_ids
})
build_ids
=
retrieve_queue
(
->
{
queue
.
build_ids
(
builds
)
})
@metrics
.
observe_queue_size
(
->
{
build_ids
.
size
},
@runner
.
runner_type
)
@metrics
.
observe_queue_size
(
->
{
build_ids
.
size
},
@runner
.
runner_type
)
...
...
lib/gitlab/ci/queue/builder.rb
View file @
47e6f1d6
...
@@ -4,89 +4,102 @@ module Gitlab
...
@@ -4,89 +4,102 @@ module Gitlab
module
Ci
module
Ci
module
Queue
module
Queue
class
Builder
<
SimpleDelegator
class
Builder
<
SimpleDelegator
attr_reader
:runner
def
initialize
(
runner
)
def
initialize
(
runner
)
@runner
=
runner
@strategy
=
begin
if
::
Feature
.
enabled?
(
:ci_pending_builds_queue_source
,
runner
,
default_enabled: :yaml
)
if
::
Feature
.
enabled?
(
:ci_pending_builds_queue_source
,
runner
,
default_enabled: :yaml
)
super
(
PendingBuildsTableStrategy
.
new
(
runner
)
)
PendingBuildsTableStrategy
.
new
(
runner
)
else
else
super
(
BuildsTableStrategy
.
new
(
runner
))
BuildsTableStrategy
.
new
(
runner
)
end
end
super
(
@strategy
)
end
end
##
# This is overridden in EE
#
def
builds_for_shared_runner
@strategy
.
builds_for_shared_runner
end
end
# rubocop:disable CodeReuse/ActiveRecord
# rubocop:disable CodeReuse/ActiveRecord
def
builds_for_group_runner
# Workaround for weird Rails bug, that makes `runner.groups.to_sql` to return `runner_id = NULL`
groups
=
::
Group
.
joins
(
:runner_namespaces
).
merge
(
runner
.
runner_namespaces
)
hierarchy_groups
=
Gitlab
::
ObjectHierarchy
.
new
(
groups
,
options:
{
use_distinct:
::
Feature
.
enabled?
(
:use_distinct_in_register_job_object_hierarchy
)
})
.
base_and_descendants
projects
=
Project
.
where
(
namespace_id:
hierarchy_groups
)
.
with_group_runners_enabled
.
with_builds_enabled
.
without_deleted
relation
=
@strategy
.
new_builds
.
where
(
project:
projects
)
@strategy
.
order
(
relation
)
end
def
builds_for_project_runner
relation
=
@strategy
.
new_builds
.
where
(
project:
runner
.
projects
.
without_deleted
.
with_builds_enabled
)
@strategy
.
order
(
relation
)
end
def
builds_queued_before
(
relation
,
time
)
relation
.
queued_before
(
time
)
end
class
BuildsTableStrategy
class
BuildsTableStrategy
attr_reader
:runner
attr_reader
:runner
,
:common
def
initialize
(
runner
)
def
initialize
(
runner
)
@runner
=
runner
@runner
=
runner
@relation
=
new_builds
end
end
def
builds_for_shared_runner
def
builds_for_shared_runner
@
relation
=
new_builds
relation
=
new_builds
# don't run projects which have not enabled shared runners and builds
# don't run projects which have not enabled shared runners and builds
.
joins
(
'INNER JOIN projects ON ci_builds.project_id = projects.id'
)
.
joins
(
'INNER JOIN projects ON ci_builds.project_id = projects.id'
)
.
where
(
projects:
{
shared_runners_enabled:
true
,
pending_delete:
false
})
.
where
(
projects:
{
shared_runners_enabled:
true
,
pending_delete:
false
})
.
joins
(
'LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id'
)
.
joins
(
'LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id'
)
.
where
(
'project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'
)
.
where
(
'project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'
)
@relation
=
begin
if
Feature
.
enabled?
(
:ci_queueing_disaster_recovery
,
runner
,
type: :ops
,
default_enabled: :yaml
)
if
Feature
.
enabled?
(
:ci_queueing_disaster_recovery
,
runner
,
type: :ops
,
default_enabled: :yaml
)
# if disaster recovery is enabled, we fallback to FIFO scheduling
# if disaster recovery is enabled, we fallback to FIFO scheduling
@
relation
.
order
(
'ci_builds.id ASC'
)
relation
.
order
(
'ci_builds.id ASC'
)
else
else
# Implement fair scheduling
# Implement fair scheduling
# this returns builds that are ordered by number of running builds
# this returns builds that are ordered by number of running builds
# we prefer projects that don't use shared runners at all
# we prefer projects that don't use shared runners at all
relation
relation
.
joins
(
"LEFT JOIN (
#{
running_builds_for_shared_runners
.
to_sql
}
) AS project_builds ON ci_builds.project_id=
project_builds.project_id"
)
.
joins
(
"LEFT JOIN (
#{
running_builds_for_shared_runners
.
to_sql
}
) AS project_builds ON ci_builds.project_id =
project_builds.project_id"
)
.
order
(
Arel
.
sql
(
'COALESCE(project_builds.running_builds, 0) ASC'
),
'ci_builds.id ASC'
)
.
order
(
Arel
.
sql
(
'COALESCE(project_builds.running_builds, 0) ASC'
),
'ci_builds.id ASC'
)
end
end
end
end
end
def
builds_for_project_runner
def
builds_matching_tag_ids
(
relation
,
ids
)
@relation
=
new_builds
.
where
(
project:
runner
.
projects
.
without_deleted
.
with_builds_enabled
).
order
(
'id ASC'
)
end
def
builds_for_group_runner
# Workaround for weird Rails bug, that makes `runner.groups.to_sql` to return `runner_id = NULL`
groups
=
::
Group
.
joins
(
:runner_namespaces
).
merge
(
runner
.
runner_namespaces
)
hierarchy_groups
=
Gitlab
::
ObjectHierarchy
.
new
(
groups
,
options:
{
use_distinct:
::
Feature
.
enabled?
(
:use_distinct_in_register_job_object_hierarchy
)
})
.
base_and_descendants
projects
=
Project
.
where
(
namespace_id:
hierarchy_groups
)
.
with_group_runners_enabled
.
with_builds_enabled
.
without_deleted
@relation
=
new_builds
.
where
(
project:
projects
).
order
(
'id ASC'
)
end
def
builds_matching_tag_ids
(
ids
)
# pick builds that does not have other tags than runner's one
# pick builds that does not have other tags than runner's one
@relation
=
@
relation
.
matches_tag_ids
(
ids
)
relation
.
matches_tag_ids
(
ids
)
end
end
def
builds_with_any_tags
def
builds_with_any_tags
(
relation
)
# pick builds that have at least one tag
# pick builds that have at least one tag
@relation
=
@
relation
.
with_any_tags
relation
.
with_any_tags
end
end
def
builds_queued_before
(
time
)
def
order
(
relation
)
@relation
=
@relation
.
queued_before
(
time
)
relation
.
order
(
'id ASC'
)
end
end
def
build_ids
def
build_ids
(
relation
)
@relation
.
pluck
(
:id
)
relation
.
pluck
(
:id
)
end
private
def
all_builds
::
Ci
::
Build
.
pending
.
unstarted
end
end
def
new_builds
def
new_builds
...
@@ -97,6 +110,12 @@ module Gitlab
...
@@ -97,6 +110,12 @@ module Gitlab
end
end
end
end
private
def
all_builds
::
Ci
::
Build
.
pending
.
unstarted
end
def
running_builds_for_shared_runners
def
running_builds_for_shared_runners
::
Ci
::
Build
.
running
::
Ci
::
Build
.
running
.
where
(
runner:
::
Ci
::
Runner
.
instance_type
)
.
where
(
runner:
::
Ci
::
Runner
.
instance_type
)
...
@@ -110,34 +129,31 @@ module Gitlab
...
@@ -110,34 +129,31 @@ module Gitlab
def
initialize
(
runner
)
def
initialize
(
runner
)
@runner
=
runner
@runner
=
runner
@relation
=
new_builds
end
end
def
builds_for_shared_runner
def
builds_for_shared_runner
@
relation
=
new_builds
relation
=
new_builds
# don't run projects which have not enabled shared runners and builds
# don't run projects which have not enabled shared runners and builds
.
joins
(
'INNER JOIN projects ON ci_pending_builds.project_id = projects.id'
)
.
joins
(
'INNER JOIN projects ON ci_pending_builds.project_id = projects.id'
)
.
where
(
projects:
{
shared_runners_enabled:
true
,
pending_delete:
false
})
.
where
(
projects:
{
shared_runners_enabled:
true
,
pending_delete:
false
})
.
joins
(
'LEFT JOIN project_features ON ci_pending_builds.project_id = project_features.project_id'
)
.
joins
(
'LEFT JOIN project_features ON ci_pending_builds.project_id = project_features.project_id'
)
.
where
(
'project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'
)
.
where
(
'project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'
)
@relation
=
begin
if
Feature
.
enabled?
(
:ci_queueing_disaster_recovery
,
runner
,
type: :ops
,
default_enabled: :yaml
)
if
Feature
.
enabled?
(
:ci_queueing_disaster_recovery
,
runner
,
type: :ops
,
default_enabled: :yaml
)
# if disaster recovery is enabled, we fallback to FIFO scheduling
# if disaster recovery is enabled, we fallback to FIFO scheduling
@
relation
.
order
(
'ci_pending_builds.build_id ASC'
)
relation
.
order
(
'ci_pending_builds.build_id ASC'
)
else
else
# Implement fair scheduling
# Implement fair scheduling
# this returns builds that are ordered by number of running builds
# this returns builds that are ordered by number of running builds
# we prefer projects that don't use shared runners at all
# we prefer projects that don't use shared runners at all
@
relation
relation
.
joins
(
"LEFT JOIN (
#{
running_builds_for_shared_runners
.
to_sql
}
) AS project_builds ON ci_pending_builds.project_id=project_builds.project_id"
)
.
joins
(
"LEFT JOIN (
#{
running_builds_for_shared_runners
.
to_sql
}
) AS project_builds ON ci_pending_builds.project_id=project_builds.project_id"
)
.
order
(
Arel
.
sql
(
'COALESCE(project_builds.running_builds, 0) ASC'
),
'ci_pending_builds.build_id ASC'
)
.
order
(
Arel
.
sql
(
'COALESCE(project_builds.running_builds, 0) ASC'
),
'ci_pending_builds.build_id ASC'
)
end
end
end
end
end
def
builds_for_project_runner
def
builds_for_project_runner
@relation
=
new_builds
new_builds
.
where
(
project:
runner
.
projects
.
without_deleted
.
with_builds_enabled
)
.
where
(
project:
runner
.
projects
.
without_deleted
.
with_builds_enabled
)
.
order
(
'build_id ASC'
)
.
order
(
'build_id ASC'
)
end
end
...
@@ -155,29 +171,27 @@ module Gitlab
...
@@ -155,29 +171,27 @@ module Gitlab
.
with_builds_enabled
.
with_builds_enabled
.
without_deleted
.
without_deleted
@relation
=
new_builds
.
where
(
project:
projects
).
order
(
'build_id ASC'
)
new_builds
.
where
(
project:
projects
).
order
(
'build_id ASC'
)
end
end
def
builds_matching_tag_ids
(
ids
)
def
builds_matching_tag_ids
(
relation
,
ids
)
@relation
=
@
relation
.
merge
(
CommitStatus
.
matches_tag_ids
(
ids
,
on:
'ci_pending_builds.build_id'
))
relation
.
merge
(
CommitStatus
.
matches_tag_ids
(
ids
,
on:
'ci_pending_builds.build_id'
))
end
end
def
builds_with_any_tags
def
builds_with_any_tags
(
relation
)
@relation
=
@
relation
.
merge
(
CommitStatus
.
with_any_tags
(
on:
'ci_pending_builds.build_id'
))
relation
.
merge
(
CommitStatus
.
with_any_tags
(
on:
'ci_pending_builds.build_id'
))
end
end
def
builds_queued_before
(
time
)
def
builds_queued_before
(
relation
,
time
)
@relation
=
@
relation
.
queued_before
(
time
)
relation
.
queued_before
(
time
)
end
end
def
build_ids
def
order
(
relation
)
@relation
.
pluck
(
:build_id
)
relation
.
order
(
'build_id ASC'
)
end
end
private
def
build_ids
(
relation
)
relation
.
pluck
(
:build_id
)
def
all_builds
::
Ci
::
PendingBuild
.
all
end
end
def
new_builds
def
new_builds
...
@@ -188,13 +202,18 @@ module Gitlab
...
@@ -188,13 +202,18 @@ module Gitlab
end
end
end
end
private
def
all_builds
::
Ci
::
PendingBuild
.
all
end
def
running_builds_for_shared_runners
def
running_builds_for_shared_runners
::
Ci
::
RunningBuild
::
Ci
::
RunningBuild
.
where
(
runner:
::
Ci
::
Runner
.
instance_type
)
.
where
(
runner:
::
Ci
::
Runner
.
instance_type
)
.
group
(
:project_id
)
.
group
(
:project_id
)
.
select
(
:project_id
,
'count(*) AS running_builds'
)
.
select
(
:project_id
,
'count(*) AS running_builds'
)
end
end
# rubocop:enable CodeReuse/ActiveRecord
# rubocop:enable CodeReuse/ActiveRecord
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