"enable_auto_commit=True молча удалил все документы из RAG-базы. Ни одной ошибки в логах"

enable_auto_commit=True молча удалил все документы из RAG-базы. Ни одной ошибки в логах

Один параметр Kafka по умолчанию. Стандартный try/except. И документы — десятки, сотни — молча исчезали из векторной базы. Без алертов. Без ретраев. Навсегда.

Контекст

RAG-пайплайн: Spring Boot принимает файл → MinIO хранит → Kafka доставляет событие → Python-воркер делает эмбеддинги → Qdrant индексирует. Стандартная архитектура, тысячи таких в production.

При стресс-тесте обнаружились два бага. Первый — шумный и очевидный. Второй — тихий и разрушительный.

Баг 1: 10 МБ превратились в 62 МБ

Наивный `text.split(" ")` на бинарном файле без пробелов создал один чанк на весь файл. При JSON-сериализации каждый нулевой байт раздувается в `\u0000` — шесть символов. Множитель 6x. Qdrant отклонил запрос (лимит 32 МБ).

Фикс простой: `RecursiveCharacterTextSplitter` из LangChain вместо наивного сплита.

Баг 2: тихое уничтожение данных

А вот что произошло дальше — гораздо страшнее.

Kafka-консьюмер работал с `enable_auto_commit=True` (это дефолт). Это значит: Kafka сдвигает offset по таймеру, **не проверяя, успешно ли обработано сообщение**.

Цепочка событий при каждом сбое:

1. Kafka доставляет сообщение 2. Воркер пытается записать в Qdrant — ошибка 3. `except` ловит исключение, пишет в лог 4. Auto-commit тикает — offset сдвинут 5. Kafka считает документ обработанным 6. Повторной доставки не будет

Документ лежит в MinIO. Но в Qdrant его нет. Поиск его не найдёт. И ни одна система не знает, что произошла потеря.

Почему это так опасно

Потому что всё *выглядит* рабочим. Логи чистые (ошибка была, но кто сканирует тысячи строк?). Мониторинг молчит. Пользователи загружают файлы — они видят их в хранилище. Но поисковый индекс молча расходится с реальностью.

Это не crash. Это коррозия данных.

Решение: Dead Letter Queue + ручной commit

Два изменения:

**Первое** — отключить auto-commit и коммитить offset только после успешной обработки:

```python consumer = KafkaConsumer( enable_auto_commit=False ) for message in consumer: try: process(message) consumer.commit() except Exception: send_to_dlq(message) ```

**Второе** — Dead Letter Queue. При любой ошибке событие уходит в отдельный топик с полным контекстом: оригинальное событие, стектрейс, correlation ID. Когда инфраструктура восстановится — DLQ реплеится.

Хаос-тест как доказательство

Разработчик загрузил 40 МБ файл и в процессе генерации эмбеддингов убил Qdrant (`docker stop`). Python получил `Connection refused`. DLQ поймал событие. Данные выжили.

Без DLQ — документ был бы потерян навсегда.

Чек-лист: что проверить в своём RAG-пайплайне

- `enable_auto_commit` = `False`? Если `True` или не задан — у вас проблема - Есть ли DLQ для каждого консьюмера? - Есть ли мониторинг расхождения между хранилищем и индексом? - Пините ли вы версии Docker-образов? (`:latest` в этом же проекте убил MinIO-бакет) - Проводили ли хаос-тест — убийство базы во время записи?

Мораль

Дефолты фреймворков оптимизированы для туториалов, а не для production. `enable_auto_commit=True` идеален для демо: меньше кода, меньше сложности. Но в системе, где данные имеют ценность, он превращает каждый сбой в безвозвратную потерю.

Самый опасный пайплайн — тот, который молчит, когда теряет данные.

У вас в production Kafka-консьюмеры? Grep по `auto_commit`. Проверьте прямо сейчас. Что нашли?

Начать дискуссию