ABAP Blog Posts
cancel
Showing results for 
Search instead for 
Did you mean: 
Bikash_R
Participant
274

Introduction:

During my SAP ABAP development experience, I have worked on reports and interfaces that process large volumes of business data. One challenge I frequently noticed is that even well-written ABAP programs can become slow when they process thousands of records sequentially. As data volume grows, runtime increases significantly, affecting overall system performance and the user experience.

While exploring different performance optimization techniques, I came across the SPTA (SAP Parallel Task Administration) framework. What interested me most was that SAP already provides a standard mechanism to execute workloads in parallel instead of requiring developers to build a custom parallel processing solution from scratch. The framework takes care of distributing work packets, managing parallel tasks, utilizing available work processes, and collecting the results after processing is complete.

To better understand how SPTA works internally, I built a simple prototype instead of directly using a business scenario. The example generates random data, splits it into multiple work packets, processes each packet in parallel, and finally combines the results. This hands-on exercise helped me understand the complete flow of parallel processing and how the framework manages task execution behind the scenes.

In this blog, I will walk through the same example step by step, explain the key components involved, and share the learnings I gained while implementing the SPTA framework. I hope this practical approach helps anyone who is getting started with parallel processing in SAP ABAP.

What is SPTA Framework?

SPTA is a standard SAP framework designed to execute independent units of work in parallel. It internally manages: 

  • Allocation of available work processes 
  • Load balancing across application servers 
  • Asynchronous RFC execution 
  • Work packet management 
  • Error handling and task synchronization 
  • Collection of results after task completion 

Instead of manually creating asynchronous RFC tasks using STARTING NEW TASK, developers can leverage the SPTA framework to simplify implementation and improve maintainability.

Solution Overview- 

The program follows the below execution flow:

Bikash_R_0-1782990587165.png

The framework repeatedly executes these callback routines until all records are processed.

TYPE-POOLS: spta.
TYPES: BEGIN OF ty_final,
        number TYPE char3,
        index TYPE char3,
       END OF ty_final.

TYPES: BEGIN OF ztest_random_data,
         number TYPE char3,
         index  TYPE char3,
       END OF ztest_random_data.

TYPES ztest_random_data_t TYPE STANDARD TABLE OF ztest_random_data
                          WITH EMPTY KEY.
DATA:
      it_random TYPE ztest_random_data_t,
      it_final TYPE STANDARD TABLE OF ty_final,
      v_max_no_of_tasks TYPE syindex.

* Create random numbers
PERFORM f_create_random_no.
* Write random numbers before calling parallel processing
PERFORM f_write_random_rec.

 "Fetch the server group
  SELECT SINGLE classname
  INTO @DATA(lv_server_grp)
  FROM rzllitab
  WHERE grouptype = 'S'.

  IF sy-subrc NE 0.
    RETURN.
  ENDIF.

* Calling the function module to invoke parallel processing
* framwork

CALL FUNCTION 'SPTA_PARA_PROCESS_START_2'
  EXPORTING
    server_group             = lv_server_grp
    before_rfc_callback_form = 'BEFORE_RFC'
    in_rfc_callback_form     = 'IN_RFC'
    after_rfc_callback_form  = 'AFTER_RFC'
    callback_prog            = 'ZTEST_SPTA_FRAME_WORK'
  CHANGING
    user_param               = it_random
  EXCEPTIONS
    invalid_server_group     = 1
    no_resources_available   = 2
    OTHERS                   = 3.

*&---------------------------------------------------------------------*
*&      Form  BEFORE_RFC
*&---------------------------------------------------------------------*
* In this routine work packets are prepared for parallel processing   *
* also check the variable for downstream processing                    *
*----------------------------------------------------------------------*
FORM before_rfc
    USING    is_before_rfc_imp     TYPE spta_t_before_rfc_imp
    CHANGING es_before_rfc_exp     TYPE spta_t_before_rfc_exp
             it_rfcdata            TYPE spta_t_indxtab
             ct_failed_objects     TYPE spta_t_failed_objects
             et_objects_in_process TYPE spta_t_objects_in_process
             it_data               TYPE ztest_random_data_t.

  DATA: l_counter     TYPE i,
        l_no_of_items TYPE i,
        it_final TYPE ztest_random_data_t,
        wa_data TYPE ztest_random_data.

