Если несколько программ конкурируют за доступ к одним и тем же данным, Вам необходимо обеспечить синхронизацию обращения к этим данным чтобы они оставались корректными и работа логики приложений не была нарушена.
К примеру, если в системе резервирования билетов необходимо оформить билет на рейс, необходимо защитить данные в таблице хранящей записи о рейсах, чтобы во время оформления другая программа не успела зарезервировать место раньше.
С помощью блокировок можно скоординировать доступ к критичным данным. Программа должна предварительно запросить блокировку на эти данные. В данном случае необходимо чтобы программа как можно раньше сняла блокировку, чтобы не задерживать выполнение других.
СУБД физически блокирует записи в таблице во время их изменения, например: если используется синтаксис SELECT SINGLE <f> FROM<dbtab> FOR UPDATE. Другие пользователи, которые хотят изменить те же данные, должны ожидать пока физическая блокировка будет снята. В конце ЛЕР (логической единицы работы, англ. LUW) базы данных все физические блокировки снимаются. В системе R3 это означает что при каждой смене экрана, будет сниматься физическая блокировка базы данных, так как при смене экрана вызывается неявный DB COMMIT, тем самым завершая ЛЕР базы данных. Следовательно, физической блокировки записей средствами СУБД не достаточно, если данные собираются на нескольких экранах и необходимо сохранять блокировку во время их переключения. (использование множества экранов при сборе данных не является единственным вариантом вызова неявного DB Commit).
Для сохранения блокировки, в случае использования множества экранов (и не только), в системе R3 используется глобальная таблица блокировок, которая служит для установки логических блокировок для записей в таблицах. Все логические блокировки (далее блокировки) не зависят от сервера приложений, на котором они были вызваны, т.о. они являются общими для всех серверов приложений.
Кроме того вы можете использовать блокировки даже для тех записей, которых ещё не существует в системе.
Допускается использование специального символа @ при передаче значений параметров. Если указать в качестве значения параметра — 12345@, будут заблокированы объекты в диапазонах: от 123450 до 123459, от 12345a до 12345z, от 12345A до 12345Z и все объекты со специальными символами в 6-й позиции.
Логические блокировки вызываются модулем блокировки, который создаётся для объекта блокировки. Для вызова блокировки вызывается модуль ENQUEUE_<имяОбъекта>. Для снятия блокировки необходимо вызвать модуль DEQUEUE_<ИмяОбъекта>. Создаётся объект блокировки в транзакции SE11 (ABAP словарь). Например: объект EMMARCE блокирует таблицу MARC от записи. На вкладке ПарамБлокировки задаётся уровень блокировки:
Таким образом, мы можем заблокировать таблицу от записи на уровне манданта, материала, завода. Свои объекты блокировок должны называться, начиная с EY или EZ. Кроме того важной особенностью системы блокировок является то, что мы можем заблокировать еще не созданный (не существующий) объект. После создания и активации объекта блокировок, модули блокирования/деблокирования создаются автоматически.
Установка, настройка и администрирование логических блокировок
При вызове модуля блокирования система может вернуть два исключения. Первое FOREIGN_LOCK – означает, что блокировка уже установлена ранее. SYSTEM_FAILURE – системная ошибка механизма блокировок.
Данные исключения необходимо корректно обработать, например: попросить пользователя подождать или сообщить о блокировании ресурса. Обратите внимание, что если блокировка не сработает, из-за уже установленной ранее, в системные переменные сообщений запишутся данные об ошибке, в частности параметр SY-MSGV1 будет содержать имя пользователя установившего блокировку.
В случае завершения работы ABAP программы, все блокировки снимаются автоматически (неявно, но если произойдёт разрыв сети, то блокировка какое-то время будет «висеть» в системе). Кроме того, блокировки могут так же сниматься программой обновления, за эту логику отвечает параметр _SCOPE (описание ниже). При вызове сообщений с типом X или A, вызовом команды LEAVE PROGRAM, LEAVE TO TRANSACTION или вводом команды /n блокировки так же снимаются.
Если вы хотите снять все блокировки в программе можно воспользоваться ФМ: DEQUEUE_ALL. Для просмотра таблицы блокировок можно использовать транзакцию SM12, или ФМ: ENQUE_READ. Для удаления записи из таблицы блокировок ФМ: ENQUE_DELETE (или тр. SM12).
Параметры EUQUEUE модуля
Mode_<ИмяТаблицы> — режим блокировки. Перезаписывает режим блокировки из объекта блокировки:
- E (Exclusive lock & cumulative) – блокирование от изменения данных, повторный вызов блокировки объекта в этом режиме не приведёт к ошибке, если вызов происходит в рамках одной программы (При запуске внутри одной сессии других программ через оператор CALL TRANSACTION или SUBMIT, если запущенная программа попытается выставить блокировку, система не позволит ей этого сделать). Следует использовать, когда вы изменяете какие-либо критичные данные, но не запрещаете просмотр этих данных. Если выставлена подобная блокировка, нельзя поставить блокировку в режиме S.
- S (Shared lock) – данный режим следует использовать, в случае если вы хотите, чтобы во время показа данных пользователю, никто другой не смог их изменить. Может устанавливаться несколькими пользователями одновременно при просмотре данных. Если выставлена подобная блокировка, нельзя поставить блокировку в режиме E. Если была выставлена блокировка в режиме E (X), нельзя установить блокировку в режиме S.
- O (Optimistic lock) –используется в тех случаях, когда вы отображаете какие-либо данные в режиме редактирования и хотите иметь возможность изменить их позже. Пример: перед вами список рейсов, вы думаете забронировать билет на один из них, что повлияет на количество занятых мест на этом рейсе. Соответственно вы должны поставить блокировки на все отображаемые рейсы, но так чтобы не мешать другим пользователям работать в системе бронирования. С другой стороны, установленные в таком режиме блокировки должны иметь возможность преобразовываться в реальные блокировки для записи (режим E).
- X (Exclusive lock & not cumulative) — расширенная блокировка от изменения данных (Если в своей программе вы вызываете блокировку от изменения данных второй раз для того же объекта, то в случае с X блокировка не пройдёт, но если вы поставили E или S система блокировок не выдаст исключения, это важно понимать когда вы будете использовать систему блокировок в расширениях SAP системы или перед вызовом BAPI функций).
Кроме вышеуказанных режимов есть еще и другие значения этого параметра, которые выступают в роли команды для модуля блокировки:
- R — перевод ранее установленной оптимистичной блокировки в блокировку на запись E.
- С — проверка возможности преобразования оптимистичной блокировки в блокировку на запись E.
- U,V,W — проверка возможности установки блокировки в режимах X,E,S соответственно.
На рисунках ниже демонстрируется, что будет происходить при установке блокировок в разных режимах (если программа ранее уже установила блокировку на объект и если блокировка была установлена другой программой):
<ИмяПараметра> — при вводе параметров вы указываете, какие записи в таблицы будете блокировать.
X_<ИмяПараметра> — в случае если значение равно X, блокируются записи с пустыми значениями.
_SCOPE – может принимать следующие значения:
- «1» — Блокировка остаётся в программе и снимается ею же, либо после завершения транзакции. Если программа использует асинхронное обновление данных, нет гарантии что во время работы процесса обновления изменяемые данные не были заблокированы другим пользователем, соответственно не следует использовать при асинхронных обновлениях, используйте scope = 3.
- «2» — По умолчанию. Блокировка снимается программой обновления, либо модулем деблокирования. Сразу при запуске ROLLBACK WORK. В случае если запускается COMMIT WORK и есть отложенные модули обновления (CALL FUNCTION ‘…’ IN UPDATE TASK), блокировка снимается после выполнения модулей обновления V1, на этапе вызова модулей обновления V2 блокировки уже сняты. Если до вызова COMMIT WORK не было запущено модулей обновления, блокировки не будут сняты.
- «3» — Устанавливается две блокировки, одна остаётся установленной в программе и должна быть удалена в ней. Другая блокировка передаётся в программу обновления. Блокировка будет снята тогда, когда мы снимем её в программе и закончится обновление (по второму сценарию), либо после завершения программы.
_wait – если значение «X», функциональный модуль, в случае если уже стоит блокировка, будет ожидать определённый промежуток времени перед повторным вызовом ФМ. Количество повторений задаётся параметром в профиле (RZ11) — ENQUE/DELAY_MAX. Время, которое будет ожидать система 1 секунда (по умолчанию), параметр ENQUE/DELAY_MAX_REFINE отвечает за то, сколько раз в секунду будет происходить проверка.
_collect – отвечает за то, что блокировка, перед тем как попасть в таблицу блокировок, будет помещена во временный буфер, после чего используя ФМ: FLUSH_ENQUEUE весь этот буфер будет отправлен на сервер блокировок (в таблицу блокировок), в случае если хотя бы одна блокировка не удалась, система выдаст исключение. Для очистки буфера служит ФМ: RESET_ENQUEUE. Используется при множественной блокировке объектов.
Две концепции применения блокировок при изменении данных
Если вы хотите быть уверенными в том, что редактируете актуальную версию данных и намерены её сохранить в базе данных, следует использовать одну из концепций установки блокировок: пессимистичную и оптимистичную. Оптимистичная концепция была внедрена начиная с версии ABAP 7.0. EhP2.
Пессимистичная блокировка (классическая) использует следующий порядок действий:
- Установить блокировку в режиме E (X)
- Если блокировка установлена, считать данные из БД
- Изменить данные в программе и БД
- Снять блокировку
Оптимистичная блокировка:
Как было сказано выше, оптимистичные блокировки используются, когда пользователю отображаются данные в режиме изменения, но менять их в текущий момент пользователь не хочет. Если пользователь захочет сохранить измененные данные, ему потребуется сначала преобразовать блокировку из оптимистичной в блокировку на запись (эксклюзивную, режим «E»). Преобразование происходит путём вызова модуля блокировки с указанным режимом «R». Преобразование не сработает, если была выставлена другая блокировка в режиме E (X) или S. Если преобразование было успешным, другие оптимистичные блокировки будут сняты, выставлена одна с типом E.
Таким образом, для реализации оптимистичной системы блокировок применяются следующие шаги:
- Установка блокировки в режиме «O». На данном этапе ошибка может возникнуть, если в системе уже выставлена блокировка на запись, режим E (X).
- Если блокировка успешно выставлена, читаем данные из БД.
- Попытаться преобразовать блокировку из оптимистичной в блокировку на запись. В случае успеха оптимистичные блокировки в других программах будут сняты. Ошибка на данном этапе может возникнуть, если установлена блокировка в режиме S или оптимистичная блокировка была удалена (в связи с преобразованием оптимистичной блокировки в блокировку на запись в другой программе)
- Изменить и сохранить данные в БД
- Снять блокировку.
Оптимистичные блокировки не защищают от внешних изменений данных, но благодаря им мы можем идентифицировать эти изменения.
Важные параметры настройки системы блокировок
- enque/table_size — размер таблицы блокировок в килобайтах, значение по умолчанию — 4096. Так как хранится таблица не на уровне сервера БД, а в памяти может потребоваться увеличение её объема.
- enque/backup_file — путь к бекап файлу, блокировки из которого будут загружены при перезагрузке сервера блокировок.
Официальная документация тут.
На рисунке, изображающем что будет, если программа, установившая блокировку попытается её изменить — ошибка. Изменить блокировку с S на E — нельзя. An exclusive lock set on an object that already has a shared lock will be rejected.
Если программа сама поставила блокировку типа S, она вполне может выставить блокировку типа E.
Да, я был не прав.
Небольшое дополнение, а скорее уточнение:
если при вызове блокировки в поле передать какое-то значение и при этом оставить ‘X’ на соответствующем ему поле X_, то в блокировку подставится именно переданное значению, а не пустое поле.
Привет! Все верно, ведь x_field имеет смысл только если field имеет начальное (initial) значение.
Привет. Ага, отсюда и вычитал (:
Добрый день! Каким образом можно узнать сколько времени на объекте была блокировка?
Добрый день! По существующей, время установки можно узнать через ФМ ENQUEUE_READ.