Программирование обновлений БД в ABAP

Транзакция — в информатике, группа последовательных операций, которая представляет собой логическую единицу работы с данными. Транзакция может быть выполнена либо целиком и успешно, с соблюдением целостности данных и независимо от параллельно идущих других транзакций, либо не выполнена вообще, в каковом случае она не должна произвести никакого эффекта. Транзакционность в системе SAP поддерживается на двух уровнях; на уровне СУБД и на уровне сервера приложений:

  • На уровне СУБД транзакционность поддерживается путем открытия ЛЕР (логических единиц работы, английский вариант LUW Logical Unit of Work). В начале выполнения программы при обращении к базе данных открывается ЛЕР на уровне базы данных, далее при работе программы, если происходит явный или не явный COMMIT, то текущий ЛЕР закрывается, все данные сохраняются в БД и открывается новый ЛЕР.
 
  • На уровне сервера приложений для обеспечения транзакционности используются «модули обновления» (такие как CALL FUNCTION IN UPDATE TASK, PERFORM ON COMMIT), данные модули при вызове регистрируются в системе, но не выполняются сразу. Система собирает очередь обновления, после того, как в программе появляется явный оператор завершения транзакции (COMMIT WORK), система начинает выполнять последовательно зарегистрированные процессы обновления. Если в программе выполняется явный оператор отката транзакции (ROLLBACK WORK), то система удаляет вызовы из очереди обновления и откатывает текущий ЛЕР на уровне БД.

По способу организации обновление базы данных может происходить следующим образом:

  1. Обновление, инициируемое в основном приложении:
  • Прямое обновление
  • Обновление, использующее отложенные вызовы процедур
  1. Обновления, запускаемые программами обновления, со следующими параметрами:
  • Синхронный, асинхронный и локальный запуск
  • Через модули обновлений с типом V1 или V2.

 

Прямое обновление

Прямое обновление подразумевает использование операторов OpenSQL изменяющих состояние базы данных, после чего происходит вызов COMMIT WORK. Программа будет остановлена на операторе COMMIT WORK до тех пор, пока все обновления не выполнятся.

Отложенный вызов процедур обновлений

Отложенный вызов подразумевает собой регистрацию процедур обновления с помощью ключевого слова PERFORM <ИмяПроцедуры> ON COMMIT (ROLLBACK). Запуск этих процедур не происходит сразу, они начинают выполняться только после оператора COMMIT WORK (ROLLBACK). Данные процедуры не должны иметь интерфейса. В случае, если в какой либо зарегистрированной процедуре происходит ошибка, для отмены изменений необходимо вызвать MESSAGE с типом A.  Работа программы возобновляется после того как отработает последняя процедура.

Кроме того отложенные вызовы процедур могут быть использованы в обновлениях запускаемых программой обновления (см. ниже).

Обычно выполнение процедур происходит в том же порядке, в котором они вызваны, однако можно переопределить порядок вызова процедуры с помощью ключевого слова LEVEL <Уровень>. Чем ниже уровень, тем раньше будет запущена процедура.

Прямые обновления рекомендуется использовать, когда количество обновляемых данных минимально.

Крайне не рекомендуется использовать одновременно прямые и обновления запускаемые программой обновления, тем более в расширениях системы, где во время работы программы может быть вызван неявный DB COMMIT из-за чего прямые обновления в систему будут записаны, а те что делались программой обновления нет (небыли запущены). Проблемы неявных COMMIT’ов рассмотрена ниже.

Отменить отложенные вызовы, без отмены SAP LUW можно следующим способом:

Обновления, запускаемые программой обновления

Общая схема работы выглядит следующим образом:

Как правило, программа обновления запускается в отдельном рабочем процессе, отдельно от главной диалоговой программы (исключение локальное обновление).  Рассмотрим процесс по шагам:

Шаг 1. Диалоговая программа получает данные от пользователя и заносит их специальную таблицу с запросами на обновление.

Шаг 2. Диалоговая программа завершает свой LUW, инициируя перенос пакета созданных запросов в программу обновления, вызывает оператор COMMIT WORK (AND WAIT).

Шаг 3. На третьем шаге  пакет запросов будет перенесен системой в программу обновления.

Шаг 4. Программа обновления переносит все данные из запросов в базу данных.

Шаг 5. Если программа обновления выполнилась успешно, система удаляет все запросы, связанные с текущим LUW. В случае если происходит ошибка, обрабатываемые записи в таблице помечаются как некорректные. Пользователь, запустивший обновление, получает уведомление на SAP почту. За отправку уведомлений отвечает параметр rdisp/vbmail, за список пользователей параметр rdisp/vb_mail_user_list. Транзакция показывающая запросы обновлений – SM13.