* Create small work packets for parallel processing
* This is custom logic
  l_no_of_items = 10.    " One work packet will contain 10 records

  LOOP AT it_data INTO wa_data.
* Read and move specified number of records
    IF ( l_counter < l_no_of_items
      AND wa_data IS NOT INITIAL ).
      APPEND wa_data TO it_final.
      DELETE it_data INDEX 1.
      l_counter = l_counter + 1.
    ELSE.
      EXIT.
    ENDIF.
  ENDLOOP.

* Convert the input data into the INDX structure that is needed for the RFC
  CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE'
    EXPORTING
      data    = it_final
    IMPORTING
      indxtab = it_rfcdata.

* This is mandatory step
* inform task manager that an rfc can be started from the data compiled
  IF it_final IS NOT INITIAL.
    es_before_rfc_exp-start_rfc = 'X'.
  ELSE.
    CLEAR es_before_rfc_exp-start_rfc.
    RETURN.
  ENDIF.

ENDFORM.                    " BEFORE_RFC_CALLBACK_FORM
*&---------------------------------------------------------------------*
*&      Form  IN_RFC
*&---------------------------------------------------------------------*
*  In this routine create own RFC enabled FM or write logic in this    *
*  routine                          *
*----------------------------------------------------------------------*
FORM in_rfc
         USING is_in_rfc_imp         TYPE spta_t_in_rfc_imp "#EC NEEDED
         CHANGING es_in_rfc_exp      TYPE spta_t_in_rfc_exp
                  it_rfcdata         TYPE spta_t_indxtab.

  DATA: it_final TYPE ztest_random_data_t.

* Decode the data from the INDX Structure into the process work list
  CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE'
    EXPORTING
      indxtab = it_rfcdata
    IMPORTING
      data    = it_final.

* Simple logic to sort data
  SORT it_final BY number.

* Again Encode output data for AFTER_RFC form
  CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE'
    EXPORTING
      data    = it_final
    IMPORTING
      indxtab = it_rfcdata.

ENDFORM.                    "in_rfc
*&---------------------------------------------------------------------*
*&      Form  AFTER_RFC
*&---------------------------------------------------------------------*
* In this routine we have to collect date after parallel processing    *
*----------------------------------------------------------------------*
FORM after_rfc
     USING it_rfcdata            TYPE spta_t_indxtab
           if_rfcsubrc           TYPE sy-subrc
           if_rfcmsg             TYPE spta_t_rfcmsg
           it_objects_in_process TYPE spta_t_objects_in_process "#EC NEEDED
           is_after_rfc_imp      TYPE spta_t_after_rfc_imp  "#EC NEEDED
  CHANGING es_after_rfc_exp      TYPE spta_t_after_rfc_exp  "#EC NEEDED
           cs_user_param         TYPE ztest_random_data_t.

  DATA: wa_final TYPE ztest_random_data.

* Decode RFC output data and add RFC-results to global data
  CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE'
    EXPORTING
      indxtab = it_rfcdata
    IMPORTING
      data    = it_final.

* After parallel processing write all the records
  LOOP AT it_final INTO wa_final.
    WRITE:/ wa_final-number, 10 wa_final-index.
    CLEAR wa_final.
  ENDLOOP.
  WRITE: sy-uline.

