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.