[COST-8115] Fix partition-drop deadlock in OCP retention purge - #6270
[COST-8115] Fix partition-drop deadlock in OCP retention purge#6270jordigilh wants to merge 3 commits into
Conversation
purge_expired_report_data_by_date dropped expired partitions for rates_to_usage and every UI_SUMMARY_TABLES entry via one combined execute_delete_sql call spanning all of them. Deleting a PartitionedTable row fires an ACCESS EXCLUSIVE DETACH/TRUNCATE/DROP on its parent table, so one statement/transaction could hold that lock on several different parent tables at once -- the precondition for Postgres's documented ATTACH/DETACH-partition queue-jump deadlock against any concurrent writer touching the same tables in the opposite order. Split the drop into one execute_delete_sql call per table so no single transaction ever holds ACCESS EXCLUSIVE on more than one parent table, eliminating the cross-table deadlock cycle. Spiked and confirmed with a real Postgres test database (deadlock reproduced deterministically across 5 runs); see masu/test/database/test_partition_drop_write_deadlock.py.
|
Warning Review limit reached
Next review available in: 59 minutes Limit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Finding H's fix replaced a single combined multi-table PartitionedTable.objects.filter(...) call with one call per table name, so the existing test's assertion against the last call's args no longer saw the full table list. Updated to aggregate across all calls instead.
koku-ci-triager-bot
left a comment
There was a problem hiding this comment.
🤖 CI Triager — Suggestion
Check: Sanity
Root cause: reorder-python-imports flagged the import order in the new test file. reporting.provider.models must come before reporting.provider.ocp.models alphabetically.
Accept the suggestion below with one click.
| from reporting.provider.ocp.models import OCPCostSummaryP | ||
| from reporting.provider.models import TenantAPIProvider |
There was a problem hiding this comment.
Swap these two imports so provider.models comes before provider.ocp.models.
| from reporting.provider.ocp.models import OCPCostSummaryP | |
| from reporting.provider.models import TenantAPIProvider | |
| from reporting.provider.models import TenantAPIProvider | |
| from reporting.provider.ocp.models import OCPCostSummaryP |
|
🤖 CI Triager — Diagnosis Check: Evidence: Action: Re-run the failing Generated automatically. Review before applying. |
Pure cleanup post-TDD-green: test_partition_drop_write_deadlock.py imported 'time' locally in two separate methods instead of once at module scope. No behavior change; reorder-python-imports also fixed a pre-existing import-ordering nit in the same file.
|
🤖 CI Triager — Diagnosis Check: Root cause: The Evidence: Action: Re-run the failing Generated automatically. Review before applying. |
|
🤖 CI Triager — Diagnosis Check: Root cause: IQE infra/environment issue — an OCP-on-AWS multi-source ingest timed out after 1600 seconds (26 min) waiting for the July Evidence: Action: Re-trigger the smoke tests. The OCP-on-cloud correlation stall is an ephemeral environment / pipeline scheduling issue unrelated to the partition DROP serialization change introduced by this PR. Generated automatically. Review before applying. |
|
/retest |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6270 +/- ##
=======================================
- Coverage 94.4% 94.4% -0.0%
=======================================
Files 369 369
Lines 33578 33580 +2
Branches 3755 3756 +1
=======================================
+ Hits 31708 31709 +1
- Misses 1210 1211 +1
Partials 660 660 🚀 New features to boost your workflow:
|
|
🤖 CI Triager — Diagnosis Check: Root cause: IQE infra/environment issue — the ephemeral koku API pod became unavailable late in the test run. All 108 failures are Evidence: Action: This is a transient ephemeral environment failure unrelated to the PR's changes (the PR only touches Generated automatically. Review before acting. |
|
/retest |
Summary
COST-7249 deadlock preflight, expanded audit, Finding H (see also companion Findings E/F/G in #6268, #6269).
OCPReportDBCleaner.purge_expired_report_data_by_date(invoked by theremove_expired_dataCelery beat task) drops expired partitions forrates_to_usageand everyUI_SUMMARY_TABLESentry via one combinedexecute_delete_sql(PartitionedTable.objects.filter(partition_of_table_name__in=table_names, ...))call.Deleting a
PartitionedTablerow firestrfn_partition_manager()'s DELETE branch:ALTER TABLE ... DETACH PARTITION(ACCESS EXCLUSIVE on the parent table, not just the partition being dropped) +TRUNCATE+DROP TABLE. Sincetable_namesspans multiple parent tables and the whole thing is one SQL statement / one transaction, a single purge run can hold ACCESS EXCLUSIVE locks on several different parent tables simultaneously whenever it has expired partitions on more than one of them -- which is the normal case, not an edge case.This reproduces Postgres's documented ATTACH/DETACH-partition "queue-jump" deadlock: if a concurrent writer touches two of those same tables in the opposite order within one transaction, Postgres's lock-queue fairness rule turns ordinary contention into a genuine wait-for cycle, and the deadlock detector aborts one side.
This is a different statement from the one Finding F (#6269) fixed in the same method -- that fix covers the later
cascade_delete(all_usage_periods...)call. This partition-drop step, earlier in the same method, had no lock of any kind, and a provider-scoped advisory lock (as used elsewhere in this audit) wouldn't help here anyway, since DETACH/CREATE PARTITION DDL locks the whole parent table regardless of which provider's data triggered it.Fix
Split the combined multi-table
execute_delete_sqlcall into one call per table name, so the drop never holds ACCESS EXCLUSIVE on more than one parent table at a time. A wait on a single resource cannot form a cycle, eliminating this deadlock class without adding a new locking primitive.Testing
New spike/regression tests in
masu/test/database/test_partition_drop_write_deadlock.py, run against a real Postgres test database:test_unrelated_write_blocks_behind_unprotected_partition_drop-- confirms an unrelated write to a different, active partition of the same parent table stalls behind the unprotected DROP (contention, not yet a deadlock).test_cross_table_partition_drop_deadlocks_against_cross_table_writer-- characterization test: two sessions touching the same two parent tables in opposite order reproduce a genuine Postgresdeadlock detectederror. Confirmed reproducible across 5 consecutive runs. Kept permanently to document the underlying mechanism.test_fixed_per_table_partition_drop_does_not_deadlock_against_cross_table_writer-- regression test: the same adversarial writer completes cleanly once the drop is split per-table (the fix).Also ran the full existing suites for
masu.test.processor.ocp.test_ocp_report_db_cleaner,koku.test_pg_partition, and the AWS/Azure/GCP report DB cleaner suites -- all pass unmodified (37 + 11 + 3 = 51 tests, no regressions).Related
The sibling AWS/Azure/GCP report DB cleaners use the same combined multi-table
execute_delete_sqlpattern for their own partition-drop steps and are very likely exposed to the same mechanism (observed while running their test suites during this spike). Tracked separately so this fix can land narrowly scoped to OCP.Fixes COST-8115.
Test plan
test_ocp_report_db_cleanersuite passes unmodifiedblack/flake8clean