refactor(ops): массовая заливка — в общий конвейер вместо отдельных скриптов
All checks were successful
Deploy / deploy (push) Successful in 2m28s
Deploy / test (push) Successful in 2m49s

Заливку Википедии и PMC я сделал отдельными скриптами мимо существующей
инфраструктуры: запуск руками через ssh, состояние в файле в /tmp, никакой
видимости. Результат предсказуем — за неделю обе умерли молча (обрыв базы на
20 008 статьях из 2 млн и таймаут сети на 85 тыс. из 100 тыс.), прогресс
потерялся, а узнали мы об этом через неделю. При том что рядом лежит готовый
механизм: parse_sources, прогоны со шкалой, журнал, кнопки, ретраи Celery.

Теперь это обычные типы источника — wikipedia_ru и pmc_bulk:

- заводятся и запускаются из админки, как OpenAlex или КиберЛенинка;
- показывают ту же шкалу, журнал и кнопку остановки;
- падение воркера больше не теряет прогресс: позиция продолжения хранится в
  parse_sources.resume_token (номер статьи в дампе / токен страницы бакета),
  повторный запуск берёт следующую порцию;
- укладываются в бюджет времени таска — заливка идёт порциями, а не одним
  многосуточным процессом.

Чего не хватало конвейеру для миллионов и что добавлено:
- парсеры отдают генератор, а не список: 2 млн статей в память не влезают;
- app/bulk_writer.py — запись пачками через COPY (21 тыс. строк/с против
  6.7 тыс. построчно) с переподключением к базе при обрыве;
- эмбеддинги при массовой заливке не диспатчатся: они на порядок медленнее и
  стали бы узким местом, вектора досчитываются отдельно (reembed_missing.py).

scripts/ops/bulk_ingest_*.py удалены — их работу делает конвейер.

Проверено на проде: оба парсера отдают документы, прогон через run_parser
завершается штатно, позиция продолжения сдвигается (300 → 600), повторный
запуск продолжает с неё.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-09-05 20:32:53 +05:00
parent 1b1ca3b7e3
commit 26de1b9de1
15 changed files with 732 additions and 584 deletions

View File

@@ -78,7 +78,9 @@
- **fingerprints** — `doc_id`, `hash_value` (BIGINT, Winnowing), `position` — для L1.
- **usage_logs** — `user_id`, `action` — учёт лимитов по тарифу.
- **parse_sources** — задания парсеров (админка): тип, query, годы, лимит, статус,
`last_run_id` — ссылка на последний прогон.
`last_run_id` — ссылка на последний прогон, `resume_token` — позиция
продолжения для массовых источников (номер статьи в дампе, токен страницы
бакета).
- **parse_runs** — прогоны заливки: стадия, счётчики (`target/fetched/processed/
added/duplicates/skipped/failed`), `cancel_requested`, `heartbeat_at`, журнал
событий (JSON). Тайминги раздельные: `started_at` — постановка в очередь,
@@ -119,10 +121,20 @@
`app.bibliography.build_bibliography` (сортировка кириллица→латиница, нумерация,
формат 7.1/7.0.5) → результат.
**Наполнение корпуса.** админка/CLI → `index.run_parser` (OpenAlex/arXiv/PMC/
КиберЛенинка, фильтр `is_oa`) → `index.add_document` (дедуп по `ext_id`,
fingerprints, MinHash, эмбеддинг) → `index.enrich_full_text` (скачать OA-PDF →
MinIO → переиндексация). Ход заливки пишется в `parse_runs` (§12).
**Наполнение корпуса.** админка → `index.run_parser` — один конвейер для всех
источников, со шкалой, журналом и кнопкой остановки. Внутри два пути записи:
- *обычные источники* (OpenAlex/arXiv/PMC/КиберЛенинка, фильтр `is_oa`) —
`index.add_document` на каждый документ: дедуп по `ext_id`, fingerprints,
MinHash, Elasticsearch, эмбеддинг, затем `index.enrich_full_text` (скачать
OA-PDF → MinIO → переиндексация);
- *массовые источники* (`wikipedia_ru`, `pmc_bulk`) — парсер отдаёт генератор,
запись идёт пачками через `COPY` (`app/bulk_writer.py`), позиция продолжения
хранится в `parse_sources.resume_token`. Эмбеддинги там не считаются: они
медленнее заливки на порядок и стали бы её узким местом, вектора
досчитываются отдельно (§12).
Ход заливки в обоих случаях пишется в `parse_runs` (§12).
Второй путь наполнения — ручная загрузка файлов админом: api сохраняет их в
MinIO (`corpus-upload/`) → `index.ingest_upload` (извлечь текст → `add_document`,
`source=manual_upload`). Это не проверка на плагиат: файл сразу становится