0% прочитано

Чому WebSocket не повинен бути єдиним джерелом правди для непрочитаних сповіщень

WebSocket добре підходить для миттєвого оновлення інтерфейсу, але подія може бути пропущена. Вкладка могла спати, мережа могла зникнути, ноутбук - прокинутися після паузи. Тому оперативні події варто поєднувати з HTTP-оновленням, яке відновлює фактичний стан.

27 жовтня 2026 р. 9 хв читанняЛаравель

Коли сповіщення приходять через WebSocket, інтерфейс виглядає живим: нова подія відразу збільшує лічильник, додає запис у список або відкриває потрібну реакцію.

З цього легко зробити небезпечний висновок: якщо подія не прийшла через WebSocket, значить стан не змінився.

Насправді WebSocket - це канал доставки оперативного сигналу, а не гарантія, що клієнт бачив кожну зміну стану.

Для непрочитаних сповіщень джерелом правди має залишатися серверний стан, який клієнт уміє повторно отримати.

WebSocket відповідає за швидкість реакції інтерфейсу. HTTP-оновлення відповідає за відновлення правильного стану, якщо частина подій була пропущена.

Подія може бути пропущена навіть у справній системі

Пропущена подія не обов’язково означає помилку сервера або несправний WebSocket.

Клієнт міг тимчасово перестати слухати канал. Браузер міг приспати вкладку. Ноутбук міг перейти в режим сну. Мережа могла зникнути на кілька секунд. З’єднання могло відновитися вже після того, як подія була відправлена.

У кожному з цих випадків серверний стан правильний, але локальний стан у браузері вже відстає.

Лічильник у браузері - це похідний стан

Якщо сервер зберігає сповіщення та їхній стан прочитання, то число "5 непрочитаних" у браузері не є самостійним джерелом даних.

Це лише локальне представлення результату, який можна знову обчислити або отримати із сервера.

Саме тому локальний лічильник можна оперативно змінювати після події, але періодично його потрібно звіряти з сервером.

Локальний стан можна відновити
Server state:    unread = 7 Browser state:    unread = 6 Reason:    one WebSocket event was missed Recovery:    GET /notifications/unread-count    -> unread = 7

WebSocket добре підходить для оперативного оновлення

Відмовлятися від WebSocket через можливість пропущених подій не потрібно.

Його сильна сторона саме в тому, що інтерфейс не мусить постійно опитувати сервер. Коли з’являється нове сповіщення, клієнт може відразу оновити лічильник і список.

Проблема виникає лише тоді, коли цей канал стає єдиним способом дізнатися про зміну.

Оперативне оновлення після події
echo.private(`notification.user.${userId}`)    .listen('.notification.created', () => {        unreadCount.value += 1;        refreshNotificationList();    });

HTTP потрібен не як запасний WebSocket, а як відновлення стану

HTTP-запит тут виконує іншу роль. Він не намагається повторити кожну подію.

Він просто запитує: "який стан правильний зараз?".

Це важливе спрощення. Клієнту не потрібно знати, яку саме подію він пропустив. Достатньо повторно отримати поточну кількість непрочитаних сповіщень або актуальний список.

Відновлення поточного стану через HTTP
async function refreshUnreadCount() {    const response = await api.get(        '/notifications/unread-count'    );     unreadCount.value =        response.data.unread;}

Коли варто повторно синхронізувати стан

Оновлювати стан після кожної секунди без потреби не потрібно. Набагато корисніше вибрати моменти, коли ризик розходження локального і серверного стану справді високий.

  • після повернення вкладки у видимий стан;
  • після події pageshow;
  • після відновлення мережі;
  • після повторного підключення WebSocket;
  • після входу або відновлення сесії;
  • після дій, які змінюють стан прочитання.
Оновлення у точках можливого розходження стану
document.addEventListener(    'visibilitychange',    () => {        if (            document.visibilityState === 'visible'        ) {            refreshUnreadCount();        }    }); window.addEventListener(    'pageshow',    refreshUnreadCount); 

Відновлення після повторного підключення важливіше за сам факт з’єднання

Повторне підключення WebSocket означає лише, що канал знову доступний.

Воно не доводить, що клієнт отримав усі події, які сталися під час розриву.

Тому після відновлення з’єднання корисно не просто продовжити слухати нові події, а ще раз отримати поточний стан через HTTP.

Не потрібно відновлювати пропущену історію, якщо потрібен лише поточний стан

Для лічильника непрочитаних сповіщень часто неважливо, чи клієнт пропустив одну, дві або п’ять подій.

Йому важливо знати правильну кількість зараз.

Тому просте повторне читання серверного стану може бути надійнішим і значно простішим за спробу відтворити кожну пропущену подію.

Оперативність і достовірність не суперечать одна одній

Два механізми з різними обов’язками
WebSocket event    -> швидке локальне оновлення visibilitychange / pageshow / reconnect / online    -> HTTP refresh    -> звірка з сервером    -> відновлення правильного стану

Ці два механізми не дублюють один одного.

WebSocket дає швидку реакцію на нову подію. HTTP повертає клієнт до правильного стану, якщо між ними виник розрив.

Так система отримує і швидкість, і можливість самостійно відновитися після тимчасової втрати зв’язку.

Цей принцип стосується не лише сповіщень

Така сама модель корисна для інших даних, які оперативно оновлюються через події:

  • лічильники повідомлень;
  • стан задач;
  • статус замовлення;
  • прогрес довгої операції;
  • наявність користувача в мережі;
  • інші похідні показники інтерфейсу.

Якщо подія лише повідомляє про зміну, клієнт повинен мати спосіб повторно отримати правильний стан без залежності від повної історії подій.

Що варто перевірити тестами

Мінімальний набір перевірок
1. Нова подія відразу оновлює лічильник.2. Пропущена подія не залишає лічильник неправильним назавжди.3. Повернення у вкладку запускає звірку стану.4. pageshow запускає звірку стану.5. Відновлення мережі запускає звірку стану.6. Повторне підключення WebSocket запускає звірку стану.7. HTTP-відповідь замінює застарілий локальний лічильник.8. Повторне оновлення не створює дублікати сповіщень.

WebSocket має прискорювати інтерфейс, а не визначати правду

Якщо правильність лічильника залежить від того, що браузер ніколи не пропустить жодної події, система крихка.

Надійніша модель інша: WebSocket швидко повідомляє про зміну, а серверний стан у будь-який момент можна повторно отримати через HTTP.

Тоді тимчасовий розрив зв’язку перестає бути незворотною втратою інформації для клієнта.

Це доповнює проблему, яку я розбирала в окремій статті про застарілий лічильник після повернення у вкладку, але тут межа ширша: справа не лише у поверненні до вкладки, а в самому розподілі відповідальності між оперативною подією та достовірним станом.

WebSocket повідомляє, що щось змінило. Серверний стан відповідає на запитання, що є правильним зараз.