Суть Читай реальные HTTP-обмены — первый запрос, ревалидация, weak vs strong сопоставление и range-запрос — и предскажи статус сервера и фикс с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Баги ETag диагностируются в логе обмена, а не в исходниках. Читай каждый обмен так, как читал бы его в DevTools или tcpdump, затем выбери, что вернёт сервер, — а там, где что-то не так, и фикс, к которому senior-инженер потянется первым.
Цель
Отработай цикл, который ты проходишь в каждом инциденте кэширования: читай заголовки запроса и ответа, проследи сравнение валидаторов и предскажи статус — 200 vs 304 — прежде чем трогать конфиг.
Обмен 1 — рукопожатие ревалидации
# Первый запросGET /api/config HTTP/1.1Host: api.example.comHTTP/1.1 200 OKETag: "a3f5c901"Content-Length: 8192... 8 КБ JSON-тело ...# Через 30 с, контент не изменилсяGET /api/config HTTP/1.1Host: api.example.comIf-None-Match: "a3f5c901"
Викторина
Completed
Контент не менялся с первого запроса. Что сервер вернёт на второй запрос и что пройдёт по проводу?
Heads-up Совпадение значит, что клиент уже держит текущую версию; повторная отправка тела — ровно то, ради избегания чего и существуют conditional request.
Heads-up 204 означает успешный запрос вообще без контента; у ревалидации есть свой статус — 304 Not Modified, говорящий клиенту переиспользовать кэшированную копию.
Heads-up Это long-polling, другой механизм. Conditional GET завершается сразу через 200 или 304.
Обмен 2 — слабый тег, сильное предусловие
GET /report.html HTTP/1.1Host: cdn.example.comIf-None-Match: "v42"# текущий валидатор сервера для ресурса:# ETag: W/"v42"
Викторина
Completed
Клиент прислал сильный тег в If-None-Match; у сервера сейчас слабая форма того же значения. Какой статус вернётся?
Heads-up Для If-None-Match сравнение слабое по определению; префикс W/ не учитывается, поэтому теги совпадают и сервер шлёт 304.
Heads-up 412 относится к предусловиям If-Match на записи; для If-None-Match слабое сравнение здесь успешно и даёт 304, а не ошибку.
Heads-up Смешение форм строго определено: If-None-Match всегда сравнивает слабо, так что это валидный 304, а не ошибка протокола.
Клиент возобновляет загрузку с If-Range, несущим слабый валидатор, совпадающий с текущим слабым ETag сервера. Что вернёт сервер?
Heads-up If-Range требует побайтовой идентичности; слабый валидатор обещает лишь семантическую эквивалентность и не гарантирует, что уже скачанные байты совпадают, поэтому range отклоняется и шлётся полный 200.
Heads-up If-Range никогда не даёт 304; он выбирает между 206 (сильный валидатор совпал) и полным 200. Слабый валидатор вынуждает полный 200.
Heads-up 416 — для диапазонов за пределами ресурса. Здесь range валиден; слабый валидатор просто понижает ответ до полного 200, а не до ошибки.
Обмен 4 — поюнитная регрессия
# Запрос попадает на под A, который выпустил кэшированный тег клиентаGET /api/config HTTP/1.1If-None-Match: "pod-a-counter-7"HTTP/1.1 304 Not Modified# Следующий опрос round-robin направляет на под BGET /api/config HTTP/1.1If-None-Match: "pod-a-counter-7"HTTP/1.1 200 OKETag: "pod-b-counter-3"Content-Length: 8192
Викторина
Completed
Контент конфига идентичен, но под A возвращает 304, а под B — 200 на тот же If-None-Match. Что сломано и какой фикс с наибольшим рычагом?
Heads-up Контент идентичен — под B шлёт то же тело 8 КБ. Дефект в локальном для узла выводе токена, а не в устаревших данных, так что перезапуск ничего не меняет.
Heads-up Sticky sessions — обходной путь, маскирующий настоящий баг и ломающийся при замене пода. Фикс — содержательный ETag, делающий ревалидацию корректной независимо от отвечающего пода.
Heads-up Это прячет симптом, пропуская ревалидацию, но устаревший конфиг теперь будет отдаваться после изменения. Первопричина — несогласованные поюнитные токены — всё равно требует содержательного ETag.
Итог
Каждый инцидент ETag читается в обмене: совпавший If-None-Match даёт 304 с пустым телом, но круговой обход всё равно платится; If-None-Match сравнивает слабо, поэтому W/ и сильная форма одного значения совпадают; If-Range — единственное место, требующее сильный валидатор, поэтому слабый тег вынуждает полный 200 вместо 206; а когда идентичный контент даёт разные токены на разных подах, баг — в локальном для узла ETag: лечится выводом тега из контента, а не из машины.