Additional Blogs by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
yuri_ziryukin
Employee
Employee

Introduction

This article was written by my colleague Steffen Koehler (thanks, Steffen!) and it contains interesting observations and recommendations for the usage of "commit work and wait" and "wait up to X seconds" ABAP statements.

Problem description

Especially in customer reports using BAPI function modules to create and change multiple objects, you often see 'WAIT UP TO XX SECONDS' statements after a call of function module BAPI_TRANSACTION_COMMIT or after a COMMIT WORK (AND WAIT) statement. In the following it will be discussed when and why a WAIT statement should be used and how the report logic could be improved.

Reason for using a WAIT statement

The goal of the WAIT statement is to ensure that a new document is completely posted so that it can be changed. The following sample code shows in a simplified way how the logic often looks like:
...
LOOP AT objects.
CALL FUNCTION 'BAPI_OBJECT_CREATE'.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
   IMPORTING
     WAIT = ' '.
WAIT UP TO 10 SECONDS.
CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
ENDLOOP.

Due to the WAIT statement each loop execution lasts at least 10 seconds. If the actual posting is much faster e.g. 2 seconds, 8 seconds are waisted. And this occurs in each loop execution, i.e. with 10 objects there are just 20 seconds of processing and 80 seconds of waiting.

COMMIT WORK AND WAIT (or BAPI_TRANSACTION_COMMIT with parameter WAIT = 'X') does not work as expected

The BAPI 'BAPI_TRANSACTION_COMMIT' is internally using statements COMMIT WORK (parameter WAIT = ' ') and COMMIT WORK AND WAIT (parameter WAIT = 'X'). In the following you will find the relevant part of the SAP Online Documentation for the COMMIT statement:

...
This executes all high-priority
(VB1) update function modules in the order of their registration and in a common database LUW. If you do not specify the addition AND WAIT, the program does not wait until the update work process has executed it (asynchronous updating), but instead is resumed immediately after COMMIT WORK. However, if the addition AND WAIT is specified, program processing after COMMIT WORK will not continue until the update work process has executed the high-priority update function modules (synchronous updating).

...

From the documentation we would expect that using COMMIT WORK AND WAIT (or the BAPI with WAIT = 'X') should be sufficient to ensure that the new object had been created successfully before the next statement is executed. In some cases this is correct in other cases it is not. Following our analysis the COMMIT WORK AND WAIT does not work if:

  • There is a COMMIT WORK executed within the BAPI. This COMMIT WORK statement is also triggering the Update processing.
    Examples are BAPI_MATERIAL_SAVEDATA, BAPI_ENTRYSHEET_CREATE and BAPI_PO_CREATE (use BAPI BAPI_PO_CREATE1 instead).
    Following our analysis the COMMIT WORK AND WAIT statement does not work as expected, if there is a COMMIT WORK statement executed within the BAPI itself. The reason is that no data are committed and no Update processing is triggered by the BAPI_TRANSACTION_COMMIT if a COMMIT WORK was executed before. The BAPI_TRANSACTION_COMMIT is therefore not waiting for the Update 1 processing which was triggered with the previous Commit in the application BAPI. The behavior can be perfectly tested with BAPI 'BAPI_SALESORDER_CREATEFROMDAT1' because this BAPI has a parameter 'WITHOUT_COMMIT' which controls whether a COMMIT WORK statement is executed.
    When we used the BAPI with parameter 'WITHOUT_COMMIT' = '' the BAPI executed a COMMIT WORK internally and the BAPI_TRANSACTION_COMMIT (WAIT = 'X') did not work as expected. In this case also other options e.g. 'SET UPDATE TASK LOCAL', CALL FUNCTION 'TRANSACTION_BEGIN' together with COMMIT WORK AND WAIT did not show the required result.
    When we used the BAPI with parameter 'WITHOUT_COMMIT' = 'X' the BAPI did not execute a COMMIT WORK internally and the BAPI_TRANSACTION_COMMIT (WAIT = 'X') worked fine.
  • There is more than one V1 update generated by the BAPI.
    An example is BAPI 'BAPI_PRODORDCONF_CREATE_TT' which creates a confirmation for a production order. If the 'Backflush' indicator is used in the confirmation there will be be a material movement generated, too. The confirmation generates a V1 update but also the material movement generates a V1 update. The COMMIT WORK AND WAIT will in this case only wait until the first V1 update is finished.