ENDFORM.                    "after_rfc
*&---------------------------------------------------------------------*
*&      Form  F_CREATE_RANDOM_NO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*  -->  p1        text
*  <--  p2        text
*----------------------------------------------------------------------*
FORM f_create_random_no.

  DATA: lcl_random TYPE REF TO cl_abap_random_int,
        l_index TYPE sytabix,
        lwa_data TYPE ztest_random_data,
        l_min TYPE i,
        l_max TYPE i.
  l_min = 10.
  l_max = 90.
  TRY.
      lcl_random = cl_abap_random_int=>create( min = l_min
                                               max = l_max ).
    CATCH cx_abap_random.
  ENDTRY.

  DO 100 TIMES.
*   Get the Random Number:
    lwa_data-number = lcl_random->get_next( ).
    l_index = l_index + 1.
    lwa_data-index = l_index.
    APPEND lwa_data TO it_random.
  ENDDO.

ENDFORM.                    " F_CREATE_RANDOM_NO
*&---------------------------------------------------------------------*
*&      Form  F_WRITE_RANDOM_REC
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*  -->  p1        text
*  <--  p2        text
*----------------------------------------------------------------------*
FORM f_write_random_rec.

  DATA: wa_final TYPE ztest_random_data.
  WRITE:/ 'Before parallel processing start'.
  LOOP AT it_random INTO wa_final.
    WRITE:/ wa_final-number, 10 wa_final-index.
    CLEAR wa_final.
  ENDLOOP.
  WRITE:/ 'After end parallel processing'.

ENDFORM.                    " F_WRITE_RANDOM_REC*&---------------------------------------------------------------------*

Generate Sample Data-

FORM f_create_random_no. 
 
  DATA: lcl_random TYPE REF TO cl_abap_random_int, 
        l_index TYPE sytabix, 
        lwa_data TYPE ztest_random_data, 
        l_min TYPE i, 
        l_max TYPE i. 
  l_min = 10. 
  l_max = 90. 
 
  TRY. 
      lcl_random = cl_abap_random_int=>create( min = l_min 
                                               max = l_max ). 
    CATCH cx_abap_random. 
  ENDTRY. 
 
  DO 100 TIMES. 
*   Get the Random Number: 
    lwa_data-number = lcl_random->get_next( ). 
    l_index = l_index + 1. 
    lwa_data-index = l_index. 
    APPEND lwa_data TO it_random. 
  ENDDO. 
 
ENDFORM.                    " F_CREATE_RANDOM_NO

This routine generates 100 random records and stores them in IT_RANDOM. 

Example: 

NUMBER   INDEX 
------------------------
45                  1 
72                  2 
18                 3 
63                 4

At this point, all records are available for processing.

Determine Server Group- 

 "Fetch the server group 
  SELECT SINGLE classname 
  INTO @DATA(lv_server_grp) 
  FROM rzllitab 
  WHERE grouptype = 'S'.

  IF sy-subrc NE 0. 
    RETURN. 
  ENDIF.

The SPTA framework requires an RFC Server Group. 

Server groups are configured in transaction: RZ12 

The framework uses this server group to determine where parallel tasks can run.

Start the SPTA Framework-

* Calling the function module to invoke parallel processing 
* framwork 
CALL FUNCTION 'SPTA_PARA_PROCESS_START_2' 
  EXPORTING 
    server_group             = lv_server_grp 
    before_rfc_callback_form = 'BEFORE_RFC' 
    in_rfc_callback_form     = 'IN_RFC' 
    after_rfc_callback_form  = 'AFTER_RFC' 
    callback_prog            = 'ZTEST_SPTA_FRAME_WORK' 
  CHANGING 
    user_param               = it_random 
  EXCEPTIONS 
    invalid_server_group     = 1 
    no_resources_available   = 2 
    OTHERS                   = 3.

This is the entry point of the framework. 

The entire table IT_RANDOM is passed through: 

user_param = it_random 

Internally, SPTA passes this table to BEFORE_RFC, where work packets are created. 

The framework then repeatedly executes: 

  1. BEFORE_RFC 
  1. IN_RFC 
  1. AFTER_RFC 

