This is a test message to test the length of the message box.
Login
ABAP Cloud Hintergrundverarbeitung
Erstellt von Software-Heroes

ABAP Cloud - Hintergrundverarbeitung

149

Wie lagert man in ABAP Cloud eigentlich speicherintensive Prozesse in den Hintergrund aus und monitort diese? In diesem Artikel schauen wir uns das Framework einmal genauer an.

Werbung


In diesem Artikel werden wir uns bgPF anschauen und wie du es in ABAP Cloud verwenden kannst, um Prozesse in den Hintergrund zu verschieben. Dabei werden wir uns an einem Beispiel anschauen, wie es im Detail funktioniert und die beiden Varianten, die für Prozesse angeboten werden.

 

Einleitung

In Prozessen kann es vorkommen, dass der eigentliche Prozess bereits abgeschlossen ist (Anlage eines Beleges oder einer Buchung) und dann gewisse Zusatzprozesse laufen sollen. Solche Prozesse können zum Beispiel sein: Sammeln von Statistiken, Erstellung von Mails, Auswertungen oder Nachfolgeprozesse anstoßen. Solche Zusatzprozesse kosten meistens Laufzeit und hindern den User die UI zu bedienen, um normal weiterzuarbeiten (Ladebalken, Sanduhr, etc.).

Für solche Fälle gibt es Hintergrundprozesse, die angestoßen werden und dann selbstständig laufen. SAP hatte deshalb das Background Processing Framework, kurz bgPF, vorgestellt. Die Verarbeitung in Prozessen im Hintergrund ist nicht neu, allerdings eine Verarbeitung ohne eigenen Funktionsbaustein war bisher nicht üblich. Auch das man sich nicht um die Verwaltung des Prozesses kümmern muss.

Möchtest du mehr zur Funktionsweise von bgPF erfahren, empfehlen wir dir den unten verlinkten SAP Community Artikel, dort wird der Prozess im System noch einmal schematisch dargestellt.

 

Vorbereitung

In unseren Beispielen werden wir eine Tabelle für das Logging verwenden, umso besser die Aktionen im System nachvollziehen zu können. Die Tabelle ist wie folgt definiert:

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);
}

 

Dazu setzen wir eine Datenverarbeitungsklasse darüber, die sich um das Befüllen der Sätze kümmert, damit wir in den Beispielen uns auf das eigentliche Framework fokussieren können.

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.

 

Die Klasse ist recht simpel gehalten, über die ADD Methode können wir neue Datensätze in den internen Puffer schreiben, über die SAVE Methode werden die Datensätze dann auf die Datenbank geschrieben. Damit wir später besser die Veränderungen sehen können, gibt es in der ADD Methode einen WAIT, um den Prozess zu verlangsamen.

 

Varianten

Das Framework bietet aktuell zwei unterschiedliche Varianten von Prozessen an, die zur Ausführung genutzt werden. Bist du im ABAP RESTful Programming Model unterwegs, solltest du die Variante "Controlled" verwenden, für alle anderen Anwendungsfälle bleibst du am besten bei "Uncontrolled".

 

 

Variante - Uncontrolled

Die unkontrollierte Variante arbeitet ohne zusätzliche Einschränkungen und wir können hier tun und lassen, was wir für unsere Arbeit benötigen. Dafür implementieren wir in der Klasse das Interface IF_BGMC_OP_SINGLE_TX_UNCONTR, welches die EXECUTE Methode zur Verfügung stellt. Diese wird später bei der Verarbeitung ausgeführt.

 

Prozess

Die Klasse mit dem Interface IF_BGMC_OP_SINGLE_TX_UNCONTR ist später unser Prozess der im Hintergrund läuft. Wir sollten also alle Daten dem Objekt zur Verfügung stellen, die aus unserem aktuell laufenden Prozess benötigt werden.

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 diesem Beispiel besteht die Verarbeitung daraus, dass wir die übergebenen Datensätze übernehmen und über die SAVE Methode mit COMMIT WORK auf die Datenbank persistieren.

 

