Synchronous implementation of series rule evaluation; returns details for debugging.
(tvg_id: str | None = None)
| 450 | |
| 451 | |
| 452 | def evaluate_series_rules_impl(tvg_id: str | None = None): |
| 453 | """Synchronous implementation of series rule evaluation; returns details for debugging.""" |
| 454 | result = {"scheduled": 0, "details": []} |
| 455 | |
| 456 | # Serialize all invocations to prevent concurrent evaluations from |
| 457 | # racing to create duplicate recordings (e.g. multiple EPG sources |
| 458 | # refreshing simultaneously each firing evaluate_series_rules.delay()). |
| 459 | # If Redis is unavailable, proceed without lock — the primary and |
| 460 | # secondary dedup guards still prevent duplicates. |
| 461 | lock_acquired = False |
| 462 | try: |
| 463 | lock_acquired = acquire_task_lock('evaluate_series_rules', 'all') |
| 464 | if not lock_acquired: |
| 465 | result["details"].append({"status": "skipped", "reason": "concurrent evaluation in progress"}) |
| 466 | return result |
| 467 | except (ConnectionError, OSError, AttributeError): |
| 468 | logger.warning("Could not acquire series rule evaluation lock (Redis unavailable), proceeding without lock") |
| 469 | |
| 470 | try: |
| 471 | return _evaluate_series_rules_locked(tvg_id, result) |
| 472 | finally: |
| 473 | if lock_acquired: |
| 474 | try: |
| 475 | release_task_lock('evaluate_series_rules', 'all') |
| 476 | except (ConnectionError, OSError, AttributeError): |
| 477 | logger.warning("Could not release series rule evaluation lock") |
| 478 | |
| 479 | |
| 480 | def _evaluate_series_rules_locked(tvg_id, result): |