Until all records have been processed.

 BEFORE_RFC: Create Work Packets-

This callback prepares the data packets that will be processed in parallel.

*&---------------------------------------------------------------------* 
*&      Form  BEFORE_RFC 
*&---------------------------------------------------------------------* 
* In this routine work packets are prepared for parallel processing   * 
* also check the variable for downstream processing                    * 
*----------------------------------------------------------------------* 
FORM before_rfc 
    USING    is_before_rfc_imp     TYPE spta_t_before_rfc_imp 
    CHANGING es_before_rfc_exp     TYPE spta_t_before_rfc_exp 
             it_rfcdata            TYPE spta_t_indxtab 
             ct_failed_objects     TYPE spta_t_failed_objects 
             et_objects_in_process TYPE spta_t_objects_in_process
             it_data               TYPE ztest_random_data_t. 
 
  DATA: l_counter     TYPE i, 
        l_no_of_items TYPE i, 
        it_final TYPE ztest_random_data_t, 
        wa_data TYPE ztest_random_data. 
 
* Create small work packets for parallel processing 
* This is custom logic 
  l_no_of_items = 10.    " One work packet will contain 10 records 
  LOOP AT it_data INTO wa_data. 
* Read and move specified number of records 
    IF ( l_counter < l_no_of_items 
      AND wa_data IS NOT INITIAL ). 
      APPEND wa_data TO it_final. 
      DELETE it_data INDEX 1. 
      l_counter = l_counter + 1. 
    ELSE. 
      EXIT. 
    ENDIF. 
  ENDLOOP. 
 
* Convert the input data into the INDX structure that is needed for the RFC 
  CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE' 
    EXPORTING 
      data    = it_final 
    IMPORTING 
      indxtab = it_rfcdata. 
 
* This is mandatory step 
* inform task manager that an rfc can be started from the data compiled 
  IF it_final IS NOT INITIAL. 
    es_before_rfc_exp-start_rfc = 'X'. 
  ELSE. 
    CLEAR es_before_rfc_exp-start_rfc. 
    RETURN. 
  ENDIF. 
 
ENDFORM.                    " BEFORE_RFC_CALLBACK_FORM 

Each work packet contains 10 records. 

For 100 records: 

Packet 1 -> Records 1 to 10 
Packet 2 -> Records 11 to 20 
Packet 3 -> Records 21 to 30 
... 
Packet 10 -> Records 91 to 100

The framework passes the remaining data in IT_DATA. 

This logic: 

  • Copies 10 records into IT_FINAL 
  • Removes them from IT_DATA 
  • Leaves the remaining records for the next callback execution 

Example: 

Before: 

IT_DATA = 100 records 

After first packet: 

IT_FINAL = 10 records 
IT_DATA  = 90 records

Convert Packet into RFC Format-

Bikash_R_0-1781759737806.png

SPTA transfers data between work processes using INDX structures. 

This function serializes the packet into a format suitable for RFC communication.

Signal Framework to Start Processing-

Bikash_R_1-1781759772102.png

This informs the framework that a work packet is ready, and a new RFC task can be started.

IN_RFC: Execute Business Logic- 

This routine runs in a separate work process.

*&---------------------------------------------------------------------* 
*&      Form  IN_RFC 
*&---------------------------------------------------------------------* 
*  In this routine create own RFC enabled FM or write logic in this    * 
*  routine                          * 
*----------------------------------------------------------------------* 
FORM in_rfc 
         USING is_in_rfc_imp         TYPE spta_t_in_rfc_imp "#EC NEEDED 
         CHANGING es_in_rfc_exp      TYPE spta_t_in_rfc_exp
                  it_rfcdata TYPE spta_t_indxtab. 

  DATA: it_final TYPE ztest_random_data_t. 
 
* Decode the data from the INDX Structure into the process work list 
  CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE' 
    EXPORTING 
      indxtab = it_rfcdata 
    IMPORTING 
      data    = it_final. 
 
