Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Що відбувається, коли кілька запитів одного користувача одночасно змінюють сесію, і навіщо блокування сесії?

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

Приклад:

Запит A (додати товар у кошик)   читає кошик [1]       записує [1, 2]
Запит B (додати інший товар)     читає кошик [1]       записує [1, 3]   <- товар 2 втрачено

Так буває з кількома AJAX-запитами одночасно, подвійним кліком, кількома вкладками. На відміну від стандартних файлових сесій PHP, драйвери Laravel (database, redis, file) не блокують сесію за замовчуванням.

Блокування сесії для конкретних маршрутів:

Route::post('/cart/items', AddToCartController::class)
    ->block($lockSeconds = 10, $waitSeconds = 10);
  • запит бере атомарне блокування для сесії (через кеш) і тримає його до $lockSeconds;
  • наступний запит цієї ж сесії до маршруту з block чекає до $waitSeconds, а якщо не дочекався - отримує LockTimeoutException;
  • запити виконуються послідовно, кожен бачить зміни попереднього.

Вимоги: драйвер кешу з атомарними блокуваннями (redis, memcached, database, dynamodb) і драйвер сесій не cookie.

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

Часто краще не тримати такі дані в сесії взагалі:

  • кошик, чернетки, налаштування - у базі з транзакціями чи атомарними операціями. Там конкурентність розв'язується надійніше й не залежить від сесії;
  • для лічильників - атомарний інкремент у Redis чи базі.

Як виявити проблему: «зникають» щойно додані дані, флеш-повідомлення показується не там, кошик втрачає товари при швидких кліках. Відтворюється одночасною відправкою двох запитів (наприклад, Promise.all з двох fetch).

Докладніше в документації: Блокування сесії

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання