|
1 | 1 | import logging |
| 2 | +import time |
2 | 3 | from collections import defaultdict |
3 | 4 | from typing import Any, Dict, List, Tuple |
4 | 5 |
|
|
9 | 10 | AIRFLOW_DAGRUN_TIMEOUT, |
10 | 11 | AIRFLOW_DEBUG_MODE, |
11 | 12 | EXECUTE_VALUES_PAGE_SIZE, |
| 13 | + TEMP_TABLE_CLEANUP_DELAY, |
12 | 14 | ) |
13 | 15 | from libs.utils import update_job_status |
14 | 16 | from openpyxl.worksheet.worksheet import Worksheet |
@@ -238,3 +240,91 @@ def delete_temp_tables(scan_report_id: int, table_pairs: List[Tuple[str, int]]) |
238 | 240 | except Exception as e: |
239 | 241 | logging.error(f"Error deleting temporary tables: {str(e)}") |
240 | 242 | raise e |
| 243 | + |
| 244 | + |
| 245 | +def cleanup_temp_tables_for_scan_report(scan_report_id: int) -> List[Tuple[str, int]]: |
| 246 | + """ |
| 247 | + Clean up temporary tables for a scan report. |
| 248 | +
|
| 249 | + Deletes temporary tables (temp_data_dictionary and temp_field_values) for all tables |
| 250 | + associated with the given scan_report_id in mapping_scanreporttable. |
| 251 | +
|
| 252 | + Returns: |
| 253 | + List of (table_name, table_id) tuples for the tables that were cleaned up. |
| 254 | + """ |
| 255 | + |
| 256 | + query = """ |
| 257 | + SELECT name, id |
| 258 | + FROM mapping_scanreporttable |
| 259 | + WHERE scan_report_id = %(scan_report_id)s |
| 260 | + """ |
| 261 | + records = pg_hook.get_records(query, parameters={"scan_report_id": scan_report_id}) |
| 262 | + table_pairs = [(record[0], record[1]) for record in records] if records else [] |
| 263 | + if table_pairs: |
| 264 | + delete_temp_tables(scan_report_id, table_pairs) |
| 265 | + return table_pairs |
| 266 | + |
| 267 | + |
| 268 | +def handle_failure_and_cleanup_temp_tables(context): |
| 269 | + """ |
| 270 | + Delete temporary tables when the DAG fails or times out. |
| 271 | +
|
| 272 | + This handles cleanup when failures happen outside the normal pipeline code, like |
| 273 | + timeouts or external errors. Since the tables are already in the database even if |
| 274 | + the DAG fails, we query mapping_scanreporttable to find all tables for this |
| 275 | + scan_report_id, then delete the temp tables (temp_data_dictionary and |
| 276 | + temp_field_values). |
| 277 | +
|
| 278 | + When a timeout occurs, the task that was running may still be creating temporary tables in the background. |
| 279 | + This function waits TEMP_TABLE_CLEANUP_DELAY seconds first so any in-flight table creation can finish, |
| 280 | + then runs a single cleanup pass. Cleanup is not urgent, so waiting once is simpler than cleaning twice. |
| 281 | +
|
| 282 | +
|
| 283 | + Args: |
| 284 | + context: Airflow execution context containing task_instance, dag, dag_run, etc. |
| 285 | + """ |
| 286 | + try: |
| 287 | + dag_run = context["dag_run"] |
| 288 | + dag = context.get("dag") |
| 289 | + dag_run_conf = dag_run.conf or {} |
| 290 | + scan_report_id = dag_run_conf.get("scan_report_id") |
| 291 | + |
| 292 | + if not scan_report_id: |
| 293 | + logging.warning( |
| 294 | + "No scan_report_id found in DAG run configuration, skipping temp table cleanup" |
| 295 | + ) |
| 296 | + return |
| 297 | + |
| 298 | + # Update job status to FAILED for scan_report_processing DAG |
| 299 | + if dag and dag.dag_id == "scan_report_processing": |
| 300 | + try: |
| 301 | + update_job_status( |
| 302 | + stage=JobStageType.UPLOAD_SCAN_REPORT, |
| 303 | + status=StageStatusType.FAILED, |
| 304 | + scan_report=scan_report_id, |
| 305 | + details="Scan report processing DAG timed out or failed.", |
| 306 | + ) |
| 307 | + logging.info( |
| 308 | + "Updated job status to FAILED for scan_report_id=%s", |
| 309 | + scan_report_id, |
| 310 | + ) |
| 311 | + except Exception as e: |
| 312 | + logging.error("Failed to update job status on failure: %s", str(e)) |
| 313 | + |
| 314 | + # Wait so a timed-out task can finish creating tables, then delete the temporary tables |
| 315 | + delay = TEMP_TABLE_CLEANUP_DELAY |
| 316 | + time.sleep(delay) |
| 317 | + |
| 318 | + table_pairs = cleanup_temp_tables_for_scan_report(scan_report_id) |
| 319 | + if table_pairs: |
| 320 | + logging.info( |
| 321 | + "Deleted temp tables for scan_report_id=%s (n=%d)", |
| 322 | + scan_report_id, |
| 323 | + len(table_pairs), |
| 324 | + ) |
| 325 | + logging.info( |
| 326 | + "Completed temp table cleanup for scan_report_id=%s", scan_report_id |
| 327 | + ) |
| 328 | + |
| 329 | + except Exception as e: |
| 330 | + logging.error("Failed to delete temporary tables on failure: %s", str(e)) |
0 commit comments