How do I know for which BAPIs the COMMIT WORK AND WAIT is sufficient to ensure complete posting?

In the following you will find the description of the new transaction model for BAPIs:

The BAPI transaction model must afford the user explicit transaction control. Therefore, if several BAPIs are called together, the caller can decide him/herself when to execute a COMMIT WORK (or, as the case may be, a ROLLBACK WORK). This means that BAPIs themselves cannot (generally) execute a COMMIT WORK command.
The following restrictions apply to combining several BAPIs in one LUW:

  • If an instance was created, modified or deleted by a write BAPI, a read BAPI can only access the most recent data if a COMMIT WORK has taken place.
  • It is not possible to make two write accesses on the same instance within one LUW. For example, you cannot first create and then change the object
    within the same LUW.
    You can, however, create several instances of the same object type within an LUW.

Although all BAPIs provided by SAP should follow this transaction model, there are exceptions. There is no list of BAPIs for which the COMMIT WORK AND
WAIT works fine. But there is also no list of BAPIs for which the statement does not work as expected. Some BAPIs have a parameter e.g. 'DO_COMMIT' to decide if a COMMIT should be performed within the BAPI. For those BAPIs the internal COMMIT should be deactivated and the COMMIT WORK AND WAIT statement (or BAPI_TRANSACTION_COMMMIT with WAIT = 'X') should be used. For all other BAPIs it has to be tested.

What to do when COMMIT WORK AND WAIT does not work as expected?

Grouping by BAPI method

The processing of one particular object from the beginning to the end by calling different BAPIs (BAPI_OBJECT_CREATE, BAPI_OBJECT_CHANGE) often requires that the update of one BAPI is completed before the next BAPI could be called. Below you will find a sample how this would look like in a straightforward implementation.

...
LOOP AT objects.
  CALL FUNCTION 'BAPI_OBJECT_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = 'X'.
  CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = ' '.
  ENDIF.
ENDLOOP.

Instead of doing all actions object by object it could be better to use grouping by BAPI method. Below you will find a sample how this could look like:

...
LOOP AT objects.
  CALL FUNCTION 'BAPI_OBJECT_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = ' '.
ENDLOOP.
LOOP AT objects.
  IF object not exists.
    APPEND object TO object_work_list
  ELSE.
    CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      IMPORTING
        WAIT = ' '.
  ENDIF.
ENDLOOP.
Reprocess the objects in object_work_list and wait

The sample will first use method CREATE to create all objects before the objects are going to be changed. Since the second loop starts with the object which was created first, it is highly probable that this object exists and could be changed. If the object does not exist yet it could be put into a work list and processed at a later point in time.

Use WAIT UP TO XX SECONDS in combination with Select

If the COMMIT WORK AND WAIT does not work as described before, the report logic has to check actively whether the object is posted before the object is
changed. This check could be done either using a SELECT statement on a database table (e.g. table VBAK for sales orders) or calling a function module which sets an ENQUEUE on the object.

...
LOOP AT objects.
CALL FUNCTION 'BAPI_OBJECT_CREATE'.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
   IMPORTING
     WAIT = 'X'.
  counter = 0.
  WHILE counter < 60. " Wait for 60 seconds in maximum
    SELECT SINGLE * FROM db_table WHERE key = object_key.
    IF sy-subrc = 0.
      counter = 70.
    ELSE.
      counter = counter + 1.
      WAIT UP TO 1 SECONDS.
    ENDIF.
  ENDWHILE.
  IF counter = 60.
    MESSAGE 'Object not posted in time'.
  ELSE.
    CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
  ENDIF.
  ...
ENDLOOP.

