feat(admin): честная длительность прогона + добор эмбеддингов

Разбор итогов массовой заливки показал в отладке «среднюю длительность прогона»
в 2.8 часа там, где парсинг занимал 50 секунд: started_at пишется в момент
постановки в очередь, а очередь из 173 источников разбирается часами. Теперь
момент реального старта пишется отдельно (run_started_at, миграция 006), а
схема отдаёт обе величины — сколько ждал очереди и сколько работал.

Плюс scripts/ops/reembed_missing.py: документ попадает в корпус сразу, а вектор
для L3 считает отдельная задача; когда worker-gpu или Ollama недоступны, эти
задачи теряются и документ остаётся невидимым для семантического поиска. Скрипт
находит faiss_id IS NULL и переотправляет пачками (dry-run по умолчанию) —
сейчас таких 23 101 из 177 147.

Документация: актуальные цифры корпуса, дубли при повторном прогоне, лимит
OpenAlex, и главное — гипервизор .254 зафиксирован в DR-HA как самая широкая
единая точка отказа (брокер, эмбеддинги, секреты и прокси на одном железе;
подтверждено аварией 28.08).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-08-28 16:51:25 +05:00
parent f1a8d07cb3
commit 99bf14fe6a
10 changed files with 255 additions and 9 deletions

View File

@@ -81,7 +81,10 @@
`last_run_id` — ссылка на последний прогон.
- **parse_runs** — прогоны заливки: стадия, счётчики (`target/fetched/processed/
added/duplicates/skipped/failed`), `cancel_requested`, `heartbeat_at`, журнал
событий (JSON). Из них админка рисует шкалу загрузки, см. §12.
событий (JSON). Тайминги раздельные: `started_at` — постановка в очередь,
`run_started_at` — реальный старт работы воркером (при массовом запуске между
ними часы ожидания), `finished_at` — конец. Из них админка рисует шкалу
загрузки, см. §12.
- **staged_works** — пользовательские загрузки на модерацию перед добавлением в корпус.
- **admin_sessions** — одноразовые коды входа в админку.
@@ -226,6 +229,12 @@ Identity (Universal Auth) и генерирует `.env` заново (`infisica
`VECTOR_BACKEND`).
- **Бюджет времени прогона** (`PARSER_TIME_BUDGET_S`, по умолчанию 1500с) —
защита от краш-лупа по `consumer_timeout` RabbitMQ, см. [DR-HA.md](DR-HA.md) §6.
- **Добор эмбеддингов** — [`scripts/ops/reembed_missing.py`](../scripts/ops/reembed_missing.py):
документ попадает в корпус сразу, а вектор для L3 считает отдельная задача
`gpu.embed_documents`; если worker-gpu или Ollama были недоступны, эти задачи
теряются и документ остаётся невидимым для семантического поиска. Скрипт
находит `faiss_id IS NULL` и переотправляет задачи пачками (dry-run по
умолчанию). Покрытие видно в панели отладки.
## 13. Безопасность

View File

@@ -55,6 +55,17 @@ Redis у нас — кэш/rate-limits/LSH-индекс (префикс `antipla
| Векторный индекс | было | `VECTOR_BACKEND=qdrant` снимает (см. README) ✅ |
| RabbitMQ .82 | да | мониторинг ловит падение ✅; кластер — по потребности |
| app/ES 1.32 | да | воркеры горизонтальны; ES single-node (для BM25 не критично) |
| Хост Proxmox .254 | **да, широкий** | митигации нет — см. ниже |
**Гипервизор — самая широкая единая точка отказа.** На хосте `192.168.20.254`
одновременно живут RabbitMQ (.82), embedding-gpu (.109), Infisical (.111) и
CT 102 (.253) — фронтенд и реверс-прокси. Его падение снимает сразу: приём и
обработку задач (нет брокера), эмбеддинги (нет Ollama), деплой (нет Infisical и
Gitea) и весь публичный доступ (нет прокси) — при том что API, PostgreSQL и
MinIO продолжают работать. Проверено на практике 2026-08-28: хост перестал
отвечать даже на ARP, всё перечисленное отвалилось разом, данные не пострадали.
Разнести хотя бы прокси/брокер по разным физическим хостам — самая дешёвая
мера; пока её нет, восстановление требует физического доступа к железу.
## 6. Известный операционный риск — RabbitMQ `consumer_timeout` vs долгие таски

View File

@@ -1,13 +1,18 @@
# Наполнение корпуса — runbook
## Текущее состояние (на 2026-08-27)
## Текущее состояние (на 2026-08-28)
- **165 480 документов**, русский теперь большинство: `ru` 97 507, `en` 67 646,
- **177 147 документов**, русский большинство: `ru` 99 884, `en` 76 936,
остальные языки — единицы/десятки. Проблема «не с чем сравнивать русские
работы» из более ранней версии этого документа закрыта.
- По источникам: CyberLeninka 97 506, OpenAlex 49 218, PMC 10 451, arXiv 8 304,
- По источникам: CyberLeninka 99 883, OpenAlex 49 276, PMC 14 910, arXiv 13 077,
`user_submission` (проверенные пользователями работы, не источник для сравнения
сами с собой — см. ARCHITECTURE.md §7) 1.
- Повторный прогон по уже залитым источникам даёт почти одни дубли (типично
1490 из 1500 на источник): прирост дают только новые публикации. Реальный
рост корпуса — поднятый `limit` или новые темы, а не повторный запуск.
- OpenAlex при массовом запуске упирается в лимит вежливого пула (10 req/s
на mailto, общий для всех воркеров) — пауза между страницами поднята до 1с.
- Добавлен 4-й парсер — **PMC** (PubMed Central, `scripts/parsers/pmc.py`),
англоязычные научные статьи открытого доступа.
- Массовое расширение по дисциплинам теперь двумя сидерами: `seed_ru_sources.py`
@@ -61,6 +66,11 @@ python scripts/seed_ru_sources.py --apply --limit 500
- **Страница «Отладка»** — очереди, воркеры, покрытие эмбеддингами, зависшие и
упавшие прогоны (см. ARCHITECTURE.md §12).
После большой заливки стоит свериться с покрытием L3 (панель отладки, строка
«без вектора»): эмбеддинги считаются отдельной задачей на worker-gpu, и если он
или Ollama были недоступны, документы останутся без вектора. Догнать —
`scripts/ops/reembed_missing.py` (dry-run по умолчанию, `--apply` отправляет).
Прогон со статусом `partial` — это не ошибка: сработал бюджет времени
(`PARSER_TIME_BUDGET_S`, 1500с), заливка остановилась раньше `consumer_timeout`
RabbitMQ. Остаток добирается повторным запуском источника.
@@ -82,6 +92,6 @@ SELECT source, count(*) FROM documents WHERE source='cyberleninka'; -- > 0
заголовки с меткой ru).
- Для миллионов — **bulk** (снапшот OpenAlex на S3), а не постраничный API.
- На масштабе обязателен `VECTOR_BACKEND=qdrant` (FAISS flat не тянет), а таблица
`fingerprints` (уже ~88M строк на 165K доков — партиционирование стоит планировать
`fingerprints` (уже ~113M строк на 177K доков — партиционирование стоит планировать
заранее, не постфактум) потребует партиционирования. См.
[ARCHITECTURE.md](ARCHITECTURE.md) и [DR-HA.md](DR-HA.md).