summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Callaghan <dcallagh@redhat.com>2018-06-04 17:25:58 +1000
committerDan Callaghan <dcallagh@redhat.com>2018-07-27 03:00:59 +0000
commitea0711c0bad6bd21c93e4921700a869245ee0d67 (patch)
tree18f71407521a8770b5d60a6bc477669896fee9eb
parentd8694c5e0f714235c66c821ea3234a60ea49eb0b (diff)
replace job dirty_version/clean_version columns with a simple is_dirty flag
-rw-r--r--IntegrationTests/src/bkr/inttest/server/test_database_migration.py8
-rw-r--r--IntegrationTests/src/bkr/inttest/server/test_model.py2
-rw-r--r--Server/bkr/server/alembic/versions/4d4e0a92ee10_job_is_dirty.py53
-rw-r--r--Server/bkr/server/model/scheduler.py19
-rw-r--r--Server/bkr/server/tools/beakerd.py6
5 files changed, 67 insertions, 21 deletions
diff --git a/IntegrationTests/src/bkr/inttest/server/test_database_migration.py b/IntegrationTests/src/bkr/inttest/server/test_database_migration.py
index a364390..577f79f 100644
--- a/IntegrationTests/src/bkr/inttest/server/test_database_migration.py
+++ b/IntegrationTests/src/bkr/inttest/server/test_database_migration.py
@@ -883,8 +883,8 @@ class MigrationTest(unittest.TestCase):
upgrade_db(self.migration_metadata)
# create a job in Installing state
connection.execute(
- "INSERT INTO job (owner_id, retention_tag_id, dirty_version, clean_version, status) "
- "VALUES (1, 1, '', '', 'Installing')")
+ "INSERT INTO job (owner_id, retention_tag_id, is_dirty, status) "
+ "VALUES (1, 1, FALSE, 'Installing')")
connection.execute(
"INSERT INTO recipe_set (job_id, queue_time, waived, status) "
"VALUES (1, '2015-11-09 17:03:04', FALSE, 'Installing')")
@@ -1361,8 +1361,8 @@ class MigrationTest(unittest.TestCase):
"INSERT INTO tg_user (user_id, user_name, display_name, email_address, disabled, removed, use_old_job_page, notify_job_completion, notify_broken_system, notify_system_loan, notify_group_membership, notify_reservesys) "
"VALUES (1, 'bob', 'Bob', 'bob@example.com', 1, '2015-12-01 15:43:28', 0, 0, 0, 0, 0, 0)")
connection.execute(
- "INSERT INTO job (owner_id, retention_tag_id, dirty_version, clean_version) "
- "VALUES (1, 1, '', '')")
+ "INSERT INTO job (owner_id, retention_tag_id, is_dirty) "
+ "VALUES (1, 1, FALSE)")
connection.execute(
"INSERT INTO arch (id, arch) "
"VALUES (2, 'i386')")
diff --git a/IntegrationTests/src/bkr/inttest/server/test_model.py b/IntegrationTests/src/bkr/inttest/server/test_model.py
index 9474a12..160eaa7 100644
--- a/IntegrationTests/src/bkr/inttest/server/test_model.py
+++ b/IntegrationTests/src/bkr/inttest/server/test_model.py
@@ -1029,7 +1029,7 @@ class TestJob(DatabaseTestCase):
job = data_setup.create_job()
data_setup.mark_job_waiting(job)
job.cancel()
- self.assertNotEquals(job.dirty_version, job.clean_version)
+ self.assertTrue(job.is_dirty)
job.update_status()
self.assertEquals(job.status, TaskStatus.cancelled)
self.assertEquals(job.result, TaskResult.warn)
diff --git a/Server/bkr/server/alembic/versions/4d4e0a92ee10_job_is_dirty.py b/Server/bkr/server/alembic/versions/4d4e0a92ee10_job_is_dirty.py
new file mode 100644
index 0000000..c8bf23f
--- /dev/null
+++ b/Server/bkr/server/alembic/versions/4d4e0a92ee10_job_is_dirty.py
@@ -0,0 +1,53 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+"""Replace job dirty_version/clean_version with is_dirty
+
+Revision ID: 4d4e0a92ee10
+Revises: 1ce53a2af0ed
+Create Date: 2018-06-04 17:04:35.679835
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '4d4e0a92ee10'
+down_revision = '1ce53a2af0ed'
+
+from alembic import op
+from sqlalchemy import Column, Boolean
+
+def upgrade():
+ op.add_column('job', Column('is_dirty', Boolean(create_constraint=False), nullable=True))
+ op.execute("""
+ UPDATE job
+ SET is_dirty = (dirty_version != clean_version)
+ """)
+ op.execute("""
+ ALTER TABLE job
+ MODIFY is_dirty TINYINT NOT NULL,
+ ADD KEY ix_job_is_dirty (is_dirty),
+ DROP KEY ix_job_dirty_clean_version,
+ DROP dirty_version,
+ DROP clean_version
+ """)
+
+def downgrade():
+ op.execute("""
+ ALTER TABLE job
+ ADD dirty_version BINARY(16),
+ ADD clean_version BINARY(16),
+ ADD KEY ix_job_dirty_clean_version (dirty_version, clean_version)
+ """)
+ op.execute("""
+ UPDATE job
+ SET dirty_version = '0000000000000000',
+ clean_version = CASE WHEN is_dirty THEN '1111111111111111' ELSE '0000000000000000' END
+ """)
+ op.execute("""
+ ALTER TABLE job
+ MODIFY dirty_version BINARY(16) NOT NULL,
+ MODIFY clean_version BINARY(16) NOT NULL,
+ DROP is_dirty
+ """)
diff --git a/Server/bkr/server/model/scheduler.py b/Server/bkr/server/model/scheduler.py
index 9764e7a..93e910f 100644
--- a/Server/bkr/server/model/scheduler.py
+++ b/Server/bkr/server/model/scheduler.py
@@ -627,8 +627,7 @@ class Job(TaskBase, ActivityMixin):
__tablename__ = 'job'
__table_args__ = {'mysql_engine': 'InnoDB'}
id = Column(Integer, primary_key=True)
- dirty_version = Column(UUID, nullable=False)
- clean_version = Column(UUID, nullable=False)
+ is_dirty = Column(Boolean, nullable=False, index=True)
owner_id = Column(Integer, ForeignKey('tg_user.user_id'), index=True)
owner = relationship(User, back_populates='jobs',
primaryjoin=owner_id == User.user_id)
@@ -685,8 +684,7 @@ class Job(TaskBase, ActivityMixin):
self.whiteboard = whiteboard
self.retention_tag = retention_tag
self.product = product
- self.dirty_version = uuid.uuid4()
- self.clean_version = self.dirty_version
+ self.is_dirty = False
stop_types = ['abort','cancel']
max_by_whiteboard = 20
@@ -1258,14 +1256,12 @@ class Job(TaskBase, ActivityMixin):
self._mark_clean()
def _mark_dirty(self):
- self.dirty_version = uuid.uuid4()
+ self.is_dirty = True
def _mark_clean(self):
- self.clean_version = self.dirty_version
-
- @hybrid_property
- def is_dirty(self):
- return (self.dirty_version != self.clean_version)
+ # NOTE: this is only race-free if the transaction issued a SELECT...FOR UPDATE
+ # on this job row before doing any other work.
+ self.is_dirty = False
def _update_status(self):
"""
@@ -1367,9 +1363,6 @@ class Job(TaskBase, ActivityMixin):
cc = association_proxy('_job_ccs', 'email_address')
-# for fast dirty_version != clean_version comparisons:
-Index('ix_job_dirty_clean_version', Job.dirty_version, Job.clean_version)
-
class JobCc(DeclarativeMappedObject):
__tablename__ = 'job_cc'
diff --git a/Server/bkr/server/tools/beakerd.py b/Server/bkr/server/tools/beakerd.py
index cdf3c79..fbba0ee 100644
--- a/Server/bkr/server/tools/beakerd.py
+++ b/Server/bkr/server/tools/beakerd.py
@@ -92,7 +92,7 @@ def get_virt_executor():
def update_dirty_jobs():
work_done = False
with session.begin():
- dirty_jobs = Job.query.filter(Job.dirty_version != Job.clean_version)
+ dirty_jobs = Job.query.filter(Job.is_dirty)
job_ids = [job_id for job_id, in dirty_jobs.values(Job.id)]
if job_ids:
log.debug('Updating dirty jobs [%s ... %s] (%d total)',
@@ -114,7 +114,7 @@ def update_dirty_jobs():
def update_dirty_job(job_id):
log.debug('Updating dirty job %s', job_id)
- job = Job.by_id(job_id)
+ job = Job.query.filter(Job.id == job_id).with_lockmode('update').one()
job.update_status()
def process_new_recipes(*args):
@@ -360,7 +360,7 @@ def abort_dead_recipes(*args):
filters.append(not_(Recipe.systems.any()))
recipes = MachineRecipe.query\
.join(MachineRecipe.recipeset).join(RecipeSet.job)\
- .filter(Job.dirty_version == Job.clean_version)\
+ .filter(not_(Job.is_dirty))\
.outerjoin(Recipe.distro_tree)\
.filter(Recipe.status == TaskStatus.queued)\
.filter(or_(*filters))