Fix for FK constraint violation

First force purging of records that are not soft deleted but are
referencing soft deleted tasks/images records (e.g. task_info records).
Then purge all soft deleted records in glance tables in the right order
to avoid FK constraint violation.

Closes-Bug: #1803643
Change-Id: I1c471adce002545f8965a57ef78a57e1e3031ef0
Co-authored-by: Tee Ngo <tee.ngo@windriver.com>
Signed-off-by: Liang Fang <liang.a.fang@intel.com>
This commit is contained in:
Liang Fang 2018-11-14 14:18:54 +08:00
parent e7365a4474
commit 72159a4a7b
2 changed files with 53 additions and 0 deletions

View File

@ -1322,6 +1322,35 @@ def purge_deleted_rows(context, age_in_days, max_rows, session=None):
continue
if hasattr(model_class, 'deleted'):
tables.append(model_class.__tablename__)
# First force purging of records that are not soft deleted but
# are referencing soft deleted tasks/images records (e.g. task_info
# records). Then purge all soft deleted records in glance tables in the
# right order to avoid FK constraint violation.
t = Table("tasks", metadata, autoload=True)
ti = Table("task_info", metadata, autoload=True)
joined_rec = ti.join(t, t.c.id == ti.c.task_id)
deleted_task_info = sql.select([ti.c.task_id],
t.c.deleted_at < deleted_age).\
select_from(joined_rec).order_by(t.c.deleted_at).limit(max_rows)
delete_statement = DeleteFromSelect(ti, deleted_task_info,
ti.c.task_id)
LOG.info(_LI('Purging deleted rows older than %(age_in_days)d day(s) '
'from table %(tbl)s'),
{'age_in_days': age_in_days, 'tbl': ti})
try:
with session.begin():
result = session.execute(delete_statement)
except (db_exception.DBError, db_exception.DBReferenceError) as ex:
LOG.exception(_LE('DBError detected when force purging '
'table=%(table)s: %(error)s'),
{'table': ti, 'error': six.text_type(ex)})
raise
rows = result.rowcount
LOG.info(_LI('Deleted %(rows)d row(s) from table %(tbl)s'),
{'rows': rows, 'tbl': ti})
# get rid of FK constraints
for tbl in ('images', 'tasks'):
try:

View File

@ -1946,6 +1946,7 @@ class DBPurgeTests(test_utils.BaseTestCase):
self.adm_context = context.get_admin_context(show_deleted=True)
self.db_api = db_tests.get_db(self.config)
db_tests.reset_db(self.db_api)
self.context = context.RequestContext(is_admin=True)
self.image_fixtures, self.task_fixtures = self.build_fixtures()
self.create_tasks(self.task_fixtures)
self.create_images(self.image_fixtures)
@ -2079,6 +2080,29 @@ class DBPurgeTests(test_utils.BaseTestCase):
images_rows = session.query(images).count()
self.assertEqual(4, images_rows)
def test_purge_task_info_with_refs_to_soft_deleted_tasks(self):
session = db_api.get_session()
engine = db_api.get_engine()
# check initial task and task_info row number are 3
tasks = self.db_api.task_get_all(self.adm_context)
self.assertEqual(3, len(tasks))
task_info = sqlalchemyutils.get_table(engine, 'task_info')
task_info_rows = session.query(task_info).count()
self.assertEqual(3, task_info_rows)
# purge soft deleted rows older than yesterday
self.db_api.purge_deleted_rows(self.context, 1, 5)
# check 1 row of task table is purged
tasks = self.db_api.task_get_all(self.adm_context)
self.assertEqual(2, len(tasks))
# and no task_info was left behind, 1 row purged
task_info_rows = session.query(task_info).count()
self.assertEqual(2, task_info_rows)
class TestVisibility(test_utils.BaseTestCase):
def setUp(self):