Версия:

Блокировки

Принцип работы

Система имеет механизм блокировки записи или всей функции. Данные блокировок хранятся в определенном объекте в памяти процесса, однако все подготовлено для того, чтобы вынести этот объект, например в 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 не позволяет снять блокировку всей функции. Если понадобится такой метод, его можно будет доработать.