Skip to content

Commit

Permalink
Deletes are now part of an atomic transaction that will set constrain…
Browse files Browse the repository at this point in the history
…ts to immediate (#3005)

I currently suspect that the deferred constraints are causing a problem with accessing the provider table during these long-running deletes. If that is the case, then setting the constraints from deferred to immediate should help.
  • Loading branch information
Red-HAP authored Jul 9, 2021
1 parent ba317d8 commit 32ee18a
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 4 deletions.
18 changes: 14 additions & 4 deletions koku/koku/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ def execute_update_sql(query, **updatespec):
return execute_compiled_sql(sql, params=params)


def set_constraints_immediate():
with transaction.get_connection().cursor() as cur:
LOG.debug("Setting constaints to execute immediately")
cur.execute("set constraints all immediate;")


def cascade_delete(base_model, from_model, instance_pk_query, level=0):
instance_pk_query = instance_pk_query.values_list("pk").order_by()
LOG.info(f"Level {level} Delete Cascade for {base_model.__name__}: Checking relations for {from_model.__name__}")
Expand All @@ -207,8 +213,10 @@ def cascade_delete(base_model, from_model, instance_pk_query, level=0):
f" Executing SET NULL constraint action on {related_model.__name__}"
f" relation of {from_model.__name__}"
)
rec_count = execute_update_sql(related_model.objects.filter(**filterspec), **updatespec)
LOG.info(f" Updated {rec_count} records in {related_model.__name__}")
with transaction.atomic():
set_constraints_immediate()
rec_count = execute_update_sql(related_model.objects.filter(**filterspec), **updatespec)
LOG.info(f" Updated {rec_count} records in {related_model.__name__}")
elif model_relation.on_delete.__name__ == "CASCADE":
filterspec = {f"{model_relation.remote_field.column}__in": models.Subquery(instance_pk_query)}
related_pk_values = related_model.objects.filter(**filterspec).values_list(related_model._meta.pk.name)
Expand All @@ -217,5 +225,7 @@ def cascade_delete(base_model, from_model, instance_pk_query, level=0):

filterspec = {f"{from_model._meta.pk.name}__in": models.Subquery(instance_pk_query)}
LOG.info(f"Level {level}: delete records from {from_model.__name__}")
rec_count = execute_delete_sql(from_model.objects.filter(**filterspec))
LOG.info(f"Deleted {rec_count} records from {from_model.__name__}")
with transaction.atomic():
set_constraints_immediate()
rec_count = execute_delete_sql(from_model.objects.filter(**filterspec))
LOG.info(f"Deleted {rec_count} records from {from_model.__name__}")
48 changes: 48 additions & 0 deletions koku/koku/test_delete_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,51 @@ def test_cascade_delete(self):
self.assertEqual(OCPUsageReportPeriod.objects.filter(pk=ocpurp.pk).count(), 0)

self.assertEqual(Provider.objects.filter(pk__in=(paws.pk, pazure.pk, pgcp.pk, pocp.pk)).count(), 0)

def test_cascade_delete_immediate_constraint(self):
"""Test that cascade_delete can walk relations to delete FK constraint matched records"""
action_ts = datetime.now().replace(tzinfo=UTC)
# Add a bogus customer
c = Customer(
date_created=action_ts,
date_updated=action_ts,
uuid=uuid.uuid4(),
account_id="918273",
schema_name="acct918273",
)
c.save()
# Create a customer tenant
t = Tenant(schema_name=c.schema_name)
t.save()
t.create_schema()
# Add some bogus providers
paws = Provider(
uuid=uuid.uuid4(),
name="eek_aws_provider_4",
type=Provider.PROVIDER_AWS,
setup_complete=False,
active=True,
customer=c,
)
paws.save()
# Create billing period stuff for each provider
period_start = datetime(2020, 1, 1, tzinfo=UTC)
period_end = datetime(2020, 2, 1, tzinfo=UTC)
awsceb = AWSCostEntryBill(
billing_resource="6846351687354184651",
billing_period_start=period_start,
billing_period_end=period_end,
provider=paws,
)
with schema_context(c.schema_name):
awsceb.save()

expected = "DEBUG:koku.database:Setting constaints to execute immediately"
with self.assertLogs("koku.database", level="DEBUG") as _logger:
paws.delete()
self.assertIn(expected, _logger.output)

with schema_context(c.schema_name):
self.assertEqual(AWSCostEntryBill.objects.filter(pk=awsceb.pk).count(), 0)

self.assertEqual(Provider.objects.filter(pk=paws.pk).count(), 0)

0 comments on commit 32ee18a

Please sign in to comment.