From ed24ca870a1e559f7a2b2e56a6434edaa22ffb80 Mon Sep 17 00:00:00 2001 From: Martin Curlej Date: Fri, 21 Jun 2019 16:05:58 +0200 Subject: [PATCH] Module builds now remember what module they reused for building. There was a race condition, when 2 builds where build in the same time. There was an issue that after one has finished the other reused the new build for reuse and not the one it started with. Ticket-ID: FACTORY-3862 Signed-off-by: Martin Curlej --- ...0b2c7d988d7_add_reused_module_id_column.py | 23 ++++++++++ module_build_service/models.py | 2 + module_build_service/utils/reuse.py | 8 +++- tests/test_utils/test_utils.py | 42 +++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 module_build_service/migrations/versions/40b2c7d988d7_add_reused_module_id_column.py diff --git a/module_build_service/migrations/versions/40b2c7d988d7_add_reused_module_id_column.py b/module_build_service/migrations/versions/40b2c7d988d7_add_reused_module_id_column.py new file mode 100644 index 00000000..514786b6 --- /dev/null +++ b/module_build_service/migrations/versions/40b2c7d988d7_add_reused_module_id_column.py @@ -0,0 +1,23 @@ +"""add reused_module_id column + +Revision ID: 40b2c7d988d7 +Revises: bf861b6af29a +Create Date: 2019-06-21 13:41:06.041269 + +""" + +# revision identifiers, used by Alembic. +revision = '40b2c7d988d7' +down_revision = 'bf861b6af29a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('module_builds', sa.Column('reused_module_id', sa.Integer(), nullable=True)) + sa.ForeignKeyConstraint(['reused_module_id'], ['module_builds.id'], ), + + +def downgrade(): + op.drop_column('module_builds', 'reused_module_id') diff --git a/module_build_service/models.py b/module_build_service/models.py index 17c73bb3..b3fae068 100644 --- a/module_build_service/models.py +++ b/module_build_service/models.py @@ -221,6 +221,8 @@ class ModuleBuild(MBSBase): rebuild_strategy = db.Column(db.String, nullable=False) virtual_streams = db.relationship( "VirtualStream", secondary=module_builds_to_virtual_streams, back_populates="module_builds") + reused_module_id = db.Column(db.Integer, db.ForeignKey("module_builds.id")) + reused_module = db.relationship("ModuleBuild", remote_side="ModuleBuild.id") # List of arches against which the module is built. # NOTE: It is not filled for imported modules, because imported module builds have not been diff --git a/module_build_service/utils/reuse.py b/module_build_service/utils/reuse.py index 9ae05687..b1397144 100644 --- a/module_build_service/utils/reuse.py +++ b/module_build_service/utils/reuse.py @@ -85,8 +85,11 @@ def get_reusable_module(session, module): :param module: the ModuleBuild object of module being built. :return: ModuleBuild object which can be used for component reuse. """ - mmd = module.mmd() + if module.reused_module: + return module.reused_module + + mmd = module.mmd() # Find the latest module that is in the done or ready state previous_module_build = ( session.query(models.ModuleBuild) @@ -111,6 +114,9 @@ def get_reusable_module(session, module): log.info("Cannot re-use. %r is the first module build." % module) return None + module.reused_module_id = previous_module_build.id + session.commit() + return previous_module_build diff --git a/tests/test_utils/test_utils.py b/tests/test_utils/test_utils.py index 2abe6975..cb04883e 100644 --- a/tests/test_utils/test_utils.py +++ b/tests/test_utils/test_utils.py @@ -1429,3 +1429,45 @@ class TestOfflineLocalBuilds: module_build = models.ModuleBuild.get_build_from_nsvc( db.session, "platform", "y", 1, "000000") assert module_build + + +class TestUtilsModuleReuse: + + def setup_method(self, test_method): + init_data() + + def teardown_method(self, test_method): + clean_database() + + def test_get_reusable_module_when_reused_module_not_set(self): + module = models.ModuleBuild.query.filter_by( + name="nginx").order_by(models.ModuleBuild.id.desc()).first() + module.state = models.BUILD_STATES["build"] + db.session.commit() + + assert not module.reused_module + + reusable_module = module_build_service.utils.get_reusable_module( + db.session, module) + + assert module.reused_module + assert reusable_module.id == module.reused_module_id + + def test_get_reusable_module_when_reused_module_already_set(self): + modules = models.ModuleBuild.query.filter_by( + name="nginx").order_by(models.ModuleBuild.id.desc()).limit(2).all() + build_module = modules[0] + reused_module = modules[1] + build_module.state = models.BUILD_STATES["build"] + build_module.reused_module_id = reused_module.id + db.session.commit() + + assert build_module.reused_module + assert reused_module == build_module.reused_module + + reusable_module = module_build_service.utils.get_reusable_module( + db.session, build_module) + + assert build_module.reused_module + assert reusable_module.id == build_module.reused_module_id + assert reusable_module.id == reused_module.id