Короткий ответ
Нужно разделить ответ на части с разной «свежестью» — редко меняющиеся данные кэшировать агрессивно (CDN/HTTP-кэш с TTL или ETag), часто меняющиеся данные (остатки, цены в реальном времени) либо не кэшировать вовсе, либо кэшировать на очень короткий TTL, либо вынести в отдельный некэшируемый эндпоинт/запрос на клиенте.
Как это работает подробнее
HTTP-кэширование (заголовки Cache-Control, ETag, Last-Modified) позволяет CDN или промежуточным прокси отдавать ответ без обращения к backend, резко снижая нагрузку, но вводит окно, в течение которого клиент может увидеть устаревшие данные (пока не истечёт TTL или не придёт инвалидция). Инвалидация кэша по событию (push-инвалидция при изменении данных) даёт более свежие данные, но требует дополнительной инфраструктуры и усложняет систему по сравнению с простым TTL.
Агрессивное кэширование с большим TTL резко снижает нагрузку на backend и упрощает инфраструктуру, но увеличивает окно показа устаревших данных, что может быть неприемлемо для полей типа остатка на складе или цены при высокочастотных изменениях. Короткий TTL/отсутствие кэша для динамических данных даёт свежесть, но требует, чтобы backend выдерживал соответствующую нагрузку напрямую. Push-инвалидция по событию даёт и свежесть, и сниженную нагрузку, но добавляет архитектурную сложность и ещё одну точку отказа (система инвалидции). Критерий выбора — бизнес-требования к допустимой устарелости конкретных данных, а не единый TTL для всего ответа.
Пример кода
GET /products/42 HTTP/1.1
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600 # описание товара - можно кэшировать час
ETag: "v17-description"
{
"id": 42,
"name": "Беспроводная мышь",
"description": ""
}
# Отдельный некэшируемый запрос для динамических данных:
GET /products/42/availability HTTP/1.1
Cache-Control: no-store
{"in_stock": 3, "price": 1999.00}Что использовать на практике
- Практика. Частая практика — разделить ответ API на статическую часть (описание товара, кэшируется агрессивно) и динамическую (остаток на складе, цена) запрашиваемую отдельно без кэша или с очень коротким TTL; либо явно документировать для клиентов приемлемую задержку свежести данных (staleness) для каждой части ответа.
Подводные камни
- Риск. Кэшируют весь составной ответ целиком с одним TTL, подобранным для самой редко меняющейся части, из-за чего часто меняющиеся поля (остаток, цена) показываются устаревшими дольше, чем приемлемо для бизнеса; вводят кэширование без продуманной инвалидации при изменении данных, из-за чего после обновления товара клиенты ещё долго видят старые данные без явного объяснения, почему.