This is a test message to test the length of the message box.
Login
ABAP Cloud Background Processing
Created by Software-Heroes

ABAP Cloud - Background processing

1621

How do you actually outsource memory-intensive processes to the background and monitor them in ABAP Cloud? In this article, we take a closer look at the framework.



In this article we will look at bgPF and how you can use it in ABAP Cloud to move processes to the background. We will use an example to look at how it works in detail and the two variants that are offered for processes.

 

Introduction

In processes, it can happen that the actual process has already been completed (creation of a document or a booking) and then certain additional processes should run. Such processes can be, for example: collecting statistics, creating emails, evaluations or initiating follow-up processes. Such additional processes usually cost runtime and prevent the user from using the UI to continue working normally (loading bar, hourglass, etc.).

For such cases, there are background processes that are initiated and then run independently. SAP therefore introduced the Background Processing Framework, or bgPF for short. Processing in background processes is not new, but processing without its own function module was not common until now. Also, you don't have to worry about managing the process.

If you would like to find out more about how bgPF works, we recommend the SAP Community article linked below, where the process in the system is shown schematically again.

 

Preparation

In our examples, we will use a table for logging so that we can better understand the actions in the system. The table is defined as follows:

define table zbs_dmo_bgpf {
  key client     : abap.clnt not null;
  key identifier : sysuuid_x16 not null;
  description    : abap.char(60);
  inumber        : abap.int4;
  @Semantics.amount.currencyCode : 'zbs_dmo_bgpf.currency'
  amount         : abap.curr(15,2);
  currency       : abap.cuky;
  created_at     : abap.utclong;
  created_from   : abap.char(12);
}

 

To do this, we set a data processing class on top of it that takes care of filling the lines so that we can focus on the actual framework in the examples.

CLASS zcl_bs_demo_bgpf_data DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES ts_data TYPE zbs_dmo_bgpf.
    TYPES tt_data TYPE STANDARD TABLE OF ts_data WITH EMPTY KEY.

    METHODS add
      IMPORTING is_data TYPE ts_data.

    METHODS save
      IMPORTING id_commit TYPE abap_bool.

  PRIVATE SECTION.
    DATA mt_data TYPE tt_data.
ENDCLASS.


CLASS zcl_bs_demo_bgpf_data IMPLEMENTATION.
  METHOD add.
    WAIT UP TO 1 SECONDS.

    DATA(ls_data) = is_data.
    ls_data-identifier   = cl_system_uuid=>create_uuid_x16_static( ).
    ls_data-created_from = cl_abap_context_info=>get_user_alias( ).
    ls_data-created_at   = utclong_current( ).

    INSERT ls_data INTO TABLE mt_data.
  ENDMETHOD.


  METHOD save.
    INSERT zbs_dmo_bgpf FROM TABLE @mt_data.

    IF id_commit = abap_true.
      COMMIT WORK.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

 

The class is kept quite simple, we can use the ADD method to write new records to the internal buffer, and the SAVE method then writes the records to the database. So that we can see the changes better later, there is a WAIT in the ADD method to slow down the process.

 

Variants

The framework currently offers two different variants of processes that are used for execution. If you are using the ABAP RESTful Programming Model, you should use the "Controlled" variant; for all other use cases, it is best to stick with "Uncontrolled".

 

 

Variant - Uncontrolled

The uncontrolled variant works without any additional restrictions and we can do whatever we need for our work. To do this, we implement the interface IF_BGMC_OP_SINGLE_TX_UNCONTR in the class, which provides the EXECUTE method. This is later executed during processing.

 

Process

The class with the interface IF_BGMC_OP_SINGLE_TX_UNCONTR will later be our process that runs in the background. We should therefore make all the data required from our currently running process available to the object.

CLASS zcl_bs_demo_bgpf_process_uncon DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_bgmc_op_single_tx_uncontr.

    METHODS constructor
      IMPORTING it_data TYPE zcl_bs_demo_bgpf_data=>tt_data.

  PRIVATE SECTION.
    DATA mt_data TYPE zcl_bs_demo_bgpf_data=>tt_data.
ENDCLASS.