The Select will be executed as long as there is a record returned or the maximum wait time is exceeded.

Attention: This option will not work if the record could be read from the database table before the Commit is completed. For READ COMMITTED databases like Oracle this option should work, for other databases the scenario might fail.

Use WAIT UP TO XX SECONDS in combination with Enqueue

If the COMMIT WORK AND WAIT does not work as described before, the report logic has to check actively whether the object is posted before the object is
changed. This check could be done either using a SELECT statement on a database table (e.g. table VBAK for sales orders) or calling a function module which sets an ENQUEUE on the object.

...
LOOP AT objects.
  CALL FUNCTION 'BAPI_OBJECT_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = 'X'.
  counter = 0.
  WHILE counter < 60. " Wait for 60 seconds in maximum
    CALL FUNCTION 'ENQUEUE_EVVBAKE'
        EXPORTING
          vbeln          = US_VBELN
        EXCEPTIONS
          foreign_lock   = 02
          system_failure = 03.
    IF sy-subrc = 0.
      CALL FUNCTION 'DEQUEUE_EVVBAKE'
          EXPORTING
            VBELN = US_VBELN.
      counter = 70.
    ELSE.
      counter = counter + 1.
      WAIT UP TO 1 SECONDS.
    ENDIF.
  ENDWHILE.
  IF counter = 60.
    MESSAGE 'Object not posted in time'.
  ELSE.
    CALL FUNCTION 'BAPI_OBJECT_CHANGE'.
  ENDIF.
...
ENDLOOP.

The "while" loop will be executed as long as the enqueue is not successful or the maximum wait time is exceeded.

How to identify the enqueue table and function module

The enqueue table is usually related to the header table of an object e.g. VBAK for sales orders, EKKO for purchase orders etc.. To find the corresponding
lock object for a header table follow this procedure:
1. Go to transaction SE11.
2. Open the F4 help for field 'Lock object'.
3. Click at icon 'All selections (Shift+F7)' on the bottom to get selection field 'Base table' displayed.
4. Fill the header table (e.g. VBAK) into field 'Base table' and execute the search. The function module for the enqueue has the name 'ENQUEUE_<lock object>' e.g.'ENQUEUE_EVVBAKE'.

Use function module 'TRANSACTION_BEGIN' together with 'BAPI_REQUISITION_CREATE' and 'BAPI_REQUISITION_CHANGE'

This option can only be used for very special BAPIs i.e. 'BAPI_REQUISITION_CREATE' and 'BAPI_REQUISITION_CHANGE'. The reason is that these BAPIs internally use function module 'TRANSACTION_BEGIN' themselves to control a commit inside the BAPI.

Function module 'TRANSACTION_BEGIN' defines a new LUW and generates a transaction ID for this LUW. The first thing which is done inside the function
module is to check whether the function module had been called before i.e. whether a LUW already exists. If the function module is called before the BAPI
call, the internal COMMIT WORK statement in the BAPI is suppressed. Since there is no internal commit in the BAPI, the COMMIT WORK AND WAIT works as expected.

...
LOOP AT objects.
  CALL FUNCTION 'TRANSACTION_BEGIN'
   IMPORTING
      TRANSACTION_ID = lv_tid
    EXCEPTIONS
      OTHERS         = 0.
  CALL FUNCTION 'BAPI_REQUISITION_CREATE'.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    IMPORTING
      WAIT = 'X'.
  CALL FUNCTION 'TRANSACTION_BEGIN'
   IMPORTING
      TRANSACTION_ID = lv_tid
    EXCEPTIONS
      OTHERS         = 0.
  CALL FUNCTION 'BAPI_REQUISITION_CHANGE'.
 
...
ENDLOOP.

Note: The LUW should be completed by calling function module 'BAPI_TRANSACTION_COMMIT' (with WAIT = 'X') and not with function module

'TRANSACTION_END'. Function module 'TRANSACTION_END' would also complete the LUW but this function module would only perform a COMMIT WORK (without WAIT).

7 Comments