Web Locks API: безопасная координация общей работы браузера
Координируйте вкладки, окна и workers через Web Locks API, не смешивая браузерные блокировки с распределёнными серверными блокировками.
BotBrowser Team
Нужна структурированная документация по теме Платформа?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
Когда несколько вкладок или workers изменяют один ресурс браузера, проверка перед записью не координирует работу: оба контекста могут пройти проверку одновременно. Web Locks API ставит именованные запросы в очередь, чтобы только один контекст одного origin владел критической секцией. API не координирует серверы или разные origin и сам по себе не исправляет состояние после закрытия приложения. BotBrowser может повторить авторизованный профильный сценарий, но не выдаёт блокировку и не заменяет корректность приложения или сервера.
TL;DR
- Охватывайте блокировкой только минимальную общую операцию и освобождайте её возвратом из callback.
- Используйте
ifAvailableили дедлайн приложения, если ожидание не подходит; отказ не означает получение блокировки. - Worker, вкладка или окно могут исчезнуть. Делайте операцию идемпотентной, а восстановление держите вне callback.
- Web Locks координирует контексты браузера одного origin, но не cross-origin страницы и не серверные процессы.
- BotBrowser помогает повторять авторизованный профильный сценарий, но семантика блокировки, хранилище и отмена остаются ответственностью страницы.
Содержание
- Модель блокировки
- Небольшой fixture
- Безопасность worker и восстановление
- Доказательства и матрица
- Итог
- FAQ
Модель блокировки
navigator.locks.request(name, options, callback) ставит именованную блокировку в очередь. По умолчанию режим эксклюзивный; shared-режим допускает нескольких читателей и исключает эксклюзивного писателя. Callback запускается после выдачи блокировки и удерживает её до возврата или завершения promise. Другой контекст того же origin ждёт в очереди, а не опрашивает флаг в хранилище.
Это не глобальный mutex backend: очередь не пересекает origin, профили, устройства или серверные экземпляры. Критическая секция должна быть короткой: прочитать запись, проверить изменение, записать и вернуть результат. Сетевые вызовы обычно выполняются вне блокировки; долговечное решение требует серверной транзакции или lease.
Небольшой fixture
Fixture увеличивает локальный счётчик и сообщает, получена ли блокировка. При ifAvailable значение lock равно null, если имя занято.
async function incrementCounter(storage, report) {
if (!navigator.locks) return report('unsupported');
return navigator.locks.request('counter-write', { ifAvailable: true }, async lock => {
if (!lock) return report('busy');
const current = Number(storage.getItem('counter') || 0);
storage.setItem('counter', String(current + 1));
report('committed');
});
}
Два одновременных контекста не входят в секцию вместе; занятый сообщает busy, а браузер без API сообщает unsupported. Это только локальный пример: localStorage не является транзакционной базой. Для важных данных используйте подходящее хранилище и повторно проверяйте конфликт на сервере.
Безопасность worker и восстановление
Worker может запросить то же имя, что и окно. Если его завершить во время ожидания или callback, браузер освободит блокировку, но частичная запись или внешний эффект остаются задачей приложения. Используйте ключ идемпотентности и различайте «не начато», «подтверждено» и «неизвестно после прерывания». Не держите блокировку во время медленного запроса сети.
Не создавайте бесконечную очередь пользовательских действий. ifAvailable подходит необязательному обновлению; обязательной работе нужны дедлайн и проверка отмены перед необратимым шагом. Всегда возвращайтесь из callback и закрывайте порты worker при остановке.
BotBrowser позволяет повторять авторизованные сценарии профиля для проверки совместимости. Он не выдаёт Web Lock, не освобождает блокировку другой вкладки, не управляет меж-origin планированием и не заменяет серверные транзакции.
Доказательства и матрица
Возьмите два окна одного origin и worker, уникальное имя, счётчик и принудительное завершение worker. Запишите версию браузера, профиль, headed/headless, хранилище, видимый результат и итоговое значение; не сохраняйте данные аккаунта или сырые снимки.
| Наблюдение | Безопасная трактовка | Действие |
|---|---|---|
Один committed на принятый запрос | Секция сериализована в этом тесте | Сохранить короткую секцию и повторить после обновления |
busy при ifAvailable | В момент запроса имя занято | Сохранить состояние и решить, нужен ли видимый повтор |
unsupported | API нет в текущем контексте | Использовать запасной путь или серверную транзакцию |
| Worker завершился до отчёта | Результат может быть неизвестен | Сначала сверить ключ идемпотентности |
| Счётчик неверен при сериализации | Хранилище или проверка неатомарны | Перенести инвариант в транзакционное хранилище |
Fixture показывает наблюдаемое поведение браузера, а не распределённую координацию. Проверяйте также сообщение, сохранённую форму и повтор без дублирования записи.
Граница ресурса и ответственность callback
Имя должно описывать защищённый ресурс, а не экран. Разделяйте разные инварианты и не используйте общий lock для независимой работы. Callback должен прочитать, проверить, записать и вернуть результат; блокировка освобождается после завершения promise. Сеть, ожидание пользователя и анимации выполняются вне lock.
Ошибка освобождает lock, но не доказывает завершение внешнего эффекта. AbortController.abort() не отзывает уже начатый callback: он только сообщает коду, который проверяет signal. Проверяйте отмену перед необратимым шагом и делайте повтор идемпотентным.
Исчезновение владельца и дедлайн
Вкладка, окно или worker могут исчезнуть. Браузер освободит lock, но приложение должно записать намерение с ключом идемпотентности и сверить outbox: отличить не начато, подтверждено и неизвестно перед повтором. При ifAvailable состояние busy сохраняется; обязательной работе нужен дедлайн, а не плотный цикл запросов.
Поддержка и повторная проверка
Разделяйте наличие API, завершение операции и видимый результат. Строка версии браузера не заменяет fixture. Проверяйте отсутствие API, отказ callback, ошибку хранилища и остановку worker; сохраняйте доступный статус и повтор, доступный с клавиатуры. После изменения браузера, хранилища или offline-режима пересмотрите имена, запасной путь и хранение доказательств.
Итог
Web Locks подходит для короткой координации контекстов одного origin. Делайте записи атомарными, восстанавливайте прерывания и храните долговечные инварианты там, где они переживут браузер. Следующий шаг - запустить fixture двух окон и worker с настоящим запасным путём и проверить итоговую запись.
FAQ
Защищает ли Web Lock строку сервера?
Нет. Сервер всё равно отвечает за авторизацию, конфликты и транзакцию или идемпотентность.
Можно ли принудительно освободить блокировку по таймауту?
Нет. Браузер освобождает её после callback или завершения владельца. Проектируйте отмену и не держите медленную работу внутри.
ifAvailable означает ошибку?
Это осознанный отказ от ожидания. null значит, что операция не выполнялась; сохраните состояние и решите, предлагать ли повтор.
Делает ли BotBrowser планирование детерминированным?
Нет. Он делает повторяемым совместимый профильный сценарий, но планирование, хранилище и восстановление остаются ответственностью приложения.
Источники и чтение
Sources
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.