CLASS zcl_bs_demo_bgpf_process_uncon IMPLEMENTATION.
  METHOD constructor.
    mt_data = it_data.
  ENDMETHOD.


  METHOD if_bgmc_op_single_tx_uncontr~execute.
    DATA(lo_table) = NEW zcl_bs_demo_bgpf_data( ).

    LOOP AT mt_data INTO DATA(ls_data).
      lo_table->add( ls_data ).
    ENDLOOP.

    lo_table->save( abap_true ).
  ENDMETHOD.
ENDCLASS.

 

In this example, the processing consists of taking the transferred data records and persisting them to the database using the SAVE method with COMMIT WORK.

 

Execution

If we now want to execute our process, we can do this as follows:

  • Creating the operation (our process object)
  • Creating a process using the factory
  • Setting the process name
  • Passing the operation using the SET_OPERATION_TX_UNCONTROLLED method
  • Saving for execution
  • COMMIT for Execution of the process

 

DATA lo_operation TYPE REF TO if_bgmc_op_single_tx_uncontr.

DATA(lt_data) = VALUE zcl_bs_demo_bgpf_data=>tt_data(
    ( description = 'Test 1' inumber = 12 amount = '12.54' currency = 'EUR' )
    ( description = 'Test 2' inumber = 95 amount = '0.21' currency = 'USD' )
    ( description = 'Test 3' inumber = 547 amount = '145.50' currency = 'EUR' ) ).

lo_operation = NEW zcl_bs_demo_bgpf_process_uncon( lt_data ).

DATA(lo_process) = cl_bgmc_process_factory=>get_default( )->create( ).
lo_process->set_name( 'Uncontrolled Process' )->set_operation_tx_uncontrolled( lo_operation ).
lo_process->save_for_execution( ).

COMMIT WORK.

 

Execution only begins when COMMIT WORK is set. If a ROLLBACK is performed due to an error, our process does not start either. This is to ensure that the process is only executed if it is successful.

 

Variant - Controlled

The difference to the uncontrolled variant is that here the RAP phases are run through, which can be controlled via the CL_ABAP_TX class. This means we have an interaction phase and the storage sequence, which we can map in our process. In this case, the class is implemented with the interface IF_BGMC_OP_SINGLE, but the EXECUTE method is also available here.

 

Process

Again, we accept the data in the process and save it for execution. During processing, we can optionally switch to the interaction phase (MODIFY) and accept the data. Since we would actually be working with RAP here, we would send our EML statement. Finally, we switch to the storage sequence and can also send statements such as INSERT or UPDATE to the database.

CLASS zcl_bs_demo_bgpf_process_contr DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_bgmc_op_single.

    METHODS constructor
      IMPORTING it_data TYPE zcl_bs_demo_bgpf_data=>tt_data.

  PRIVATE SECTION.
    DATA mt_data TYPE zcl_bs_demo_bgpf_data=>tt_data.
ENDCLASS.


CLASS zcl_bs_demo_bgpf_process_contr IMPLEMENTATION.
  METHOD constructor.
    mt_data = it_data.
  ENDMETHOD.


  METHOD if_bgmc_op_single~execute.
    DATA(lo_table) = NEW zcl_bs_demo_bgpf_data( ).

    " Optional
    cl_abap_tx=>modify( ).

    LOOP AT mt_data INTO DATA(ls_data).
      lo_table->add( ls_data ).
    ENDLOOP.

    cl_abap_tx=>save( ).

    lo_table->save( abap_false ).
  ENDMETHOD.
ENDCLASS.

 

Hint: In this variant we are not allowed to issue a COMMIT WORK, this is set by the framework and only leads to an error if we try.

 

Execution

The execution is very similar to the first variant, the only difference is the SET_OPERATION method that we call to execute our operation. Here too, the process is only triggered after the COMMIT WORK.

DATA lo_operation TYPE REF TO if_bgmc_op_single.

DATA(lt_data) = VALUE zcl_bs_demo_bgpf_data=>tt_data(
    ( description = 'Control 1' inumber = 12 amount = '12.54' currency = 'EUR' )
    ( description = 'Control 2' inumber = 95 amount = '0.21' currency = 'USD' )
    ( description = 'Control 3' inumber = 547 amount = '145.50' currency = 'EUR' ) ).

lo_operation = NEW zcl_bs_demo_bgpf_process_contr( lt_data ).

