ABAP调用BAPI时COMMIT WORK AND WAIT未按照预期同步提交问题分析

背景:

在做ABAP开发时,经常会有连续调用BAPI的需求,比如先创建销售订单,再依据销售订单创建交货单,再对交货单进行过账等类似的一连串调用,这种类似的场景往往需要前一步操作的数据完全写入数据库才能进行一下个步骤,但是数据写入底表是需要时间的,如果一些业务数据比较复杂,可能在调用下一个BAPI时会因为数据尚未写入底表而导致BAPI报出单据不存在等类似的错误消息(如果BAPI是以异步提交方式处理),这时候不同的开发者会往往有不同的处理方式,比如WAIT UP TO XXX SECONDS之类的操作,但并不推荐这么去做,因为往往会浪费一些不必要的时间,本文将结合笔者自身经验来分析这个问题,提供一些不同的解决方式,根据实际情况进行选择。


BAPI的执行原理:

大部分BAPI执行的大概过程如下:

:报表程序中调用BAPI;

②③④:BAPI内部经过一系列检查处理后,将需要更新提交的数据统一注册到更新进程LUW中;

如果BAPI本身比较规范的话,一般会有类似COMMIT_WORK以及COMMIT_WORK_AND_WAIT等类似的传入参数,那么我们最好是通过参数使其内部以同步方式提交,或者使其内部不要提交,由调用者来决定是否已同步方式提交(AND WAIT附加项决定了更新进程是否以同步模式提交);

在碰到COMMIT WORK语句后,之前注册的所有更新函数将按照顺序依次执行⑦⑧⑨⑩,并且BAPI中产生的锁会一并带到更新进程中,优先执行V1更新,V1更新执行完毕后,数据库进行提交,并且释放所有的锁,V2进程不会再有加锁的操作,V2执行完毕后,整个数据库提交过程完毕,如果更新进程中出错,则会自动触发回滚,并收到一条Dump快件;

如果是以同步提交(COMMIT WORK AND WAIT)触发更新,则程序会在更新进程执行完毕后才返回至调用程序,如果是以异步方式(COMMIT WORK)触发更新,则程序不会等待更新进程执行完毕,立即进行后续处理,这种时候如果要对该BAPI产生的单据进行下一步处理,则可能会出现单据不存在,或者锁定报错;

某些BAPI中的更新进程中甚至会有上图中的处理流程,将一部分数据处理用IN BACKGROUD TASK的方式去触发异步事务性函数(tRFC)处理,尽管这种方式已被SAP标记为过时的方式,但是仍然有一些BAPI中采用了这种方式,比如报工BAPI:BAPI_PRODORDCONF_CREATE_TT,其中针对于货物移动的处理就是采用的这种方式,使用IN BACKGROUND TASK触发的事务会立即以异步方式在独立进程中执行,所以AND WAIT不会对其生效。


更新进程的处理流程:

官方说明:

After the transaction closes, the dialog work process closes the VBHDR entry (the beginning of the update for the update request), and searches for an update server for the U1 update. This described in more detail in Update Dispatching with Load-Balancing.

The update server distributes the tasks to an update work process. This processes the V1 modules of the update request, triggers a COMMIT to the database, and releases the R/3 locks on the update request (see The SAP Lock Concept). Then the work process searches for an update server for the U2 update, if the U2 update modules exist.

These are forwarded from a U2 update server to a U2 work process that processes the U2 modules, and triggers a COMMIT on the database.

The following graphic displays this process from the view of various work processes. It also displays the time at which changes to the database are made.

官方说明:

The U1 modules are processed by transmitting the contents of the update table VBMOD and VBDATA to the application tables of the database. The changes are actually in the desired tables in the database only at the end of the database LUW in which it occurs. The SAP locks are released and, if V2 update modules exist, the V2 update is started. This is similar to the V1 update with the exception that there are no locks that have to released and no search for a process for further processing.

