bg

Оптимизация массовых прогонов данных в SAP Cloud Application Studio

Это третья часть о фоновых прогонах в SAP Sales/Service Cloud. Это статья содержит информацию об оптимизации фоновых прогонов.

Основная проблема, с которой можно столкнуться, это время выполнения. Если в одном прогоне обрабатывается большой объем данных, то время выполнения растет очень быстро (не линейно). Время выполнения одного прогона ограничено 10 часами. Существуют различные способы решения данной проблемы. Давайте их рассмотрим.

Оптимизируйте код.

Можно использовать стандартное средство «Run Performance Check». Результаты проверки в некоторых случаях могут быть полезны. Данные результаты изображены на рисунке 1.

1.png

Рисунок 1 – Результаты выполнения «Run Performance Check»

 

В блоге SAP есть очень хорошая статья о повышении производительности и оптимизации кода. Я очень советую ее просмотреть и изучить: https://blogs.sap.com/2016/05/12/performance-best-practice-with-mass-enabled-event/.

Подытоженная информация из статьи:

  • Не перегружайте логикой стандартный события, особенно After-Modify. Это событие очень сильно влияет на производительность системы, так как вызывается каждый раз при обновлении каких-либо данных в выбранных данных. After-Modify может инициировать выполнение иных событий в других бизнес-объектах и может вести к длинным цепочкам вызовов. Где возможно, используйте событие Before-Save, вместо After-Modify;

  • Избегайте длинных цепочек переходов по ассоциациям. Сохраняйте промежуточные результаты в переменную и используйте значение из переменной;

  • Не используйте стандартные выборки (QueryByElements) для кастомных объектом, создавайте свои выборки;

  • Используйте Query.ExecuteDataOnly где возможно;

  • Избегайте вложенных циклов.

  • Используйте пакетную обработку данных

Про пакетную обработку данных:

Время выполнения прогонов растет не линейно, поэтому использование пакетной обработки один из лучших способов улучшить производительность. Основная идея — это разделение данных на пакеты небольшой размерности и обработка пакетов последовательно или параллельно.

Возможны две ситуации:

1)    Существуют параметры для выборки, по которым можно разделить данные на пакеты. Например, обновить поля в клиентах. Клиенты присвоены различным сбытовым организациям. Тогда можно создать MDR с параметром «Sales Organization ID». Затем, для каждой сбытовой организации следует создать отдельный прогон с заполненным полем «Sales Organization ID».

В итоге: следует создать отдельный прогон для каждого пакета из отобранных данных и запланировать прогоны параллельно или последовательно (picture 2). Данные в пакетах не должны пересекаться.

Примечание: очень желательно чтобы параметры разбивали данные в практически идентичные по размерам пакеты.

2.png

 

Рисунок 2 – Разделение на пакеты с использованием параметром

 

2)    Нет параметров, которые бы позволили разбить данные на пакеты. Рассмотрим тот же пример с обновлением полей в клиенте, но все клиенты будут присвоены одной сбытовой организации. Мы все еще можем воспользоваться решением, из пункта выше, если существуют параметры, которое бы позволили разбить всех клиентов на небольшие группы. Но если таких параметров нет, то можно сделать следующее:

1.        Фоновый прогон обрабатывает данные из одного пакета и перезапускает сам себя, используя ABSL для обработки следующего пакета. Данные будут обработаны последовательно. Для этого необходимо создать бизнес-объект, с полями: MDR id, start row, package size.

Бизнес-объект:

 

businessobject PackageProcessing {

[Label("Package ID")] element PackageID: ID; // Additional for selection

   [Label("MDR id")] element MDR_ID: ID;

[Label("Start row")] element StartRow: IntegerValue;

   [Label("Package size")] element PackageSize: NumberValue;

   action ProcessPackage;

}



 

Действие:

 

import ABSL;

import AP.Common.GDT;

import AP.FO.BusinessPartner.Global;

import AP.PlatinumEngineering; // For scheduling MDR from ABSL

 

// Const

var PACKAGE_SIZE = 200; // Default value for package size, can be reset

var MDR_NAME = "MDR_NAME"; // MDR object in studio, ex. MultifunctionalMDR

       var DELAY_SECONDS = 30;

 