DATA(lo_process) = cl_bgmc_process_factory=>get_default( )->create( ).
lo_process->set_name( 'Controlled Process' )->set_operation( lo_operation ).

lo_process->save_for_execution( ).

 

Monitoring

What happens next once we have started the process? SAP offers a very simple monitoring option here.

 

Derive ID

If we look at the SAVE_FOR_EXECUTION method, we find a monitoring object there that we receive when the process is executed.

 

We can use the object directly or we can have the ID returned to us in order to save it and evaluate it later in different sessions.

DATA(lo_process_monitor) = lo_process->save_for_execution( ).
DATA(ld_id) = lo_process_monitor->to_string( ).

 

The TO_STRING method then returns an XML format with the relevant information about the process. Here is the prepared XML that we receive from the method:

<?xml version="1.0" encoding="utf-16"?>
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
    <asx:values>
        <MONITOR href="#o146"/>
    </asx:values>
    <asx:heap xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:abap="http://www.sap.com/abapxml/types/built-in" xmlns:cls="http://www.sap.com/abapxml/classes/global" xmlns:dic="http://www.sap.com/abapxml/types/dictionary">
        <cls:CL_BGMC_PROCESS_MONITOR id="o146">
            <CL_BGMC_PROCESS_MONITOR>
                <MV_BGRFC_UNIT_ID>Ghuf92k+Hu+I55PryC/zBw==</MV_BGRFC_UNIT_ID>
                <MV_BGRFC_QUEUE/>
            </CL_BGMC_PROCESS_MONITOR>
        </cls:CL_BGMC_PROCESS_MONITOR>
    </asx:heap>
</asx:abap>

 

Create monitor

We can save the ID in the database or use it for further processing. We can then use the ID to create the monitoring object again in order to carry out various actions. In the following source code we create a new monitoring object and have the status of the process returned to us.

DATA(lo_process_monitor) = cl_bgmc_process_factory=>create_monitor_from_string( ld_id ).
DATA(ld_state) = lo_process_monitor->get_state( ).

 

In the interface IF_BGMC_PROCESS_MONITOR we find an enum (GCS_STATE) with all constants that affect the status.

 

Process

Let's now test the execution sequence. Our test is planned as follows:

  • Creating the operation and starting the process via COMMIT WORK
  • Creating the ID via the monitoring object
  • Checking the status every second and writing the UTC timestamp and the status to the console

 

Results

We now receive the following results as output. Firstly the output from the console:

 

And secondly the output from the database:

 

Evaluation

After the process has been started via the commit, we check the status for the first time. Since this is still in the queue, we receive the status NEW. After around one second the process is executing, the timestamps of the database entries are set when the data record is adopted. A new data record is adopted every second. This is how long our monitoring process sees that the process is running. After the data records have been processed, the process returns a SUCCESSFUL and our logic ends.

From this we can see that the processing of the queue does not start immediately after it is added, but the process is executed with a short delay, meaning it is in a queue. We can use monitoring to track the status of the process and provide the user with information about it.

 

Complete example

Finally, the complete example of the executing class. Both variants are run through and the result is written to the console. At the start of processing, the table is deleted so that only one new result is recorded.

CLASS zcl_bs_demo_bgpf_start DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    METHODS run_uncontrolled_process
      RETURNING VALUE(rd_result) TYPE string.

    METHODS run_controlled_process
      RETURNING VALUE(rd_result) TYPE string.

    METHODS wait_and_log
      IMPORTING io_out    TYPE REF TO if_oo_adt_classrun_out
                id_string TYPE string
      RAISING   cx_bgmc.
ENDCLASS.


