Fair usage of Shared Runners
This commit is contained in:
parent
6aefd3c321
commit
ee2e583500
3 changed files with 60 additions and 6 deletions
|
@ -19,6 +19,7 @@ v 8.9.0 (unreleased)
|
|||
- Added descriptions to notification settings dropdown
|
||||
- Improve note validation to prevent errors when creating invalid note via API
|
||||
- Reduce number of fog gem dependencies
|
||||
- Implement a fair usage of shared runners
|
||||
- Remove project notification settings associated with deleted projects
|
||||
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
|
||||
- Redesign navigation for project pages
|
||||
|
|
|
@ -7,15 +7,15 @@ module Ci
|
|||
|
||||
builds =
|
||||
if current_runner.shared?
|
||||
# don't run projects which have not enables shared runners
|
||||
builds.joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true })
|
||||
# this returns builds that are ordered by number of running builds
|
||||
# we prefer projects that don't use shared runners at all
|
||||
builds.joins("JOIN (#{projects_with_builds_for_shared_runners.to_sql}) AS projects ON ci_builds.gl_project_id=projects.gl_project_id").
|
||||
order('projects.running_builds ASC', 'ci_builds.id ASC')
|
||||
else
|
||||
# do run projects which are only assigned to this runner
|
||||
builds.where(project: current_runner.projects.where(builds_enabled: true))
|
||||
# do run projects which are only assigned to this runner (FIFO)
|
||||
builds.where(project: current_runner.projects.where(builds_enabled: true)).order('created_at ASC')
|
||||
end
|
||||
|
||||
builds = builds.order('created_at ASC')
|
||||
|
||||
build = builds.find do |build|
|
||||
build.can_be_served?(current_runner)
|
||||
end
|
||||
|
@ -35,5 +35,18 @@ module Ci
|
|||
rescue StateMachines::InvalidTransition
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def projects_with_builds_for_shared_runners
|
||||
Ci::Build.running_or_pending.
|
||||
joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }).
|
||||
group(:gl_project_id).
|
||||
select(:gl_project_id, "count(case when status = 'running' AND runner_id = (#{shared_runners.to_sql}) then 1 end) as running_builds")
|
||||
end
|
||||
|
||||
def shared_runners
|
||||
Ci::Runner.shared.select(:id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,6 +50,46 @@ module Ci
|
|||
project.update(shared_runners_enabled: true)
|
||||
end
|
||||
|
||||
context 'for multiple builds' do
|
||||
let!(:project2) { create :empty_project, shared_runners_enabled: true }
|
||||
let!(:pipeline2) { create :ci_pipeline, project: project2 }
|
||||
let!(:project3) { create :empty_project, shared_runners_enabled: true }
|
||||
let!(:pipeline3) { create :ci_pipeline, project: project3 }
|
||||
let!(:build1_project1) { pending_build }
|
||||
let!(:build2_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
|
||||
let!(:build3_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
|
||||
let!(:build1_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
|
||||
let!(:build2_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
|
||||
let!(:build1_project3) { FactoryGirl.create :ci_build, pipeline: pipeline3 }
|
||||
|
||||
it 'prefers projects without builds first' do
|
||||
# it gets for one build from each of the projects
|
||||
expect(service.execute(shared_runner)).to eq(build1_project1)
|
||||
expect(service.execute(shared_runner)).to eq(build1_project2)
|
||||
expect(service.execute(shared_runner)).to eq(build1_project3)
|
||||
|
||||
# then it gets a second build from each of the projects
|
||||
expect(service.execute(shared_runner)).to eq(build2_project1)
|
||||
expect(service.execute(shared_runner)).to eq(build2_project2)
|
||||
|
||||
# in the end the third build
|
||||
expect(service.execute(shared_runner)).to eq(build3_project1)
|
||||
end
|
||||
|
||||
it 'equalises number of running builds' do
|
||||
# after finishing the first build for project 1, get a second build from the same project
|
||||
expect(service.execute(shared_runner)).to eq(build1_project1)
|
||||
build1_project1.success
|
||||
expect(service.execute(shared_runner)).to eq(build2_project1)
|
||||
|
||||
expect(service.execute(shared_runner)).to eq(build1_project2)
|
||||
build1_project2.success
|
||||
expect(service.execute(shared_runner)).to eq(build2_project2)
|
||||
expect(service.execute(shared_runner)).to eq(build1_project3)
|
||||
expect(service.execute(shared_runner)).to eq(build3_project1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared runner' do
|
||||
let(:build) { service.execute(shared_runner) }
|
||||
|
||||
|
|
Loading…
Reference in a new issue