// Set package size

       var packageSize;

       if(!this.PackageSize.IsInitial()){

             packageSize = this.PackageSize;

       }else{

             packageSize = PACKAGE_SIZE;

       }

 

       // Get some data from custom BO and sort by customer id

       var query = СustomBO.QueryByElements; // Custom query will be quicker

       var selParams = query.CreateSelectionParams();

       var sortParams = query.CreateSortingParams();

       sortParams.Add(СustomBO.CustomerID);

       // Get package, execute not from DB, as we need to change some data

       var result = query.Execute(selParams, sortParams, packageSize, this.StartRow);

 

 

       // Do something this package (data in variable "result")

       ...

 

       // Reschedule mass data run

       if(result.Count() > 0){

             // Set technical data for next iteration

             this.StartRow = this.StartRow + packageSize;

             // Additional: set selection parameter for MDR

// Needed if created in UI run had selection parameters

var queryParam: QueryParameter;

             queryParam.ParameterName = "PackageID";

             queryParam.Sign = "I";

             queryParam.Option = "EQ";

             queryParam.Low = this.PackageID;

             // Time parameters

             var currentTime = Context.GetCurrentGlobalDateTime();

             var delayDuration = Library::Duration.Create(0,0,0,0,0, DELAY_SECONDS);

             var mdrStartTime = currentTime.AddDuration(delayDuration);

             // Fill MDR data

var MDRO_ID : XPEString;

             MDRO_ID = this.MDR_ID;

             MDRO.AddSelectionParameter(MDR_NAME, "", "PackageID", MDRO_ID, queryParam);

             // Plan MDR

             MDRO.ExecuteDateTime(MDR_NAME, "", mdrStartTime, MDRO_ID);

            

       }

       // Reset start row

       else{

             this.StartRow = 0;

       }

 

Используя описанный выше подход можно эффективно обрабатывать данные по пакетам из кастомных объектов. Мы можем отбирать данные частями только для кастомных объектов. Но для стандартных объектов аналогичное выполнить не получится, так как выборки стандартных объектов не содержат параметров для сортировки (sorting parameters).

Действие в таком случае будет выглядеть примерно так:

 

import ABSL;

import AP.Common.GDT;

import AP.FO.BusinessPartner.Global;

import AP.PlatinumEngineering; // For scheduling MDR from ABSL

 

// Const

var PACKAGE_SIZE = 200; // Default value for package size, can be reset

var MDR_NAME = "MDR_NAME"; // MDR object in studio, ex. MultifunctionalMDR

       var DELAY_SECONDS = 30;

       var STATUS_ACTIVE = "2";

 

// Set package size

       var packageSize;

       if(!this.PackageSize.IsInitial()){

             packageSize = this.PackageSize;

       }else{

             packageSize = PACKAGE_SIZE;

       }

      

// Set start and end

var start = this.StartRow;

var end = start + packageSize;

 

// Customers query

var query = Customer.QueryByBusinessObjectCustomer;

var selParams = queryCustomer.CreateSelectionParams();

selParams.Add(query.LifeCycleStatusCode, "I", "EQ", STATUS_ACTIVE);

var result = query.ExecuteDataOnly(selParams).OrderBy(n => n.InternalID);

 

var counterCustomers = 0;

foreach(var customer in result){        

              // 1...n, not 0...n as in index array

              counterCustomers = counterCustomers + 1;

              // Skip processed records

              if(counterCustomers < start){

                    continue;

              }

              // Stop as needed package was processed or processed last record

              if(counterCustomers >= end || counterCustomers == resultCustomer.Count()){

                    break;

             }

             // Retrieve

var customerInstance = Customer.Retrieve(customer.InternalID);

 

// Do something this package data

             ...

 

 

}

 

       // Reschedule mass data run

       if(result.Count() > 0 && result.Count() end > 0){

             // Reschedule in the same way

       }

// Reset start row

       else{

             this.StartRow = 0;

       }

 

      

Отбор данных реализован иначе, так как мы отбираем все данные, а затем проходим по ним в поиске нужного пакета. Но для большого объема данных, такой подход позволит нам получить выигрыш по скорости, по сравнению с единовременной обработкой всех данных в одном прогоне (а также можно не уложиться потенциально в 10 часов времени на прогон).

          Подытожим:

-       Пакетная обработка позволяет обрабатывать данные быстрее и эффективнее;

-       Очень низкая вероятность достигнуть десятичасового лимита по времени, так как каждый пакет обрабатывается в отдельном прогоне;

-       Все строки из БД будут обработаны (прогон сам себя перезапустит, если есть еще данные);

-       Данные будут обработаны последовательно;

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

2.        Создайте N прогонов для обработки данных в определенных интервалах. Количество прогонов: количество строк в БД по обрабатываемому объекту разделить на размер пакета. Данные в пакетах могут обрабатываться параллельно или последовательно, в зависимости от того, как прогоны будут запланированы. То есть, это  обработка с использованием параметров, которыми являются стартовые строки и размеры пакетов (на рисунок 3).

3.png

Рисунок 3 – Разделение на пакеты с использованием начальной строки и размера пакета

 

Бизнес-объект будет иметь ту же структуру, как и в пункте 2.1.

Действие – структура также аналогична, но необходимо убрать автоматическое перепланирование.

        Подытожим: 

-     Все прогоны должны планироваться вручную;

-     Для каждого пакета необходимо создать и запланировать отдельный прогон;

-     При появлении новых записей в БД необходимо создавать новые прогоны и планировать их;

-     Возможность частичной обработки данных (только запланированные пакеты), обработку отдельных пакетов можно перезапустить;

-     Прогоны можно запланировать последовательно или параллельно.

 

Спасибо за внимание!