mirror of
https://github.com/ansible/awx.git
synced 2026-10-29 22:03:20 -02:30
AAP-87549 — Add management command to create JobHostSummary composite index (#16586)
Adds `create_host_summary_index` management command that creates the composite index (host_id, id DESC) on main_jobhostsummary using CREATE INDEX CONCURRENTLY. The command is idempotent: it checks for existing valid/invalid indexes, drops invalid indexes from failed prior attempts, and skips if the index already exists. Disables statement_timeout for the session to avoid killing the index build on large tables where ALTER ROLE timeout is set. Forward-port of tower#7893 (stable-2.6). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
7242fe89ae
commit
587b816793
@@ -0,0 +1,59 @@
|
|||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import connection
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Creates composite index (host_id, id DESC) on main_jobhostsummary to improve host list query performance.'
|
||||||
|
|
||||||
|
INDEX_NAME = 'main_jobhostsumm_host_id_desc'
|
||||||
|
TABLE_NAME = 'main_jobhostsummary'
|
||||||
|
|
||||||
|
def _index_status(self):
|
||||||
|
"""Check whether the index exists in pg_class and whether it is valid.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
'valid' - index exists and is usable; no action needed.
|
||||||
|
'invalid' - index exists but was left in an invalid state by a
|
||||||
|
previous failed CREATE INDEX CONCURRENTLY; must be
|
||||||
|
dropped before retrying.
|
||||||
|
None - index does not exist; safe to create.
|
||||||
|
"""
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT pg_index.indisvalid " "FROM pg_class JOIN pg_index ON pg_class.oid = pg_index.indexrelid " "WHERE pg_class.relname = %s",
|
||||||
|
[self.INDEX_NAME],
|
||||||
|
)
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row is None:
|
||||||
|
return None
|
||||||
|
return 'valid' if row[0] else 'invalid'
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
status = self._index_status()
|
||||||
|
|
||||||
|
if status == 'valid':
|
||||||
|
self.stdout.write(f"Index {self.INDEX_NAME} already exists, nothing to do.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if status == 'invalid':
|
||||||
|
self.stdout.write(f"Dropping invalid index {self.INDEX_NAME} from a previous failed attempt...")
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(f"DROP INDEX IF EXISTS {self.INDEX_NAME}")
|
||||||
|
|
||||||
|
self.stdout.write(f"Creating index {self.INDEX_NAME} on {self.TABLE_NAME} (host_id, id DESC) concurrently...")
|
||||||
|
|
||||||
|
# CREATE INDEX CONCURRENTLY cannot run inside a transaction.
|
||||||
|
connection.ensure_connection()
|
||||||
|
old_autocommit = connection.connection.autocommit
|
||||||
|
connection.connection.autocommit = True
|
||||||
|
try:
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
# Disable statement_timeout for this session — the customer may
|
||||||
|
# have set ALTER ROLE ... SET statement_timeout which would kill
|
||||||
|
# the index build on large tables.
|
||||||
|
cursor.execute("SET statement_timeout = 0")
|
||||||
|
cursor.execute(f"CREATE INDEX CONCURRENTLY {self.INDEX_NAME} " f"ON {self.TABLE_NAME} (host_id, id DESC)")
|
||||||
|
finally:
|
||||||
|
connection.connection.autocommit = old_autocommit
|
||||||
|
|
||||||
|
self.stdout.write(f"Index {self.INDEX_NAME} created successfully.")
|
||||||
Reference in New Issue
Block a user