
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.
...
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.
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:
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.
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.
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.
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.
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'.
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).