Эфемерный слой ускорения частых чтений: что класть в кэш, на чём строить
ключ, когда содержимое протухает. Кэш — быстрый производный,
пересоберётся при потере (водораздел Redis ⟂ Postgres).
Политика кэша
Единственный потребитель сейчас — кандидаты поиска
Query Engine
(exact-match по запросу). Кэшируем дорогой retrieval-слой
(старт ~10 мин), а не финальный ответ модели — он персонализирован и
фильтруется по правам, общего значения для кэша не имеет.
Кэшируем
retrieval-кандидаты — общий дорогой слой
Не кэшируем
финальный ответ LLM — персонализация · ACL
-
TTL-only. Истечение по времени, без событийной инвалидации —
устаревшие кандидаты дёшевы, цена ошибки низкая. Точный TTL — тюнинг.
-
ACL на кэше не держится. Права досверяет late-binding trim на
стороне retrieval, уже после выдачи кандидатов — поэтому строгая
ACL-инвалидация кэшу не нужна, корректность доступа от него не зависит.
-
Дом —
redis-cache (вытесняемый, политика LRU): при
нехватке памяти старые ключи уходят, кэш восстановится темпом
(два инстанса).
-
Значение несёт тела чанков, не только id + score. Попадание
возвращает кандидатов целиком, минуя выборку строк из Postgres — проще и
быстрее, пока рабочий набор скромен. Тощая альтернатива (кэшировать
только id + score, тела дочитывать из Postgres на попадании) вмещает на
порядок больше запросов в ту же память
(память кэша) —
рычаг v2, когда рост числа пользователей или
давление на память Redis начнут бить по hit-rate.
-
Single-flight-замка нет — пер-юзерный ключ делает его излишним.
Запись кэша ключуется запрос + личность,
поэтому «горячий ключ» — это точный запрос одного пользователя: два
конкурентных промаха по нему требовали бы, чтобы один и тот же
пользователь выстрелил идентичным запросом в одно мгновение. Общего
между пользователями ключа нет — нет и стада промахов: промах просто
считает и пишет, без rebuild-замка. Общий межпользовательский слой
выборки под single-flight-замком
(
lock:)
— рычаг v2, если такой слой появится.
Состав ключа
Кэш-гейт стоит на входе retrieval, до выбора модели и
промпта — на состав кандидатов они не влияют и в ключ не
входят. Результат определяют только сам запрос и identity; устаревшие
кандидаты протухают по TTL, отдельной инвалидации не требуется
(кэш-гейт в RAG-конвейере).
самостоятельный запрос
+
identity
Запрос
самостоятельный запрос + identity
→
Ключ
сборка из сегментов
→
hit
готовые кандидаты из redis-cache
miss
дорогой retrieval → запись с TTL