Протухшая сессия: как один баг научил меня не доверять чужим API
В моём сервисе интеграции с перевозчиком однажды перестали работать расчёты доставки. Не у всех и не всегда — самый неприятный тип бага. Виноват оказался кэш авторизации, который молча умирал.
Симптом
Часть запросов к API перевозчика возвращала ошибку авторизации, хотя учётные данные были верными. Перезапуск «лечил» проблему — на время. Классическая примета: если рестарт помогает, ищите состояние, которое живёт дольше, чем должно.
Причина
Чтобы не логиниться на каждый запрос, я кэшировал идентификатор сессии внешнего API. Логика выглядела разумно: сессия живёт долго, зачем дёргать авторизацию. Но у провайдера сессия могла умереть раньше заявленного срока — по их внутренним причинам, о которых документация умалчивала. Мой кэш продолжал уверенно отдавать труп.
Кэш чужого состояния — это всегда гипотеза о чужой системе. Гипотезы нужно проверять, а не постулировать.
Решение
- Консервативный
TTLкэша — заметно короче официального времени жизни сессии. - Инвалидация по факту: получил ошибку авторизации — сбрось кэш, получи новую сессию, повтори запрос один раз. Пользователь этого даже не видит.
- Логирование каждого перелогина: если сессии умирают чаще обычного, я узнаю об этом из логов, а не из письма клиента.
Три правила, которые я из этого вывел
- Чужой API врёт. Не по злому умыслу — просто документация описывает намерения, а не поведение. Проектируйте под поведение.
- Любой кэш должен уметь ошибаться дёшево. Стоимость промаха — один лишний логин. Стоимость протухшего значения без инвалидации — сломанный продакшен.
- Ретрай — часть интеграции, а не заплатка. Один повтор с обновлённой авторизацией закрывает целый класс проблем и пишется за полчаса.
Интеграция с внешним API — это на 20% «подключить» и на 80% «пережить все способы, которыми оно ломается». Если вам нужна интеграция, которая переживает, — напишите мне.