From 8aeabb01e306cb71d8a9c2a2fa7595e3ec6c14af Mon Sep 17 00:00:00 2001 From: Christophe Monniez Date: Tue, 16 Apr 2019 13:50:00 +0200 Subject: [PATCH] [IMP] runbot: give priority to normal builds When some special builds are scheduled during the night, free slots on runbot instances are used. Depending on the number of scheduled builds, all the slots can be used. That prevents people to use the runbot for normal builds during this time. To mitigate the problem, the scheduled builds were postponed to the middle of the night ... the CET night. It means that it could be morning in India. With this commit, a build priority is given to normal builds. On the other hand, scheduled builds are pushed at the end of the queue. So even if there are plenty of builds during the Belgian night, if someone pushes a commit in between, it will be built in priority before the scheduled pending builds. --- runbot/models/repo.py | 1 + runbot/tests/test_repo.py | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/runbot/models/repo.py b/runbot/models/repo.py index 86f002a5..575f216e 100644 --- a/runbot/models/repo.py +++ b/runbot/models/repo.py @@ -306,6 +306,7 @@ class runbot_repo(models.Model): ORDER BY runbot_branch.sticky DESC, runbot_branch.priority DESC, + array_position(array['normal','rebuild','indirect','scheduled']::varchar[], runbot_build.build_type) ASC, runbot_build.sequence ASC FOR UPDATE OF runbot_build SKIP LOCKED LIMIT %(available_slots)s)""" diff --git a/runbot/tests/test_repo.py b/runbot/tests/test_repo.py index 7ee9b0da..ad9daeb5 100644 --- a/runbot/tests/test_repo.py +++ b/runbot/tests/test_repo.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from unittest.mock import patch from odoo.tests import common +import odoo class Test_Repo(common.TransactionCase): @@ -22,3 +23,78 @@ class Test_Repo(common.TransactionCase): local_repo = self.Repo.create({'name': '/path/somewhere/rep.git'}) self.assertEqual(local_repo.short_name, 'somewhere/rep') + + +class Test_Repo_Scheduler(common.TransactionCase): + + @patch('odoo.addons.runbot.models.repo.runbot_repo._root') + def setUp(self, mock_root): + # as the _scheduler method commits, we need to protect the database + registry = odoo.registry() + registry.enter_test_mode() + self.addCleanup(registry.leave_test_mode) + super(Test_Repo_Scheduler, self).setUp() + + mock_root.return_value = '/tmp/static' + self.Repo_model = self.env['runbot.repo'] + self.Branch_model = self.env['runbot.branch'] + self.foo_repo = self.Repo_model.create({'name': 'bla@example.com:foo/bar'}) + + self.foo_branch = self.Branch_model.create({ + 'repo_id': self.foo_repo.id, + 'name': 'refs/head/foo' + }) + + @patch('odoo.addons.runbot.models.build.runbot_build._reap') + @patch('odoo.addons.runbot.models.build.runbot_build._kill') + @patch('odoo.addons.runbot.models.build.runbot_build._schedule') + @patch('odoo.addons.runbot.models.repo.fqdn') + def test_repo_scheduler(self, mock_repo_fqdn, mock_schedule, mock_kill, mock_reap): + mock_repo_fqdn.return_value = 'test_host' + Build_model = self.env['runbot.build'] + builds = [] + # create 6 builds that are testing on the host to verify that + # workers are not overfilled + for build_name in ['a', 'b', 'c', 'd', 'e', 'f']: + build = Build_model.create({ + 'branch_id': self.foo_branch.id, + 'name': build_name, + 'port': '1234', + 'build_type': 'normal', + 'state': 'testing', + 'host': 'test_host' + }) + builds.append(build) + # now the pending build that should stay unasigned + scheduled_build = Build_model.create({ + 'branch_id': self.foo_branch.id, + 'name': 'sched_build', + 'port': '1234', + 'build_type': 'scheduled', + 'state': 'pending', + }) + builds.append(scheduled_build) + # create the build that should be assigned once a slot is available + build = Build_model.create({ + 'branch_id': self.foo_branch.id, + 'name': 'foobuild', + 'port': '1234', + 'build_type': 'normal', + 'state': 'pending', + }) + builds.append(build) + self.env['runbot.repo']._scheduler(ids=[self.foo_repo.id, ]) + + build.invalidate_cache() + scheduled_build.invalidate_cache() + self.assertFalse(build.host) + self.assertFalse(scheduled_build.host) + + # give some room for the pending build + Build_model.search([('name', '=', 'a')]).write({'state': 'done'}) + + self.env['runbot.repo']._scheduler(ids=[self.foo_repo.id, ]) + build.invalidate_cache() + scheduled_build.invalidate_cache() + self.assertEqual(build.host, 'test_host') + self.assertFalse(scheduled_build.host)