Ausführung

Wollen wir nun unseren Prozess ausführen, dann können wir dies wie folgt tun:

  • Erzeugung der Operation (unser Prozessobjekt)
  • Erzeugen eines Prozesses über die Factory
  • Setzen des Prozessnamens
  • Übergabe der Operation über die Methode SET_OPERATION_TX_UNCONTROLLED
  • Speichern zur Ausführung
  • COMMIT zur Ausführung des Prozesses

 

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.

 

Die Ausführung beginnt nur, wenn der COMMIT WORK gesetzt wird. Wird ein ROLLBACK wegen eines Fehlers durchgeführt, startet auch unser Prozess nicht. Dies soll sicherstellen, dass der Prozess nur im Erfolgsfall ausgeführt wird.

 

Variante - Controlled

Der Unterschied zur unkontrollierten Variante ist, dass hier die RAP Phasen durchlaufen werden, die über die Klasse CL_ABAP_TX angesteuert werden können. Das heißt wir haben eine Interaktionsphase und die Speichersequenz, die wir in unserem Prozess abbilden können. Die Klasse wird in diesem Fall mit dem Interface IF_BGMC_OP_SINGLE implementiert, es steht uns aber auch hier die EXECUTE Methode zur Verfügung.

 

Prozess

Wieder übernehmen wir die Daten im Prozess und speichern sie für die Ausführung. Bei der Verarbeitung können wir Optional in die Interaktionsphase wechseln (MODIFY) und die Daten übernehmen. Da wir hier eigentlich auch mit RAP arbeiten würden, würden mir unser EML Statement absetzen. Zum Abschluss wechseln wir in die Speichersequenz und können auch Statements wir INSERT oder UPDATE auf die Datenbank absetzen. 

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.

 

Hinweis: In dieser Variante dürfen wir keinen COMMIT WORK absetzen, dieser wird durch das Framework gesetzt und führt nur zu einem Fehler, wenn wir es versuchen sollten.

 

Ausführung

Die Ausführung ist sehr ähnlich zur ersten Variante aufgebaut, der einzige Unterschied ist die Methode SET_OPERATION die wir aufrufen, um unsere Operation ausführen zu lassen. Auch hier gilt, dass der Prozess erst nach dem COMMIT WORK ausgelöst wird.

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

Wie geht es eigentlich weiter, wenn wir den Prozess gestartet haben? SAP bietet hier eine recht einfache Möglichkeit eines Monitorings an.

 

ID ableiten

Wenn wir uns die Methode SAVE_FOR_EXECUTION anschauen, finden wir dort ein Monitoring Objekt, welches wir bei Ausführung erhalten.

 

Wir können das Objekt direkt verwenden oder wir lassen uns die ID zurückgeben, um sie zu sichern und später in unterschiedlichen Sessions auszuwerten.

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

 

Die Methode TO_STRING liefert uns dann ein XML Format mit den entsprechenden Informationen zum Prozess zurück. Hier einmal das aufbereitete XML, welches wir aus der Methode erhalten:

<?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>

 

Monitor erzeugen

Die ID können wir auf der Datenbank speichern oder für die weitere Verarbeitung nutzen. Über die ID können wir uns dann wieder das Monitoring Objekt erzeugen, um verschiedene Aktionen auszuführen. Im folgenden Quellcode erzeugen wir ein neuen Monitoring Objekt und lassen uns den Status des Prozess zurück geben.

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

 

Im Interface IF_BGMC_PROCESS_MONITOR finden wir ein Enum (GCS_STATE) mit allen Konstanten, die den Status betreffen.

 

Ablauf

Testen wir nun einmal den Ablauf der Ausführung, unser Test ist wie folgt geplant:

  • Erzeugung der Operation und Start des Prozesses über COMMIT WORK
  • Erzeugung der ID über das Monitoring Objekt
  • Prüfung des Status alle eine Sekunde und schreiben des UTC Timestamp, sowie des Status, in die Konsole

 