需要注意的是,V1更新是可选同步更新或者异步更新,根据附加项AND WAIT来决定,V2更新则永远是异步更新,COMMIT WORK AND WAIT也只会等待V1更新完毕。 


COMMIT WORK AND WAIT不生效的原因:

知道了BAPI以及更新进程的原理,那同步提交不生效的原因就变得清晰了,大概有以下几种情况:

  • BAPI没有NO_COMMIT以及COMMIT_WORK_AND_WAIT等类似的参数,直接在内部执行了COMMIT WORK语句(没有附加AND WAIT),因为内部的COMMIT WORK已经触发了更新进程的处理,所以调用程序中的COMMIT WORK AND WAIT将没有东西可触发,所以会不生效,类似的BAPI有BAPI_MATERIAL_SAVEDATA,BAPI_ENTRYSHEET_CREATE 和 BAPI_PO_RESET_RELEASE等。
  • BAPI本身提供了NO_COMMIT参数,但是调用时未传入该参数,导致BAPI内部仍然以异步方式进行了提交,故调用侧的同步提交不生效,类似的BAPI有BAPI_PO_RELEASE,CO_SE_PRODORD_OPR_CREATE等。
  • BAPI在更新进程中触发了新的异步远程更新进程,如报工BAPI:BAPI_PRODORDCONF_CREATE_TT,COMMIT WORK AND WAIT只会等到IN UPDATE TASK注册的更新进程处理完毕,并不会等待IN BACKGROUND TASK注册的更新进程。

标准BAPI事务模型规范:

BAPI 事务模型必须为用户提供明确的事务控制权。因此,如果同时调用多个 BAPI,则调用方可以自行决定何时执行 COMMIT WORK(或者,视情况而定,执行 ROLLBACK WORK)。这意味着 BAPI 本身不能(通常)执行 COMMIT WORK 命令。
以下限制适用于将多个 BAPI 合并到一个 LUW 中:

  • 如果实例是由写入 BAPI 创建、修改或删除的,则读取 BAPI 只有在发生 COMMIT WORK 时才能访问最新数据。
  • 不能在一个 LUW 中的同一实例上进行两次写入访问。例如,不能先在同一 LUW 中创建然后更改对象。但是,您可以在 LUW 中创建同一对象类型的多个实例。

尽管 SAP 提供的所有 BAPI 都应遵循此事务模型,但也有例外,并不是所有BAPI都会按照这个规范去操作,所以碰到这种情况,则需要按照实际情况去分析处理。


如何解决:

解决该问题的方式有很多种,其中最不推荐的则是WAIT UP TO XXX SECONDS的方式,尽管高版本中可以选择WAIT UP TO '0.1' SECONDS的方式,但对于一些强迫症开发者来说,多余浪费的0.01秒都是不能容忍的,所以可以有以下几种替代方式来处理异步提交问题。

1:分组处理

比如针对一批数据的每一条,都需要调用完创建BAPI之后再调用修改BAPI对其修改,则可以先统一进行创建处理,再进行修改处理,通常当所有单据创建完之后,最先创建的单据应该早已提交更新完毕,示例如下:

改造前:

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

改造后:

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

2:SET UPDATE LOCAL TASK

SET UPDATE LOCAL TASK会忽略IN UPDATE TASK附加项,使BAPI中数据库更新的过程以本地更新的方式在当前会话内部单独执行,而非注册到VB更新进程中,并且COMMIT WORK会隐式的添加同步提交,但每次COMMIT WORK之后,本地更新会被禁用,所以需要的时候需要在每次调用BAPI之前执行SET UPDATE LOCAL TASK语句来开启本地更新,该方式适用于数据库提交函数中没有触发异步远程RFC的BAPI,例如BAPI_PRODORDCONF_CREATE_TT。


3:使用时间戳+WHILE

通过下面的代码,可以将等待时间减少到尽可能短的等待时间 ,具体取决于使用情况,从而不会浪费额外多余的时间。

