From 506ff03e07d7d8d8409b87ddc0fe9a23c5024f96 Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Wed, 28 Jun 2023 08:06:50 +0200 Subject: [PATCH] [IMP] runbot: pause, and profile. Pausing a host can be usefull in some case, mainly when testing new code The loop will have no effect avoiding to break some build wainting for testing. Profile will help to identify potential performance flows during the loop. --- runbot/models/host.py | 3 +++ runbot_builder/tools.py | 41 ++++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/runbot/models/host.py b/runbot/models/host.py index 947928e4..e9939415 100644 --- a/runbot/models/host.py +++ b/runbot/models/host.py @@ -38,6 +38,9 @@ class Host(models.Model): host_message_ids = fields.One2many('runbot.host.message', 'host_id') build_ids = fields.One2many('runbot.build', compute='_compute_build_ids') + paused = fields.Boolean('Paused', help='Host will stop scheduling while paused') + profile = fields.Boolean('Profile', help='Enable profiling on this host') + def _compute_nb(self): groups = self.env['runbot.build'].read_group( [('host', 'in', self.mapped('name')), ('local_state', 'in', ('testing', 'running'))], diff --git a/runbot_builder/tools.py b/runbot_builder/tools.py index c5135c79..63b11ebf 100644 --- a/runbot_builder/tools.py +++ b/runbot_builder/tools.py @@ -10,6 +10,7 @@ import threading import time import signal +from contextlib import nullcontext from datetime import datetime, timedelta, timezone from pathlib import Path from logging.handlers import WatchedFileHandler @@ -37,6 +38,7 @@ class RunbotClient(): def main_loop(self): from odoo import fields + from odoo.tools.profiler import Profiler self.on_start() signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler) @@ -52,23 +54,28 @@ class RunbotClient(): ' (assigned only)' if self.host.assigned_only else '' ) while True: - try: - self.host.last_start_loop = fields.Datetime.now() - self.env.cr.commit() - self.count = self.count % self.max_count - sleep_time = self.loop_turn() - self.count += 1 - self.host.last_end_loop = fields.Datetime.now() - self.env.cr.commit() - self.env.clear() - self.sleep(sleep_time) - except Exception as e: - _logger.exception('Builder main loop failed with: %s', e) - self.env.cr.rollback() - self.env.clear() - self.sleep(10) - if self.ask_interrupt.is_set(): - return + context_manager = Profiler(db=self.env.cr.dbname) if self.host.profile else nullcontext() + with context_manager: + try: + self.host.last_start_loop = fields.Datetime.now() + self.env.cr.commit() + self.count = self.count % self.max_count + if self.host.paused: + sleep_time = 5 + else: + sleep_time = self.loop_turn() + self.count += 1 + self.host.last_end_loop = fields.Datetime.now() + self.env.cr.commit() + self.env.clear() + self.sleep(sleep_time) + except Exception as e: + _logger.exception('Builder main loop failed with: %s', e) + self.env.cr.rollback() + self.env.clear() + self.sleep(10) + if self.ask_interrupt.is_set(): + return def loop_turn(self): raise NotImplementedError()