С технической точки зрения, регистрация запросов на обновление происходит с помощью специальных ФМ – модулей обновления. Определяется в свойствах ФМ:

Интерфейс данных функций поддерживает только IMPORTING и TABLES параметры. Параметры EXPORTING и EXCEPTIONS игнорируются и не должны использоваться.

Регистрация запросов на обновление в диалоговой программе происходит с помощью специального вызова ФМ с ключевым выражением – IN UPDATE TASK. Все запросы сохраняются в таблице со специальным ключом VBKEY, который идентифицирует текущий LUW.  Как только вы запустите COMMIT WORK (Шаг 2), будет инициировано обновление (Шаг 3).

Для того чтобы удалить все запросы на обновления можно воспользоваться либо ROLLBACK WORK, либо MESSAGE TYPE A. При этом будут отменены как все запросы на обновления, так и все прямые изменения (если они были, нежелательно), будет создан новый LUW.

В модулях обновления запрещено использование COMMIT WORK или ROLLBACK WORK, но если вы хотите отменить все изменения вносимые запросами, необходимо вызывать MESSAGE TYPE A. После чего все запросы в текущем LUW будут отмечены как ошибочные (тр. SM13), пользователь совершающий обновление будет оповещен об ошибке.

При использовании системы блокировок, если параметр _scope выставлен по умолчанию (2), система снимет все поставленные блокировки (см. статью про блокировки).

Асинхронный запуск модулей обновления

При асинхронном обновлении диалоговая программа и программа обновления запускаются в разных рабочих процессах на сервере приложений:

После вызова COMMIT WORK системой будет запущена программа обновления, при этом диалоговая программа не будет остановлена и продолжит свою работу, не ожидая конца обновлений.

Асинхронные обновления должны использоваться в программах, где обновление базы данных занимает продолжительное время.

Синхронный запуск модулей обновления

При синхронном запуске модулей обновления необходимо использовать конструкцию COMMIT WORK AND WAIT.

При синхронном запуске диалоговая программа и программа обновления так же запускаются в разных рабочих процессах на сервере приложений. Разница состоит в том, что программа приостанавливает свою работу до тех пор, пока не будет завершена программа обновления. При этом в после ошибки sy-subrc попадет код ошибки, в случае если она произошла в программе обновления.

Локальные обновления

При локальном обновлении обработка запросов на обновление осуществляется в той же диалоговой программе, где они были зарегистрированы, сами запросы хранятся при этом в области памяти, а не в таблице. Для того чтобы включить локальные обновления необходимо воспользоваться следующим выражением SET UPDATE TASK LOCAL до момента регистрации запросов.  Работа программы возобновляется только после обработки всех запросов текущего LUW, т.о. обновление происходит синхронно, из-за этого локальные обновления применяются в основном в фоновом режиме.

 

Типы модулей обновления

Существует два типа модулей обновления V1 и V2. Тип определяет порядок обработки запросов. Сначала обрабатываются все запросы V1 в своем LUW (в отдельном рабочем процессе, если в системе настроены V2 модули обновления), затем если все V1 запросы были выполнены, запускается обновление V2 запросов в отдельном LUW.

V1 модули могут быть с возможностью повторного запуска и без. Повторить запросы можно через транзакцию SM13. Запросы V2 модулей можно всегда повторить.

Общая схема работы V1 и V2 модулей:

Как видно из рисунка – V1 и V2 запросы обрабатываются в отдельных рабочих процессах. В случае если в системе не настроено использование V2 модулей, они будут выполняться в том же рабочем процессе что и V1, но в разных DB LUW. Следует отметить, что если используются блокировки и параметр _scope стоит по умолчанию (2), в момент запуска обработки V2 запросов все блокировки будут уже сняты.

Стоит обратить особое внимание на то, что при запуске в синхронном режиме ожидание будет происходить только для V1 модулей, система не будет ожидать пока выполнятся V2 модули.

Отложенный запуск процедур обновлений в модулях обновлений

Во время обновлений часто используемых таблиц, важно чтобы обновление прошло как можно позже (не держать физическую блокировку базы данных на протяжении выполнения всех запросов, либо сделать какие-то действия на этапе их непосредственного сохранения в расширении системы). Для этого можно воспользоваться  отложенными процедурами обновления, схема их работы аналогична той, что была описана выше (прямые обновления), их вызов происходит в момент, когда все запросы были обработаны.

Проблемы неявных COMMIT’ов

При обработке программы на сервере приложений системы часто происходит закрытие текущей ЛЕР БД и открытие новой ЛЕР БД в рамках одной программы (неявные COMMIT’ы). Данная ситуация постоянно происходит при обработке экранов. На из справки (ниже) по обновлению системы видно, что при каждой обработке событий PAI на экране происходит неявный DB COMMIT, после чего текущий ЛЕР БД закрывается и открывается новый, и все прямые обновления сделанные в БД больше откатить нельзя. На первый взгляд, что в этом такого, но давайте рассмотрим пример.

Для примера была создана таблица ZTEST_UPDATE в БД

Написана простая программа.

До запуска данной программы таблица была пустая.

После запуска программы из общих соображений ожидается, что таблица так и останется пустой, но это не так. Сообщение выданное в виде окошка, вызывает неявный DB COMMIT и после этого оператор отката ROLLBACK WORK откатывает уже новую ЛЕР, а ЛЕР в которой был сделан MODIFY уже закрыт, и изменения в БД сохранены.

Однако, если убрать оператор MESSAGE (в Программе 1), то таблица так и останется пустой.

Неявные DB COMMIT’ы вызываются в следующих случаях:

  • Смена SAP экрана
  • Вызов диалогового сообщения
  • Оператор Wait прерван рабочим процессом
  • Синхронный или асинхронный вызов RFC функции (исключение qRFC, tRFC, bgRFC)

Полный перечень таких ситуаций можно посмотреть в справке.

Неявные DB ROLLBACK’и вызываются в следующих случаях:

  • Ошибка времени выполнения
  • Сообщение об ошибке с типом A,X

13 комментариев

    1. Спасибо за отзыв, некоторые материалы были позаимствованы из данной статьи: sapland.ru/articles/stats/2010/1/Tonkie_momenti_obnovleniya_bazi_dannih_v_ABAP.html

    1. Коллега, при вызове CALL TRANSACTION и SUBMIT не происходит неявного комита, если же комита нет внутри вызываемой программы.

        1. Коллеги, при SUBMIT произойдёт неявный DB Commit. Проверил сейчас на примере.
          В первой программе делаем прямой INSERT, потом делаем SUBMIT 2-й программы (пустой например). Изменения в БД занесутся.
          Но вот если, например, в первой программе были и прямые изменения (тот же INSERT например) и вызван ФМ обновления с добавлением IN UPDATE TASK, и вызвали SUBMIT в конце, то будет только неявный DB Commit, на БД повлияет только тот прямой INSERT, а вот то, что было в ФМ обновления, это уже пропало. Т.к. явного COMMIT в первой программе не было.

          И да, SAP LUW будет открыт новый, как Вы и сказали

        2. Коллеги, для удобства опишу полученные мною результаты тестирования запусков программ через SUBMIT и SUBMIT AND RETURN, а также транзакций с помощью CALL TRANSACTION.
          На этих примерах можно увидеть, в каких именно случаях происходит неявный DB Commit, как меняется KEY SAP LUW и как ведут себя логические блокировки.
          Возможно, это поможет более детально увидеть разницу между SUBMIT и SUBMIT AND RETURN, а также CALL TRANSACTION.

          Пример 1. В первой программе прямой INSERT, ФМ блокировки (рандомный), затем просто SUBMIT (без AND RETURN) пустой программы.
          => Произойдёт неявный DB Commit. Прямые обновления будут в БД.
          Лог. блокировки таблиц (с помощью модулей блокировки) снимаются при переходе в вызываемую программу
          SAP LUW будет новый.

          Пример 2. В первой программе ФМ обновления с IN UPDATE TASK, ФМ блокировки (рандомный), затем просто SUBMIT (без AND RETURN) пустой программы.
          => Изменения не перенесутся в БД, т.к. не было явного Commit.
          Теперь про эти изменения можно забыть.
          Лог. блокировки таблиц (с помощью модулей блокировки) снимаются при переходе в вызываемую программу
          SAP LUW будет новый.

          Пример 3. В первой программе прямой INSERT, ФМ блокировки (рандомный), затем SUBMIT AND RETURN пустой программы.
          => Неявный DB Commit (в момент перехода) не произойдёт. Прямые обновления будут в БД только после того, как
          закончится первая (основная) программа), т.к. неявного DB Commit нигде не было в данном примере.
          Лог. блокировки таблиц (с помощью модулей блокировки) сохраяются в вызываемой программе и по возвращению в первую
          SAP LUW будет новый в программе, которую вызвали через SUBMIT AND RETURN.
          Но затем по возвращению SAP LUW будет тот же
          1) SAP LUW в первой программе — 12ED7F1F90410200E00624E511B3852E
          2) SAP LUW в программе, вызванной через SUBMIT AND RETURN — 12ED7F1F90410200E00624E511B3852F
          3) SAP LUW в первой программе по возвращению из вызываемой — 12ED7F1F90410200E00624E511B3852E (тот же, как и в самом начале)

          Пример 4. В первой программе ФМ обновления с IN UPDATE TASK, ФМ блокировки (рандомный), затем SUBMIT AND RETURN пустой программы.
          => Неявный DB Commit не произойдёт. Прямые обновления будут в БД.
          Блокировки таблиц (с помощью модулей блокировки) сохряются в вызываемой программе и по возвращению в первую
          SAP LUW будет новый в программе, которую вызвали через SUBMIT AND RETURN.
          Но затем по возвращению SAP LUW будет тот же
          1) SAP LUW в первой программе — 12ED7F1F90410000E00624E008420FD2
          2) SAP LUW в программе, вызванной через SUBMIT AND RETURN — 12ED7F1F90410000E00624E008420FD3
          3) SAP LUW в первой программе по возвращению из вызываемой — 12ED7F1F90410000E00624E008420FD2 (тот же, как и в самом начале)

          CALL TRANSACTION
          Пример 1. В первой программе прямой INSERT, ФМ блокировки (рандомный), затем CALL TRANSACTION
          => Неявного DB Commit не произойдёт. Прямые обновления будут в БД только после того, как закончится выполнение транзакции,
          {если закончится; для текущего примера в CALL TRANSACTION была программа, в которой были только ФМ для установление блокировки
          и вызов метода cl_system_transaction_state=>get_sap_luw_key( ) для получения текущего ключа SAP LUW}
          и закончится выполнение основной программы

          Лог. блокировки таблиц (с помощью модулей блокировки) сохраяются в вызываемой транзакции и по возвращению в основную программу
          SAP LUW будет новый в вызванной через CALL TRANSACTION транзакции
          Но затем по возвращению в основную программу SAP LUW будет тот же, что и был изначально
          1) SAP LUW в начале основной программы — 12ED7F1F90410220E00624E1EBD5A6D9
          2) SAP LUW в транзакции, вызванной через CALL TRANSACTION — 12ED7F1F90410000E00624E008420FD3
          3) SAP LUW в первой программе по возвращению из вызываемой — 12ED7F1F90410220E00624E1EBD5A6D9 (тот же, как и в самом начале)

          1. Неявный коммит происходит ПО ОКОНЧАНИИ второй программы(вызванной через SUBMIT без RETURN), но не в момент вызова SUBMIT.

            Это можно проверить следующим образом:
            В основной программе делаем прямое обнвление БД.
            В текст вызываемой через SUBMIT(без RETURN) программы, добавляем ROLLBACK.

            Если неявный commit происходит в момент вызова SUBMIT(без RETURN), то изменения будут в БД. Т.к. ROLLBACK откатит уже новый DB LUW.

            Если же commit происходит после окончания вызванной программы, то данных в таблице не будет, т.к. пеоед эти отработает ROLLBACK

          2. В SAP Help явно прописано, что SUBMIT не открыват новый DB LUW, независимо от наличия RETRUN.
            В данном случае они не ошибаются)

  1. А есть аналог ассинхроного коммита для метода?
    Или проще в метод зашить вызов фм или перформ с соответствующими параметрами?

  2. Добрый день, коллеги!

    Подскажите, пожалуйста, почему V1 обрабатывается в отдельном рабочем процессе (отдельном SAP LUW)?
    Увидел на blogs.sap.com следующую статью — https://blogs.sap.com/2014/01/03/a-small-tip-of-class-clsystemtransactionstate/
    В данном гайде описаны результаты программы, цель которой — проверить Key SAP LUW в зависимости от способа запуска ФМ обновления (прямой вызов, с добавлением IN UPDATE TASK и в подпрограмме обновления PERFORM ON COMMIT) и сравнить Key SAP LUW в самом начале программы, во время запуска ФМ обновления, а также после выполнения COMMIT WORK AND WAIT в основной программе (т.е. по сути когда создался новый SAP LUW).
    Получили следующие результаты: Key SAP LUW в начале программы совпадает с Key SAP LUW в момент обработки ФМ обновлений, то есть после запуска COMMIT WORK AND WAIT, и только после того, как все зарегистрированные обновления после COMMIT WORK AND WAIT все отработают (или не отработают из-за внутренней ошибки), только в этот момент создаться новый SAP LUW (это если верить методу cl_system_transaction_state=>get_sap_luw_key( ) )
    Я у себя в локале сделал аналогичную программу, результаты на всех тестах такие же получились.
    То есть получается V1-модули (или если мы, например, сделали локальное обновление с помощью SET UPDATE TASK LOCAL) запускаются в том же SAP LUW что и основная программа, то есть в 1 рабочем процессе.
    Или под рабочим процессом что-то другое имелось в виду?
    Подскажите, пожалуйста) А то этот момент я немного не понял

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *