ADR-0004 · Кэш схемы зала#
Статус: принят · 2026-09-16
Метод: D-03 v1.0 · шаблон ADR v1.1
Участники: команда разработки (4 человека)
Учебная запись: часть прогона E-01, система вымышлена. Форма и рассуждение настоящие.
Короткая запись#
Решение: локальный кэш в процессе, TTL 1 с, без инвалидации,
с защитой от лавины (single-flight на ключ)
Основание: пик 8 000 чтений/с против 300 записей/с, один горячий ключ
на событие, допустимая устарелость 5 с [расчёт W1]
Отклонения: умолчание («cache-aside + удаление ключа при записи»)
отвергнуто: 300 записей/с обнуляют ключ быстрее, чем он
успевает использоваться
Вернуться: hit ratio ниже 95 % на горячем ключе, доля устаревших
ответов выше допустимого, p99 чтения выше 200 мс
Метод: D-03 v1.0
Ниже — то, что в пять строк не поместилось и без чего решение выглядит произвольным.
Почему умолчание не подошло#
D-03 предлагает умолчание: cache-aside плюс удаление ключа при записи, короткий TTL как страховка. Для этого кэша оно неработоспособно.
Схема зала — один ключ на событие. В момент старта продаж по нему идёт 8 000 чтений/с и 300 записей в ту же схему [расчёт W1]. Удаление ключа при записи означает, что ключ живёт в среднем 3 мс: он инвалидируется быстрее, чем успевает быть использованным. Hit ratio стремится к нулю, а к нагрузке на БД добавляется лавина одновременных пересчётов.
Правильное отклонение — не инвалидировать вовсе, а положиться на короткий TTL и принять устарелость. Это допустимо, потому что интерфейс и так обязан обрабатывать «место только что заняли» как штатный исход (CASE-08): устаревшая схема не создаёт нового класса ошибок, она лишь делает уже существующий чуть более частым.
Такой строки в таблице отклонений D-03 нет. Её отклонения — про стратегии записи (write-through, write-behind, write-around, refresh-ahead) и про размещение (локальный, CDN). Развилки «инвалидация против короткого TTL», главной для читающего кэша над быстро меняющимися данными, в документе не оказалось.
Почему локальный, а не общий#
Ключ один и горячий — это худший случай для общего кэша (CACHE-04): весь пик приходит на один узел. Локальная копия в каждом экземпляре приложения снимает и сетевой участок, и перекос. Цена — расхождение между экземплярами в пределах TTL, то есть до секунды; при допустимых пяти это в бюджете.
Холодные события обслуживаются тем же механизмом: их читают редко, промах стоит один запрос к БД, отдельный общий кэш ради них заводить незачем.
Дисквалификатор сработал и выдавил наружу отдельное решение#
D-03 отсекает кэш, без которого система не переживает собственного отказа. Проверка: при пустом кэше 8 000 запросов/с уходят в PostgreSQL, который в этот момент обслуживает 300 конкурентных записей с блокировками строк. Не переживёт.
Значит кэш здесь — несущий элемент, и по правилу D-03 его нельзя вводить, пока не спроектирована деградация (REL-04). Форма деградации: при недоступности кэша отдавать схему грубо — «в этом секторе есть места» вместо поимённой занятости, — а точность проверять в момент резерва, где она и так проверяется транзакцией.
Само проектирование деградации — отдельное решение, которого в наборе нет. Оно продуктовое (меняет то, что видит пользователь), не обратимое одним коммитом и принимается на W7. Здесь оно только названо как обязательное условие; пока оно не принято, кэш вводить нельзя.
Что придётся построить#
- Single-flight на пересчёт ключа: без него первое же истечение TTL на горячем событии даст 8 000 одновременных запросов к БД (CACHE-04).
- Прогрев перед стартом продаж. Пики известны по расписанию — это редкая роскошь, и не пользоваться ею странно.
- Метрика доли устаревших ответов: сверка выборкой, иначе устарелость станет неизмеримой.
- Деградация до грубой схемы — по отдельному решению, до ввода кэша в строй.
Условия пересмотра#
Срок не назначается: решение обратимо, его пересматривает метрика.
| Показатель | Вернуться, когда |
|---|---|
| Hit ratio на горячем ключе | Ниже 95 % — TTL слишком короткий или ключ не тот |
| Доля устаревших ответов | Выше согласованного с продуктом порога |
| p99 чтения схемы | Выше 200 мс — кэш не решает задачу |
| Нагрузка на БД при холодном кэше | Показывает, что деградация не работает |
| Расхождение между экземплярами | Стало заметно пользователю — TTL великоват |
Связанные ADR#
Зависит от: ADR-0001 (источник истины), ADR-0002 (путь чтения оставлен внутри монолита и решается кэшем, а не границей). Блокирует: ввод кэша в строй до принятия решения о деградации.