Блокировки
Принцип работы
Система имеет механизм блокировки записи или всей функции. Данные блокировок хранятся в определенном объекте в памяти процесса, однако все подготовлено для того, чтобы вынести этот объект, например в Redis, чтобы обеспечить возможность запуска нескольких инстансов системы с единой информацией о блокировках.
Блокировка записи
Разработчик может повесить блокировку на конкретную запись определенной сущности, тогда при попытках ее изменить или удалить, система будет искать ключ блокировки в объекте запроса, если не найдет, то в родительском запросе и так до верха.
На методы изменения и удаления блокировка ставится автоматически. Разработчик же может выполнить блокировку записи еще в начале кастомного метода, что позволяет делать реализовывать различную логику, например проверки, не опасаясь, что пока они ведутся, исходные данные могут быть изменены.
Если ключ найден, то операция допускается. Поиск в родительских объектах запроса позволяет выполнять манипуляции с записью дочерним методам, что позволяет свободно выносить логику в отдельные методы и самому не заниматься передачей в них ключа блокировки.
Если нет (какой-то другой метод пытается изменить ту же запись), то есть два режима:
В первом (по умолчанию), запрос с небольшой периодичностью пытается повторно провести блокировку (блокировка вызывается из кастомного метода, если разработчик в нем ее написал, или непосредственно в операции изменения или удаления автоматически системой.
Попытки продолжаются, пока запись не освободится или не пройдет выделенное на попытки время. По умолчанию это 2 минуты, но при вызова блокировки, разработчик может задать другое время параметром “lockTime” (в миллисекундах).
Во втором, если при вызове блокировки (если разработчик сам ее вызывает) передать параметр “reject”=true, то в случае если запись занята другой блокировкой, система сразу отклонит запрос, без повторных попыток с небольшим интервалом.
Вызов блокировки выглядит так:
const res = await lock(r, 'order_', id)
if (res.code) {
return new MyError('Не удалось заблокировать заказ для Order_.calcAmounts')
}
Объект запроса, имя сущности, ID записи которую блокировать и, четвертым необязательным параметром, объект с параметрами блокировки.
Блокировка функции
Иногда нужно заблокировать сразу весь метод, чтобы он не вызвался повторно, пока не будет завершен текущий вызов. Это делается также как и с блокировкой записи, первый параметр также объект запроса, второй параметр может быть любой строкой, например имя функции, третий null, а четвертый, необязательный объект с параметрами.
res = await lock(r, this.name, 'syncBJ', null, {reject:true})
if (res.code) {
return new UserOk('Предыдущая итерация syncBJ еще выполняется. Пропустим этот запуск.')
}
Снятие блокировки
Блокировка снимается автоматически, когда завершается метод, в котором она была установлена. Это происходит в модуле api, через который происходит вызов любого метода.
Разработчик также может самостоятельно снять блокировку вызвав unlock
unlock(name:string, id:number|string, key:string): IError
Мониторинг и управление
На практике, мне не разу не приходилось пользоваться этими методами, тем не менее упомяну. Есть два метода showLocks и unlockRec, которые позволяют просмотреть список текущих блокировок и снять любую блокировку. Методы относятся к CoreClass, то есть имеются у любого класса.
Метод unlockRec не позволяет снять блокировку всей функции. Если понадобится такой метод, его можно будет доработать.