GraphQL subscription обычно передает токен при установке WebSocket-соединения. Если access token обновился, уже открытый сокет не всегда узнает об этом, а сервер может закрыть его после истечения старого токена.
Проверьте, какой токен попал в connectionParams, когда сервер закрывает соединение и выполняется ли полное переподключение с повторной подпиской после refresh.
Коротко: что сделать
- Записать close code и время разрыва
- Сравнить срок старого и нового access token
- Проверить функцию connectionParams
- Проверить логику dispose/reconnect
- Проверить повторную регистрацию активных подписок
Почему возникает проблема
HTTP-клиент и WebSocket-клиент имеют разный жизненный цикл. Обновление заголовка HTTP не меняет авторизацию уже открытого сокета.
- connectionParams замкнуты на старое значение токена
- Сокет не пересоздается после refresh
- Два refresh-запроса создают гонку
- Сервер не отправляет понятный close code
- Клиент переподключается, но не восстанавливает subscriptions
- Keepalive маскирует истекшую авторизацию до следующего события
Пошаговая диагностика
Проверку лучше проводить на одном воспроизводимом примере и фиксировать результат каждого шага. Так можно быстро отделить первопричину от побочных ошибок и не менять несколько компонентов одновременно.
- Проверить payload connection_init без вывода токена
- Логировать версию credentials и close code
- Сократить TTL в тестовой среде
- Проверить одиночный и параллельный refresh
- Отключить сеть и восстановить ее
- Проверить число активных subscriptions после reconnect
Как исправить
Клиент должен получать актуальный токен в момент каждого подключения и централизованно восстанавливать подписки.
- Сделать connectionParams функцией, читающей текущий token store
- После успешного refresh корректно пересоздавать WebSocket client
- Сериализовать refresh одним promise
- Использовать контролируемый backoff
- Восстанавливать активные подписки после connected
- Обрабатывать unauthorized отдельно от сетевых ошибок
Как проверить результат
- Дождаться истечения токена при активной подписке
- Проверить отсутствие пропуска важных событий
- Проверить отсутствие дублированных подписок
- Проверить logout и отзыв токена
- Проверить reconnect после потери сети
Как не допустить повторения
Авторизация WebSocket должна проектироваться отдельно от HTTP, но использовать единый источник состояния учетных данных.
- Добавить тест короткого TTL
- Логировать close code и reconnect count
- Ограничить бесконечные переподключения
- Документировать поведение при logout
Чего не стоит делать
- Не передавать token в URL
- Не печатать токен в console
- Не создавать новый сокет на каждый React render
- Не повторять reconnect бесконечно при 4401/unauthorized
Что подготовить для диагностики
- Библиотека GraphQL client/server
- Close code и логи
- Схема refresh token
- Фрагмент connectionParams
- Сценарий активных подписок
Частые вопросы
Можно ли обновить токен внутри открытого WebSocket?
Зависит от протокола и реализации. Часто надежнее корректно переподключиться с новым connection_init.
Почему HTTP-запросы работают, а subscription нет?
HTTP берет новый заголовок для каждого запроса, а WebSocket мог сохранить старую авторизацию на весь сеанс.
Как избежать пропуска событий?
Использовать курсор или last event ID и после reconnect догружать события с последней подтвержденной позиции.
Когда стоит обратиться за помощью
Помощь нужна, если после reconnect теряются события, создаются двойные subscriptions или refresh реализован в нескольких вкладках и возникает гонка.
Итог
Надежная subscription получает актуальный токен при подключении, контролируемо переподключается и восстанавливает поток с курсора. Исправить GraphQL realtime можно через @rabotator_support.