Ergebnisse

Die folgenden Ergebnisse erhalten wir nun als Ausgabe. Einmal die Ausgabe aus der Konsole:

 

Und als zweites die Ausgabe auf der Datenbank:

 

Auswertung

Nachdem der Prozess über den Commit gestartet wurde, prüfen wir das erste Mal den Status. Da dieser sich noch in der Queue befindet, erhalten wir den Status NEW zurückgeliefert. Nach ca. einer Sekunde befindet sich der Prozess in der Ausführung, die Timestamps der Datenbankeinträge werden bei Übernahme des Datensatzes gesetzt. Alle eine Sekunde, wird ein neuer Datensatz übernommen. So lange sieht unser Monitoring Prozess, dass der Prozess am Laufen ist. Nachdem die Datensätze abgearbeitet wurden, meldet der Prozess ein SUCCESSFUL zurück und unsere Logik beendet sich.

Daraus erkennen wir, dass die Verarbeitung der Queue nicht unmittelbar nach dem Hinzufügen startet, sondern der Prozess mit einer kurzen Verzögerung ausgeführt wird, er damit in einer Queue steht. Über das Monitoring können wir den Status des Prozesses verfolgen und dem Anwender eine Information dazu geben.

 

Vollständiges Beispiel

Zum Abschluss noch das vollständige Beispiel der ausführenden Klasse. Dabei werden beide Varianten durchlaufen und das Ergebnis in die Konsole geschrieben. Zu Beginn der Verarbeitung wird die Tabelle gelöscht, damit nur ein neues Ergebnis aufgenommen wird.

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.

 

Verfügbarkeit

Im ABAP Environment steht die Funktion bereits eine Weile zur Verfügung, hier musst du nicht auf das Release achten. On-Premise wird das Framework mit S/4 HANA 2023 ausgeliefert und kann nach einer kurzen Konfiguration genutzt werden. Mehr Details zur Konfiguration findest du unten im verlinkten Blog der SAP.

Hinweis: Die Monitoring Funktionalität zwischen Cloud und On-Premise Version kann abweichen, daher empfiehlt sich ein Blick in die offizielle Dokumentation der SAP.

 

Fazit

Mit bgPF und dem neuen Weg der Hintergrundverarbeitung steht dir eine einfache Alternative in ABAP Cloud zur Verfügung, um Prozesse auszulagern. Selbst ein Funktionsbaustein benötigt man damit nicht und kann komplett im OO-Kontext bleiben.

 

Weitere Informationen:
SAP Blog - Introducing the Background Processing Framework
YouTube - ABAP Cloud: Background Processing Framework


Enthaltene Themen:
ABAP CloudABAPHintergrundverarbeitung
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP Cloud - ADT Bäume (Übersicht)

Kategorie - ABAP

Welchen Hilfen und Übersichten stehen dir in den ABAP Development Tools zur Verfügung, um dein Leben mit ABAP Cloud zu vereinfachen?

17.12.2024

ABAP Cloud - Relevante Objekte

Kategorie - ABAP

Welche Objekte sind in der ABAP Entwicklung eigentlich noch relevant und welche kannst du so langsam in die Mottenkiste packen? Hier erfährst du mehr.

19.11.2024

ABAP Cloud - Sperren

Kategorie - ABAP

Für was musst du Sperren setzen und wie kannst du das leicht in ABAP Cloud machen? In diesem Artikel schauen wir uns den Prozess im Detail an.

08.11.2024

ABAP Cloud - HTTP Client

Kategorie - ABAP

Wie sieht eigentlich der aktuelle HTTP Client in ABAP Cloud aus? Lasst uns einen Blick auf das neue Modell werfen.

01.11.2024

ABAP Cloud - Key User Apps

Kategorie - ABAP

Die Key User Extensibility ist ein Teil von ABAP Cloud, wenn es um die Erweiterung des Core geht, doch wie kannst du die Werkzeuge sinnvoll nutzen und wie ergänzen sie sich?

11.10.2024