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 <mcurlej@redhat.com>
This commit is contained in:
Martin Curlej
2019-06-21 16:05:58 +02:00
parent e19c58b6ad
commit ed24ca870a
4 changed files with 74 additions and 1 deletions

View File

@@ -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')

View File

@@ -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

View File

@@ -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

View File

@@ -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