|
61 | 61 | logger = get_task_logger(__name__) |
62 | 62 |
|
63 | 63 |
|
| 64 | +def _cleanup_orphan_scheduled_scans( |
| 65 | + tenant_id: str, |
| 66 | + provider_id: str, |
| 67 | + scheduler_task_id: int, |
| 68 | +) -> int: |
| 69 | + """ |
| 70 | + TEMPORARY WORKAROUND: Clean up orphan AVAILABLE scans. |
| 71 | +
|
| 72 | + Detects and removes AVAILABLE scans that were never used due to an |
| 73 | + issue during the first scheduled scan setup. |
| 74 | +
|
| 75 | + An AVAILABLE scan is considered orphan if there's also a SCHEDULED scan for |
| 76 | + the same provider with the same scheduler_task_id. This situation indicates |
| 77 | + that the first scan execution didn't find the AVAILABLE scan (because it |
| 78 | + wasn't committed yet, probably) and created a new one, leaving the AVAILABLE orphaned. |
| 79 | +
|
| 80 | + Args: |
| 81 | + tenant_id: The tenant ID. |
| 82 | + provider_id: The provider ID. |
| 83 | + scheduler_task_id: The PeriodicTask ID that triggers these scans. |
| 84 | +
|
| 85 | + Returns: |
| 86 | + Number of orphan scans deleted (0 if none found). |
| 87 | + """ |
| 88 | + orphan_available_scans = Scan.objects.filter( |
| 89 | + tenant_id=tenant_id, |
| 90 | + provider_id=provider_id, |
| 91 | + trigger=Scan.TriggerChoices.SCHEDULED, |
| 92 | + state=StateChoices.AVAILABLE, |
| 93 | + scheduler_task_id=scheduler_task_id, |
| 94 | + ) |
| 95 | + |
| 96 | + scheduled_scan_exists = Scan.objects.filter( |
| 97 | + tenant_id=tenant_id, |
| 98 | + provider_id=provider_id, |
| 99 | + trigger=Scan.TriggerChoices.SCHEDULED, |
| 100 | + state=StateChoices.SCHEDULED, |
| 101 | + scheduler_task_id=scheduler_task_id, |
| 102 | + ).exists() |
| 103 | + |
| 104 | + if scheduled_scan_exists and orphan_available_scans.exists(): |
| 105 | + orphan_count = orphan_available_scans.count() |
| 106 | + logger.warning( |
| 107 | + f"[WORKAROUND] Found {orphan_count} orphan AVAILABLE scan(s) for " |
| 108 | + f"provider {provider_id} alongside a SCHEDULED scan. Cleaning up orphans..." |
| 109 | + ) |
| 110 | + orphan_available_scans.delete() |
| 111 | + return orphan_count |
| 112 | + |
| 113 | + return 0 |
| 114 | + |
| 115 | + |
64 | 116 | def _perform_scan_complete_tasks(tenant_id: str, scan_id: str, provider_id: str): |
65 | 117 | """ |
66 | 118 | Helper function to perform tasks after a scan is completed. |
@@ -247,6 +299,14 @@ def perform_scheduled_scan_task(self, tenant_id: str, provider_id: str): |
247 | 299 | return serializer.data |
248 | 300 |
|
249 | 301 | next_scan_datetime = get_next_execution_datetime(task_id, provider_id) |
| 302 | + |
| 303 | + # TEMPORARY WORKAROUND: Clean up orphan scans from transaction isolation issue |
| 304 | + _cleanup_orphan_scheduled_scans( |
| 305 | + tenant_id=tenant_id, |
| 306 | + provider_id=provider_id, |
| 307 | + scheduler_task_id=periodic_task_instance.id, |
| 308 | + ) |
| 309 | + |
250 | 310 | scan_instance, _ = Scan.objects.get_or_create( |
251 | 311 | tenant_id=tenant_id, |
252 | 312 | provider_id=provider_id, |
|
0 commit comments