DATA: BEGIN OF ls_time,
        start   TYPE timestampl,
        now     TYPE timestampl,
        elapsed TYPE tzntstmpl,         " 目前已运行时间
        limit   TYPE tzntstmpl VALUE 3, " 最大等待时间
      END OF ls_time.

GET TIME STAMP FIELD ls_time-start.

WHILE ls_time-elapsed < ls_time-limit.

      SELECT SINGLE * INTO @DATA(LS_XXX) FROM XXXX WHERE ...
 
      IF LS_XXX IS NOT INITIAL.
         EXIT.
      ENDIF.

  GET TIME STAMP FIELD ls_time-now.

  ls_time-elapsed = cl_abap_tstmp=>subtract(
    tstmp1 = ls_time-now
    tstmp2 = ls_time-start
  ).

ENDWHILE. " elapsed time

 4:使用模式为 U 或 V 的 ENQUEUE_XXXX 功能模块

当您使用适当的功能模块并使用模式 U、V(或 W)时,它将检查锁是否冲突。此处的 SAP 帮助示例:使用锁定模式 U、V 和 W - SAP 锁定概念 - SAP 库中对此进行了详细说明。这里没有提到的是 _WAIT 参数。将此参数设置为 (abap_true 或 'X') 将使函数调用等待预定时间以释放锁。美妙之处在于,这不仅适用于您设置锁定的标准模式,而且还适用于碰撞检查。

CALL FUNCTION 'ENQUEUE_EVVBAKE'
  EXPORTING
    mode_vbak      = 'V'              " Lock mode for table VBAK
    vbeln          = l_sales_order    " 02th enqueue argument
    _wait          = abap_true
  EXCEPTIONS
    foreign_lock   = 1
    system_failure = 2
    others         = 3.

这种方法的好处是无需手动实现等待逻辑。


5:增强

如果以上方式都不能满足需求,则可以考虑将BAPI复制出来,找到其中控制异步的逻辑,通过增强来添加同步提交控制参数,使得BAPI以同步方式提交,一个案例如下:SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更新导致COGI解决方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/DeveloperMrMeng/article/details/139811212?spm=1001.2014.3001.5501SAP 报工BAPI中的 UPDATA TASK 和 BACKGROUND TASK_abap in backround task 和in update task的区别-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/DeveloperMrMeng/article/details/140174352?spm=1001.2014.3001.5501


参考链接:

Updates in the SAP System (BC-CST-UP) | SAP Help Portalicon-default.png?t=N7T8https://help.sap.com/docs/SAP_NETWEAVER_701/6d9bbcd26c4b101488b4a6a282b09136/5f6f8337dd34ca76e10000009b38f8cf.html?locale=en-US&q=V2%20Update%28Asynchronous%20Update%29


以上,如有不对,欢迎指正。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-13 03:10:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-13 03:10:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-13 03:10:03       58 阅读
  4. Python语言-面向对象

    2024-07-13 03:10:03       69 阅读

热门阅读

  1. MySQL-锁

    2024-07-13 03:10:03       14 阅读
  2. 我的PHP8编译日志

    2024-07-13 03:10:03       19 阅读
  3. error: #29: expected an expression

    2024-07-13 03:10:03       18 阅读
  4. MySQL版本升级

    2024-07-13 03:10:03       18 阅读
  5. 数据建设实践之大数据平台(四)安装mysql

    2024-07-13 03:10:03       22 阅读
  6. Python-数据爬取(爬虫)

    2024-07-13 03:10:03       20 阅读
  7. 关于QT实现绘图库的技术栈考虑

    2024-07-13 03:10:03       20 阅读
  8. 使用Python绘制百分比堆积条形图

    2024-07-13 03:10:03       23 阅读
  9. How to Use shred to Erase a Drive or File in Fedora

    2024-07-13 03:10:03       22 阅读
  10. Postman接口测试工具详解

    2024-07-13 03:10:03       20 阅读