From b4598212e5cb98996db1c483e23adac29375451e Mon Sep 17 00:00:00 2001 From: Oliver Falk Date: Wed, 15 Oct 2025 17:17:38 +0200 Subject: [PATCH] fix: resolve migration transaction issue with CONCURRENTLY - Detect transaction context using connection.in_atomic_block - Use regular CREATE INDEX when in transaction (test environment) - Use CREATE INDEX CONCURRENTLY when not in transaction (production) - Maintains production safety while fixing CI test failures - All 8 indexes now create successfully in both environments Fixes CI error: 'CREATE INDEX CONCURRENTLY cannot run inside a transaction block' --- .../0021_add_performance_indexes.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/ivatar/ivataraccount/migrations/0021_add_performance_indexes.py b/ivatar/ivataraccount/migrations/0021_add_performance_indexes.py index 2e45069..dae5501 100644 --- a/ivatar/ivataraccount/migrations/0021_add_performance_indexes.py +++ b/ivatar/ivataraccount/migrations/0021_add_performance_indexes.py @@ -8,7 +8,7 @@ from django.db import migrations, connection def create_indexes(apps: Any, schema_editor: Any) -> None: """ Create performance indexes for both PostgreSQL and MySQL compatibility. - Uses CONCURRENTLY for PostgreSQL and regular CREATE INDEX for MySQL. + Uses CONCURRENTLY for PostgreSQL production, regular CREATE INDEX for tests/transactions. """ db_engine = connection.vendor @@ -53,14 +53,30 @@ def create_indexes(apps: Any, schema_editor: Any) -> None: ] with connection.cursor() as cursor: + # Check if we're in a transaction (test environment) + try: + cursor.execute("SELECT 1") + in_transaction = connection.in_atomic_block + except Exception: + in_transaction = True + for index_name, table_name, columns, where_clause in indexes: try: if db_engine == "postgresql": - # PostgreSQL with CONCURRENTLY for production safety - if where_clause: - sql = f"CREATE INDEX CONCURRENTLY IF NOT EXISTS {index_name} ON {table_name}({columns}) {where_clause};" + # Use CONCURRENTLY only if not in a transaction (production) + # Use regular CREATE INDEX if in a transaction (tests) + if in_transaction: + # In transaction (test environment) - use regular CREATE INDEX + if where_clause: + sql = f"CREATE INDEX IF NOT EXISTS {index_name} ON {table_name}({columns}) {where_clause};" + else: + sql = f"CREATE INDEX IF NOT EXISTS {index_name} ON {table_name}({columns});" else: - sql = f"CREATE INDEX CONCURRENTLY IF NOT EXISTS {index_name} ON {table_name}({columns});" + # Not in transaction (production) - use CONCURRENTLY + if where_clause: + sql = f"CREATE INDEX CONCURRENTLY IF NOT EXISTS {index_name} ON {table_name}({columns}) {where_clause};" + else: + sql = f"CREATE INDEX CONCURRENTLY IF NOT EXISTS {index_name} ON {table_name}({columns});" else: # MySQL and other databases - skip partial indexes if where_clause: