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

Есть конечно штатное копирование, но оно нам по ряду причин не подходило, поэтому разработал своё. Ниже опишу вкратце характеристики:
Выполняется по RFC без всяких промежуточных файлов и таблиц.
Запускается из системы источника данных.
Копируются только манданто-зависимые таблицы.
Если структура таблиц между системами отличается – копируются поля имеющиеся в обоих системах.

У нас оно запускается периодическим фоновым заданием в субботу ночью, за 10-12 часов копируется продуктив в тест.

Понадобится RFC соединение с сохранённым паролем, или доверительное соединение.

Запускается программой YDK_RFC_TABDATA_TRANSFER_RN
YDK_RFC_TABDATA_TRANSFER_RN

«Имя таблицы» если не указать – будут выбраны для обработки все манданто-зависимые таблицы
«Количество потоков» — указываем количество потоков. В системе отправителе будет запущенно в фоне указанное количество программ YDK_RFC_TABDATA_TRANSFER_TH, в целевой системе работу потоков можно отслеживать через SM51
«Зачистить таблицу перед загрузкой» — по умолчанию установлена, если установлено в целевой системе данные из таблицы сначала удаляются (для некоторых таблиц требуется значительное время), а затем выполняется загрузка данных из системы источника. Если эту галку не устанавливать — в целевой системе могут остаться записи с старыми данными
«Игнорировать режим обработки» — в YDK_TRANSFER_TAB- CMODE может быть указан режим обработки таблицы: «Не копировать», «Не отчищать таблицу», «Копировать если изменилось количество записей». Эта галка позволяет игнорировать эту настройку.
«Метка таблицы» — проверяет значение в поле YDK_TRANSFER_TAB-LABL, можно использовать для отбора определённых таблиц
«Таблицы с режимом обработки» — проверяет значение поля YDK_TRANSFER_TAB- CMODE
«Статус последней обработки» — например позволяет отобрать таблицы при обработке которых возникли ошибки, и таким образом обработать их ещё раз.
«Количество записей в посл. обр», «Продолжительность обработки» — отбор по статистике собранной по таблице в предыдущей передаче данных.

Алгоритм

ReportКраткое описание функциональности
YDK_RFC_TABDATA_TRANSFER_RNЗапуск процесса копирования данных, подготавливает список таблиц подлежащих копированию и запускает в фоне один или несколько процессов YDK_RFC_TABDATA_TRANSFER_TH
YDK_RFC_TABDATA_TRANSFER_THПоток выполняющий копирование данных запускается в фоне из YDK_RFC_TABDATA_TRANSFER_RN, проходит по списку таблиц подготовленному в YDK_RFC_TABDATA_TRANSFER_RN, для каждой таблицы вызывает YDK_RFC_TABDATA_TRANSFER. Для исключения обработки одной и той же таблицы несколькими потоками выставляются блокировки и статусы в таблице YDK_TRANSFER
YDK_RFC_TABDATA_TRANSFERЗапускается из YDK_RFC_TABDATA_TRANSFER_TH для указанной одной таблицы, открывает соединение и выполняет копирование данных таблицы, результат обработки возвращает через Export to memory
YDK_RFC_TABDATA_TRANSFER_RPОтчёт по текущему состоянию/результату копирования

Таблица YDK_TRANSFER_TAB — Настройка копирования таблиц

ПолеКлючТипДлинаОписание
MANDTXCLNT3Мандант
TABNAMEXCHAR30Имя таблицы
LABLCHAR10Метка для отбора таблиц
CMODECHAR2Режим обработки таблицы, значения:
" " - Не установлен
"SK" - Не копировать
"CL" - Не отчищать таблицу
"СС" - Копировать если изменилось количество записей

Таблица YDK_TRANSFER — Лог копирования таблиц. (в логе хранится только последняя обработка таблицы)

