From 695ab84f63238cc5fb3e989f5b53b19c8c9dc55b Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Fri, 25 Feb 2022 17:04:04 +0000 Subject: [PATCH] [IMP] core: allow profiling & rollback of models population Profiling: creation of ir.profile records for each models population Rollback: allow triggering multiple times the same population, without any risk of records conflicts (& having to recreated a clean database) Note: those two options are mostly recommended for small models population. Indeed, profiling slows down the population, and large/medium population is already quite slow for some models (and it seems that the rollback breaks as well for some advanced models). Part-of: odoo/odoo#85537 --- odoo/cli/populate.py | 53 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/odoo/cli/populate.py b/odoo/cli/populate.py index 3eeec92bb14..a72ac970812 100644 --- a/odoo/cli/populate.py +++ b/odoo/cli/populate.py @@ -1,10 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from contextlib import nullcontext import time import fnmatch import logging import optparse +from unittest.mock import patch import odoo from . import Command @@ -22,19 +24,28 @@ class Populate(Command): group.add_option("--models", dest='populate_models', help="Comma separated list of model or pattern (fnmatch)") + group.add_option("--profile", + dest='profiling_enabled', action="store_true", + help="Specify if you want to profile records population.", + default=False) + group.add_option("--rollback", + dest='populate_rollback', action="store_true", + help="Specify if you want to rollback database population.", + default=False) parser.add_option_group(group) opt = odoo.tools.config.parse_config(cmdargs) populate_models = opt.populate_models and set(opt.populate_models.split(',')) - population_size = opt.population_size dbname = odoo.tools.config['db_name'] registry = odoo.registry(dbname) with registry.cursor() as cr: env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) - self.populate(env, population_size, populate_models) - + self.populate( + env, opt.population_size, populate_models, + profiling_enabled=opt.profiling_enabled, + commit=not opt.populate_rollback) @classmethod - def populate(cls, env, size, model_patterns=False): + def populate(cls, env, size, model_patterns=False, profiling_enabled=False, commit=True): registry = env.registry populated_models = None try: @@ -43,11 +54,37 @@ class Populate(Command): _logger.log(25, 'Populating database') for model in ordered_models: + if profiling_enabled: + profiling_context = odoo.tools.profiler.Profiler( + description=f'{model} {size}', + db=env.cr.dbname + ) + else: + profiling_context = nullcontext() + + if commit: + commit_context = nullcontext() + else: + commit_context = patch('odoo.sql_db.Cursor.commit') + _logger.info('Populating database for model %s', model._name) t0 = time.time() - registry.populated_models[model._name] = model._populate(size).ids - # todo indicate somewhere that model is populated - env.cr.commit() + + with profiling_context, commit_context: + registry.populated_models[model._name] = model._populate(size).ids + + if not registry.populated_models[model._name]: + # Do not create ir.profile records + # for models without any population factories + profiling_context.db = False + + # force the flush to make sure population time still + # considers flushing all values to database + env['base'].flush() + + if commit: + env.cr.commit() + model_time = time.time() - t0 if model_time > 1: _logger.info('Populated database for model %s (total: %fs) (average: %fms per record)', @@ -55,6 +92,8 @@ class Populate(Command): except: _logger.exception('Something went wrong populating database') finally: + if not commit: + env.cr.rollback() populated_models = registry.populated_models del registry.populated_models