* Simple logic to sort data 
  SORT it_final BY number. 
 
* Again Encode output data for AFTER_RFC form 
  CALL FUNCTION 'SPTA_INDX_PACKAGE_ENCODE' 
    EXPORTING 
      data    = it_final 
    IMPORTING 
      indxtab = it_rfcdata. 
 
ENDFORM.                    "in_rfc

Decode Packet-

Bikash_R_2-1781760037006.png

The encoded packet is converted back into an internal table -> process data -> then packet is sorted by the random number. 

Encode Results-

Bikash_R_3-1781760058335.png

The processed data is encoded again so that it can be returned to the caller. 

AFTER_RFC: Collect Results- 

Once a parallel task is complete, the framework invokes AFTER_RFC.

*&---------------------------------------------------------------------* 
*&      Form  AFTER_RFC 
*&---------------------------------------------------------------------* 
* In this routine we have to collect date after parallel processing    * 
*----------------------------------------------------------------------* 
FORM after_rfc 
     USING it_rfcdata            TYPE spta_t_indxtab 
           if_rfcsubrc           TYPE sy-subrc 
           if_rfcmsg             TYPE spta_t_rfcmsg 
           it_objects_in_process TYPE spta_t_objects_in_process "#EC NEEDED 
           is_after_rfc_imp      TYPE spta_t_after_rfc_imp  "#EC NEEDED 
  CHANGING es_after_rfc_exp      TYPE spta_t_after_rfc_exp  "#EC NEEDED 
           cs_user_param         TYPE ztest_random_data_t. 
 
  DATA: wa_final TYPE ztest_random_data. 
 
* Decode RFC output data and add RFC-results to global data 
  CALL FUNCTION 'SPTA_INDX_PACKAGE_DECODE' 
    EXPORTING 
      indxtab = it_rfcdata 
    IMPORTING 
      data    = it_final.
 
* After parallel processing write all the records 
  LOOP AT it_final INTO wa_final. 
    WRITE:/ wa_final-number, 10 wa_final-index. 
    CLEAR wa_final. 
  ENDLOOP. 
  WRITE: sy-uline. 
 
ENDFORM.                    "after_rfc 

Decode Returned Data-

Bikash_R_4-1781760146918.png

The processed packet is reconstructed -> The processed records are displayed.

Example-

Suppose the program generates:

45    1  
81    2  
23    3  
67    4  
... 

The framework creates:

Packet 1 -> 10 Records 
Packet 2 -> 10 Records 
Packet 3 -> 10 Records 
... 
Packet10 -> 10 Records

These packets are distributed among available work processes:

WP1 -> Packet 1 
WP2 -> Packet 2 
WP3 -> Packet 3 
WP4 -> Packet 4 

Each work process executes the logic inside IN_RFC independently and returns the results through AFTER_RFC.

Let’s execute and see--

Bikash_R_5-1781760342856.png

Bikash_R_6-1781760366731.png

Bikash_R_7-1781760395137.png

Bikash_R_8-1781760413954.png

Advantages of SPTA Framework- 

  • Standard SAP framework for parallel processing 
  • Automatic workload distribution 
  • Better utilization of available work processes 
  • Reduced execution time 
  • Simplified task management 
  • Improved scalability 
  • Centralized result collection 

Conclusion- 

The SPTA framework provides a robust and SAP-standard approach for implementing parallel processing in ABAP. By splitting large datasets into smaller work packets and distributing them across multiple work processes, developers can significantly improve application performance while keeping the implementation manageable. 

In this example, we generated random data, divided it into packets of 10 records, processed those packets in parallel, and collected the results using the three core callback routines: 

  • BEFORE_RFC – Creates work packets 
  • IN_RFC – Executes business logic in parallel 
  • AFTER_RFC – Collects and consolidates results 

For real-world scenarios involving mass data processing, SPTA offers a scalable and efficient solution that aligns with SAP development best practices.

3 Comments