Preventing sporadic lock conflicts when calling BAPIs
Suppose you need to generate a chain of supply documents automatically via BAPI, or you have to call the same BAPI several times e.g. from within a LOOP-ENDLOOP statement. In such circumstances you might face a problem when system reports that you are trying to lock an object which already locked by yourself . This is a Deadlock. And at the same time you definitely know that you did not place any lock. Also the situation may occur when you select application table immediately after successful BAPI call and committing work and the SELECT statement returns sy-subrc = 4, that is nothing found. The things are getting worse by sporadic nature of the error.
Whats behind it?
A number of BAPIs, particularly those posting various documents (financial documents, purchase/sales orders, material movements etc.); indirectly call function modules updating database tables asynchronously. So do all the common dialog transactions. This is a main part of SAP update process. As a rule before calling update functions BAPI locks some functionally related objects to prevent data inconsistencies.
The main goal of the asynchronous update mechanism is to optimize system response time for users working in dialog mode. Roughly speaking, the dialog transaction does all the sophisticated checks on-line (and reports errors to a user if any) and prepares a consistent data bundle (e.g. document header and items) and then leaves all the routine work of physical updating database tables to another background task. And that background task may run even on a different application server.
Technically this looks as follows: when you call some function module with addition IN UPDATE TASK actually the function does not start execution, instead its name and actual parameter values with a unique key are written to the special set of tables which also can be called an update queue (those are tables VBHDR, VBMOD, VBDATA). And at the moment you issue a COMMIT WORK statement the special aided parallel task (a.k.a. update work process) starts processing the update queue. And the control immediately returns to the calling program. And at that moment its not guaranteed that all the database tables already updated with actual data. So, you may get sy-subrc = 4 after selecting data from application tables with a right key value returned from the BAPI.
The treatment for the sy-subrc = 4 error is COMMIT WORK AND WAIT statement. The usage of COMMIT WORK AND WAIT can be called synchronous update technique as the control returns to the calling program only after all physical updates committed to the database. But COMMIT WORK AND WAIT cannot help with lock conflict.
Thats because of update processing sequence. First, update work process does really call all the update function modules, which issue SQL statements for inserting/updating records in application tables, then (if there is no errors) update work process issues database commit remember that at this point the program which waits at COMMIT WORK AND WAIT can continue working. And finally only after committing database updates work process starts to release SAP locks. So, as both calling program and update work process works in parallel it is not guaranteed that immediately after returning from COMMIT WORK AND WAIT all the locks previously locked in BAPI are released.
Fortunately there is a clue. You can successfully prevent both lock conflict and sy-subrc = 4 error by employing local update mode. It can be turned on by issuing SET UPDATE TASK LOCAL statement before calling BAPI. That means that all the updates which were supposed to be in parallel update task would be processed (together with lock release) in the same calling task as part of COMMIT WORK statement processing. Note that both COMMIT WORK or ROLLBACK WORK statements reset the mode to a default value (non-local update mode), so, you have to set the local update mode at the beginning of each LUW in your program (if there is more than one).
There is at least one useful complement of local update mode usage. Suppose you have implemented a user exit which is called from within particular update function module for example, there are such user exits in sales documents. What to do if you need to transfer data from calling program to such a user exit? Sometimes it can be done via additional fields of application tables, but not always for example, you need to transfer not a value but even an internal table. Using local update mode you can do this via IMPORT/EXPORT statements as all the update function modules run in the same task and share the same task memory.
Certainly the local update mode is useful in particular circumstances it is not useful or even it is undesirable to use it in dialog transaction with one LUW as local update mode can increase system response time.