3.1 KiB
3.1 KiB
ADR-003: Использование PostgreSQL FTS и pg_trgm вместо Meilisearch/Elasticsearch
Дата: 2026-09-06
Статус: Принято
Контекст
Для поиска модов по названию, описанию и авторам требуется полнотекстовый поиск и устойчивость к опечаткам (fuzzy search). Введение отдельного движка поиска (Meilisearch, OpenSearch, Elasticsearch) усложняет инфраструктуру и увеличивает потребление RAM (ещё один сервис в docker-compose, отдельный индекс, синхронизация).
На MVP ожидается <50k модов, поисковый трафик <100 RPS, VPS за $5–10.
Решение
Использовать возможности PostgreSQL 16:
tsvector+GIN-индексы для ранжированного полнотекстового поиска (to_tsvector,ts_rank,plainto_tsquery).- Расширение
pg_trgmдля поиска с опечатками (Trigram Similarity, оператор%,similarity()). - Материализованный
search_vectorс триггером наINSERT/UPDATE(см.docs/database-schema.md).
Запрос: search_vector @@ plainto_tsquery + фильтр по mod_versions (GIN (game_versions, loaders)) + ORDER BY ts_rank DESC + fallback similarity() при 0 результатах.
Последствия
- Плюсы: Нет дополнительных сервисов в
docker-compose, экономия памяти (~0 доп. RAM vs +500MB–1GB у Meilisearch), атомарные транзакции при обновлении индекса (нет лагосинка), проще бэкапы. - Минусы: При объёме >500k записей или >500 RPS скорость FTS в Postgres начинает уступать специализированным движкам (latency >100ms, нет typo-tolerance из коробки как в Meilisearch).
- План миграции: Если p95 latency поиска превысит 100ms (метрика в
tracing+ Grafana), вынести индекс в Meilisearch: добавить воркер-синкmods→ Meilisearch, переключитьGET /modsна Meilisearch с fallback на Postgres. Схема БД не меняется.
Альтернативы (отклонены на MVP)
- Meilisearch - отличный typo-tolerance, но +1 сервис, нужен отдельный деплой и синк.
- Elasticsearch / OpenSearch - оверхед по RAM/диску, нужен кластер даже для малого объёма.
- SQLite FTS - не подходит, уже Postgres как основной.
Ссылки
docs/database-schema.md- секция 2 (триггерmods_search_vector_update), секция 3 (примеры FTS + fuzzy).docs/architecture.md- секция 4 (Поиск без Elasticsearch).