fix(wikipedia): позиция — байтовое смещение, а не номер статьи
Заливка Википедии останавливалась сама собой: позиция хранилась как номер статьи, и каждый прогон перечитывал дамп с начала. На 20 тысячах это стоило 6 минут из 25 доступных, на 27 тысячах — уже около десяти, а на сотне тысяч съело бы весь бюджет и заливка встала бы совсем. Ровно это и наблюдалось: прогон висел с нулём полученных статей, воркер на 100% CPU. Переход на multistream-вариант дампа: он состоит из независимых bz2-блоков по ~95 статей, к нему прилагается индекс со смещениями. Позиция продолжения стала байтовым смещением, прогон стартует мгновенно с нужного места. Проверено на реальных файлах, а не по предположению: формат индекса (offset:page_id:title), 21 уникальное смещение на 2001 статью, прыжок seek на смещение из середины файла даёт валидный XML со страницами. wikipedia_resume_offset.py — разовый пересчёт позиции при переходе: находит по индексу блок с максимальным залитым page_id (получилось 273 281 821). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
79
scripts/ops/wikipedia_resume_offset.py
Executable file
79
scripts/ops/wikipedia_resume_offset.py
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Вычислить байтовое смещение в дампе Википедии по уже залитым статьям.
|
||||
|
||||
Нужен один раз при переходе на multistream-дамп: позиция продолжения сменила
|
||||
смысл — раньше это был номер статьи (и прогон перечитывал дамп с начала),
|
||||
теперь байтовое смещение блока.
|
||||
|
||||
Как считается: берём максимальный page_id среди залитых статей и находим в
|
||||
индексе дампа блок, которому он принадлежит. Страницы в дампе идут по
|
||||
возрастанию page_id, поэтому всё, что дальше этого блока, ещё не залито.
|
||||
|
||||
Запуск (в контейнере worker-indexer):
|
||||
docker compose -f docker-compose.prod.yml exec -T worker-indexer python - \\
|
||||
< scripts/ops/wikipedia_resume_offset.py # показать
|
||||
... python - --apply < scripts/ops/wikipedia_resume_offset.py # записать
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import bz2
|
||||
import os
|
||||
|
||||
DEFAULT_INDEX = "/parsers/ruwiki-index.txt.bz2"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("--apply", action="store_true", help="записать смещение в источник")
|
||||
ap.add_argument("--index", default=DEFAULT_INDEX, help="путь к индексу дампа")
|
||||
args = ap.parse_args()
|
||||
|
||||
from app.db import db_session
|
||||
from sqlalchemy import text
|
||||
|
||||
if not os.path.exists(args.index):
|
||||
raise SystemExit(f"индекс не найден: {args.index}")
|
||||
|
||||
with db_session() as s:
|
||||
row = s.execute(text("""
|
||||
SELECT max(split_part(ext_id, ':', 2)::bigint)
|
||||
FROM documents WHERE source = 'wikipedia_ru'
|
||||
""")).first()
|
||||
max_pageid = int(row[0]) if row and row[0] else 0
|
||||
print(f"максимальный page_id среди залитых: {max_pageid}")
|
||||
|
||||
if not max_pageid:
|
||||
print("залитых статей нет — начинать с нуля")
|
||||
return
|
||||
|
||||
# Индекс отсортирован по смещению, page_id внутри растут: ищем блок,
|
||||
# в котором лежит наш максимальный, и берём его смещение
|
||||
offset = 0
|
||||
found_title = ""
|
||||
with bz2.open(args.index, "rt", encoding="utf-8") as fh:
|
||||
for line in fh:
|
||||
parts = line.split(":", 2)
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
off, pid = int(parts[0]), int(parts[1])
|
||||
if pid > max_pageid:
|
||||
break
|
||||
offset, found_title = off, parts[2].strip()
|
||||
|
||||
print(f"блок с этой статьёй начинается на байте {offset} ({found_title[:50]!r})")
|
||||
|
||||
if not args.apply:
|
||||
print("\nзапустите с --apply, чтобы записать смещение в источник")
|
||||
return
|
||||
|
||||
with db_session() as s:
|
||||
s.execute(text("""
|
||||
UPDATE parse_sources SET resume_token = :off WHERE source_type = 'wikipedia_ru'
|
||||
"""), {"off": str(offset)})
|
||||
s.commit()
|
||||
print(f"смещение {offset} записано в источник")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user