fix watchdog
This commit is contained in:
@@ -11,6 +11,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from concurrent.futures import TimeoutError as FuturesTimeoutError
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from threading import Event, Thread
|
||||
from typing import Any, Callable
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
from urllib.request import Request, urlopen
|
||||
@@ -97,6 +98,33 @@ class _PageOpWatchdog:
|
||||
return False
|
||||
|
||||
|
||||
class _ProgressHeartbeat:
|
||||
"""Периодически пульсует progress во время долгой навигации."""
|
||||
|
||||
def __init__(self, report_progress: Callable[[str, Any], None], stage: str, interval_s: float = 15.0, **meta: Any) -> None:
|
||||
self._report_progress = report_progress
|
||||
self._stage = stage
|
||||
self._interval_s = max(5.0, float(interval_s))
|
||||
self._meta = meta
|
||||
self._stop = Event()
|
||||
self._thread: Thread | None = None
|
||||
|
||||
def _run(self) -> None:
|
||||
while not self._stop.wait(self._interval_s):
|
||||
self._report_progress(self._stage, **self._meta)
|
||||
|
||||
def __enter__(self):
|
||||
self._thread = Thread(target=self._run, name=f"progress-heartbeat-{self._stage}", daemon=True)
|
||||
self._thread.start()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
self._stop.set()
|
||||
if self._thread is not None:
|
||||
self._thread.join(timeout=1.0)
|
||||
return False
|
||||
|
||||
|
||||
class IAAIScraper:
|
||||
|
||||
@staticmethod
|
||||
@@ -453,16 +481,23 @@ class IAAIScraper:
|
||||
)
|
||||
|
||||
for expected_page in range(2, target_page_number + 1):
|
||||
# Heartbeat для watchdog: при deep-resume (100+ кликов) задача может
|
||||
# идти несколько минут без batch_upserted, поэтому пульсуем прогресс.
|
||||
if expected_page == 2 or expected_page == target_page_number or expected_page % 5 == 0:
|
||||
self._report_progress(
|
||||
"listing_resume_progress",
|
||||
current_page=expected_page - 1,
|
||||
target_page=target_page_number,
|
||||
)
|
||||
self._report_progress(
|
||||
"listing_resume_progress",
|
||||
current_page=expected_page - 1,
|
||||
target_page=target_page_number,
|
||||
next_page_number=expected_page,
|
||||
)
|
||||
|
||||
if not self.listing_collector.go_to_next_page(page, expected_page_number=expected_page):
|
||||
with _ProgressHeartbeat(
|
||||
self._report_progress,
|
||||
"listing_resume_progress",
|
||||
current_page=expected_page - 1,
|
||||
target_page=target_page_number,
|
||||
next_page_number=expected_page,
|
||||
):
|
||||
next_ok = self.listing_collector.go_to_next_page(page, expected_page_number=expected_page)
|
||||
|
||||
if not next_ok:
|
||||
self._report_progress(
|
||||
"listing_resume_failed",
|
||||
current_page=expected_page - 1,
|
||||
@@ -471,6 +506,13 @@ class IAAIScraper:
|
||||
page.close()
|
||||
raise ListingResumeError(f"Failed to resume listing at page {target_page_number}")
|
||||
|
||||
self._report_progress(
|
||||
"listing_resume_progress",
|
||||
current_page=expected_page,
|
||||
target_page=target_page_number,
|
||||
next_page_number=min(target_page_number, expected_page + 1),
|
||||
)
|
||||
|
||||
self._report_progress(
|
||||
"listing_resume_completed",
|
||||
current_page=target_page_number,
|
||||
@@ -973,8 +1015,17 @@ class IAAIScraper:
|
||||
cars_failed=cars_failed,
|
||||
)
|
||||
try:
|
||||
with _PageOpWatchdog(_PAGE_NEXT_TIMEOUT_S, f"next page {page_number + 1}"):
|
||||
next_ok = self.listing_collector.go_to_next_page(page, expected_page_number=page_number + 1)
|
||||
with _ProgressHeartbeat(
|
||||
self._report_progress,
|
||||
"listing_next_page_started",
|
||||
page_number=page_number,
|
||||
next_page_number=page_number + 1,
|
||||
pending_urls=len(pending_urls),
|
||||
cars_upserted=cars_upserted,
|
||||
cars_failed=cars_failed,
|
||||
):
|
||||
with _PageOpWatchdog(_PAGE_NEXT_TIMEOUT_S, f"next page {page_number + 1}"):
|
||||
next_ok = self.listing_collector.go_to_next_page(page, expected_page_number=page_number + 1)
|
||||
except (PageOperationTimeoutError, PlaywrightError) as nav_exc:
|
||||
logger.warning(
|
||||
"go_to_next_page stalled/failed at page %d (%s) — will reopen",
|
||||
|
||||
Reference in New Issue
Block a user