CLASS zcl_bs_demo_bgpf_start IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DELETE FROM zbs_dmo_bgpf.
    COMMIT WORK.

    out->write( `Start uncontrolled` ).
    DATA(ld_string) = run_uncontrolled_process( ).
    wait_and_log( io_out    = out
                  id_string = ld_string ).

    out->write( `Start controlled` ).
    ld_string = run_controlled_process( ).
    wait_and_log( io_out    = out
                  id_string = ld_string ).
  ENDMETHOD.


  METHOD run_controlled_process.
    DATA lo_operation TYPE REF TO if_bgmc_op_single.

    DATA(lt_data) = VALUE zcl_bs_demo_bgpf_data=>tt_data(
        ( description = 'Control 1' inumber = 12 amount = '12.54' currency = 'EUR' )
        ( description = 'Control 2' inumber = 95 amount = '0.21' currency = 'USD' )
        ( description = 'Control 3' inumber = 547 amount = '145.50' currency = 'EUR' ) ).

    lo_operation = NEW zcl_bs_demo_bgpf_process_contr( lt_data ).

    TRY.
        DATA(lo_process) = cl_bgmc_process_factory=>get_default( )->create( ).
        lo_process->set_name( 'Controlled Process' )->set_operation( lo_operation ).

        DATA(lo_process_monitor) = lo_process->save_for_execution( ).
        COMMIT WORK.

        RETURN lo_process_monitor->to_string( ).

      CATCH cx_bgmc.
        ROLLBACK WORK.
    ENDTRY.
  ENDMETHOD.


  METHOD run_uncontrolled_process.
    DATA lo_operation TYPE REF TO if_bgmc_op_single_tx_uncontr.

    DATA(lt_data) = VALUE zcl_bs_demo_bgpf_data=>tt_data(
        ( description = 'Test 1' inumber = 12 amount = '12.54' currency = 'EUR' )
        ( description = 'Test 2' inumber = 95 amount = '0.21' currency = 'USD' )
        ( description = 'Test 3' inumber = 547 amount = '145.50' currency = 'EUR' ) ).

    lo_operation = NEW zcl_bs_demo_bgpf_process_uncon( lt_data ).

    TRY.
        DATA(lo_process) = cl_bgmc_process_factory=>get_default( )->create( ).
        lo_process->set_name( 'Uncontrolled Process' )->set_operation_tx_uncontrolled( lo_operation ).

        DATA(lo_process_monitor) = lo_process->save_for_execution( ).
        COMMIT WORK.

        RETURN lo_process_monitor->to_string( ).

      CATCH cx_bgmc.
        ROLLBACK WORK.
    ENDTRY.
  ENDMETHOD.


  METHOD wait_and_log.
    DATA(lo_process_monitor) = cl_bgmc_process_factory=>create_monitor_from_string( id_string ).

    DO.
      IF sy-index = 60.
        EXIT.
      ENDIF.
      DATA(ld_state) = lo_process_monitor->get_state( ).

      io_out->write( ld_state ).
      io_out->write( utclong_current( ) ).

      IF    ld_state = if_bgmc_process_monitor=>gcs_state-successful
         OR ld_state = if_bgmc_process_monitor=>gcs_state-erroneous.
        EXIT.
      ENDIF.

      WAIT UP TO 1 SECONDS.
    ENDDO.
  ENDMETHOD.
ENDCLASS.

 

Availability

The function has been available in the ABAP Environment for a while, so you don't have to pay attention to the release. The framework is delivered on-premise with S/4 HANA 2023 and can be used after a short configuration. You can find more details about the configuration in the linked SAP blog below.

Hint: The monitoring functionality between cloud and on-premise versions may differ, so we recommend taking a look at the official SAP documentation.

 

Conclusion

With bgPF and the new way of background processing, you have a simple alternative in ABAP Cloud to outsource processes. You don't even need a function module and can remain completely in the OO context.

 

More information:
SAP Blog - Introducing the Background Processing Framework
YouTube - ABAP Cloud: Background Processing Framework


Included topics:
ABAP CloudABAPBackground processingbgPF
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP Cloud - Clean Core (Scenarios)

Category - ABAP

In this article, let's take another look at the Clean Core architecture with ABAP Cloud, where it is used and where you can build your applications.

01/10/2025

ABAP Cloud - Programming Model

Category - ABAP

Which programming model is used with ABAP Cloud and what can we learn from its predecessor? More details in the article.

01/03/2025

ABAP Cloud - ADT Trees (Overview)

Category - ABAP

What help and overviews are available in the ABAP Development Tools to simplify your life with ABAP Cloud?

12/17/2024

ABAP Cloud - Relevant Objects

Category - ABAP

Which objects are still relevant in ABAP development and which ones can you slowly put away? Find out more here.

11/19/2024

ABAP Cloud - Locks

Category - ABAP

What do you need to set locks for and how can you easily do that in ABAP Cloud? In this article we will look at the process in detail.

11/08/2024