ПолеКлючТипДлинаОписание
MANDTXCLNT3Мандант
TABNAMEXCHAR30Имя таблицы
RFCDESTXCHAR32Логический адрес назначения (указывается при вызове функции)
ROWSIZEINT410Размер записи
ODATEDATS8Дата изменеия каличества
OCOUNTINT410Успешо переданное изменённое количество
ODURATIONINT410Длительность обработки в сек. (изменение каличества)
LDATEDATS8Дата последней обработки
LTIMETIMS6Время запуска обработки
LCOUNTINT410Количество записей в последней обработке
ERRCHAR132Сообщение об ошибке
STATUSCHAR1Статус обработки, значение:
"I" - Предстоит обработка
"O" - Выполнена успешно
"E" - Ошибка при обработке
"P" - Выполняется в данный момент
"S" - Не выполнялось
"Z" - Требуется пост-обработка таблицы
SDATEDATS8Дата начала обработки
STIMETIMS6Время начала обработки
ETIMETIMS6Время конца обработки
DURATIONINT410Длительность обработки в сек.

Интересные моменты:
Некоторые таблицы имеют очень большие размеры что существенно усложняет поставленную задачу:

Не возможно передать весь объём данных таблицы за один RFC вызов, поэтому надо данные передавать пакетами, что собственно не сложно организовать но RFC вызов вызывает неявный DB commit который рвёт выборку данных. Было протестировано множество вариантов, в конце концов в документации было найдено дополнение для call function  KEEPING LOGICAL UNIT OF WORK «This addition is for internal use only. When this addition is used incorrectly, the worst case scenario may be a system shutdown, использование этого дополнения решило эту проблему, никаких проблем описанных в предупреждении не разу не возникало.

Перед сохранением данных в целевом манданте, в целевой таблице надо удалить старые данные. Однако если таблица большая нельзя просо вызвать delete from <tabname>  поскольку не хватит объёма roll back сегмента для отката транзакции и в результате дамп, поэтому удалять данные надо порциями с commit work между ними. Но к сожалению в синтаксисе delete from <tabname>  нельзя указать сколько записей надо удалить, однако можно удалить используя  таблицу с ключами полей  DELETE (tabname) FROM TABLE <keytab>. Таким образом алгоритм такой: в цикле выбираем  пачку ключевых полей в таблицу <keytab> затем удаляем их и так до тех пор пока в таблице не останется записей

Дополнение OPEN CURSOR WITH HOLD позволяет делать CALL FUNCTION ‘DB_COMMIT’ не прерывая выборку, если его не использовать при вызове DB_COMMIT курсор SQL  запроса автоматически закроется. Однако это дополнение не срабатывает при RFC вызове… курсор всё равно закрывается.

Ещё одно замечание:  как было выше написано «Если структура таблиц между системами отличается – копируются поля имеющиеся в обоих системах», для того чтоб это реализовать надо при загрузке данных сравнить структуру таблицы источника с таблицей приёмником, и если она отличается для переноса полей использовать move corresponding. Сначала для этих целей я хотел использовать  классы cl_abap_datadescr=>describe_by_data и cl_abap_structdescr=>create( fct ) однако при тестировании системы оказалось, что работает это не удовлетворительно,  поэтому я перешёл на классы cl_salv_bs_ddic=>get_components_by_data( <stab> ) и cl_salv_bs_ddic=>create_data_from_components. Насколько я помню (давно это было) проблема была связана с точностью описания и воспроизведения сложных структур – вложенные структуры/инклюды и т.п. правда и cl_salv_bs_ddic не безгрешен он спотыкается на полях в имени которых присутствует символ «-» но таких таблиц крайне мало.

Ну и напоследок:
Скопировать данные таблиц не достаточно, что в общем то было ожидаемо, требуется сделать ещё кое какие операции:

После копирования таблицы диапазонов номеров NRIV (и не только) необходимо сделать сброс буфера диапазона номеров , иначе номера будут выдаваться из сделанного буфера, что может привести duplicate record insert. При копировании этой таблицы, сброс выполняется автоматически, но может потребоваться сделать это в ручную, делается это транзакцией SM56 затем кнопка F5.

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

Установка:
Архив с транспортными файлами запроса на перенос
файл для SAPLink

ФМ для построения каталога ALV

Довольно часто встречающейся заботой ABAP программиста — является настройка каталога полей для ALV.

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

Однако я регулярно вижу код и SAP и различных сторонних разработчиков где каталог полей набивается как угодно, чуть ли не руками (append-ы, всякие макросы, loop с настройкой и т.п.), да и я порой тоже так делал. Мне это надоело и я решил решить эту проблему.

Написал небольшую группу функций решающую эту проблему (и не только) которой хочу с вами поделиться: Читать далее ФМ для построения каталога ALV