perf(ops): начинать листинг PMC после последней залитой статьи
Первый прогон нового конвейера показал слабое место: листинг бакета шёл с начала, и заливка часами перемалывала уже существующие статьи как дубли — из 300 полученных 300 оказались дублями. S3 умеет start-after, и стартовый ключ выводится прямо из базы: ext_id вида `pmc:PMC10000000` соответствует ключу `PMC10000000.1`, бакет отдаётся лексикографически. Теперь при отсутствии сохранённого токена (первый прогон после ручных заливок) листинг начинается после максимального залитого. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -532,6 +532,24 @@ def _stage_work(
|
||||
logger.warning(f"Не удалось добавить работу {task_id!r} в отстойник: {e}")
|
||||
|
||||
|
||||
def _last_pmc_key() -> str | None:
|
||||
"""Ключ бакета после последней залитой статьи PMC — старт листинга.
|
||||
|
||||
Бакет отдаётся в лексикографическом порядке ключей, и ext_id вида
|
||||
`pmc:PMC10000000` соответствует ключу `PMC10000000.1`. Берём максимальный
|
||||
залитый и продолжаем после него.
|
||||
"""
|
||||
from sqlalchemy import text as sql_text
|
||||
|
||||
with db_session() as session:
|
||||
row = session.execute(sql_text(
|
||||
"SELECT max(ext_id) FROM documents WHERE ext_id LIKE 'pmc:PMC%'"
|
||||
)).first()
|
||||
if not row or not row[0]:
|
||||
return None
|
||||
return row[0].split(":", 1)[1] + ".1"
|
||||
|
||||
|
||||
def _parser_for(source_type: str, cfg: dict[str, Any]) -> tuple[Any, dict[str, Any]]:
|
||||
"""Инстанс парсера и совместимые с его fetch() аргументы по типу источника."""
|
||||
limit = cfg["limit"]
|
||||
@@ -571,7 +589,14 @@ def _parser_for(source_type: str, cfg: dict[str, Any]) -> tuple[Any, dict[str, A
|
||||
}
|
||||
elif source_type == "pmc_bulk":
|
||||
from pmc_bulk import PMCBulkParser as P
|
||||
kwargs = {"limit": limit, "resume_token": cfg.get("resume_token")}
|
||||
kwargs = {
|
||||
"limit": limit,
|
||||
"resume_token": cfg.get("resume_token"),
|
||||
# Токена ещё нет (первый прогон после ручных заливок) — начинаем
|
||||
# после последней уже залитой статьи, иначе листинг часами
|
||||
# перемалывает существующие как дубли
|
||||
"start_after": None if cfg.get("resume_token") else _last_pmc_key(),
|
||||
}
|
||||
else:
|
||||
raise ValueError(f"неизвестный тип источника: {source_type}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user