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

目录

背景:

BAPI的执行原理:

更新进程的处理流程:

V1更新和V2更新区别:

COMMIT WORK AND WAIT不生效的原因:

标准BAPI事务模型规范:

如何解决:

1:分组处理

2:SET UPDATE LOCAL TASK

3:使用时间戳+WHILE

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

5:增强


背景:

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


BAPI的执行原理:

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

:报表程序中调用BAPI;

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

如果BAPI本身比较规范的话,一般会有类似COMMIT_WORK以及COMMIT_WORK_AND_WAIT等类似的传入参数,如果我们需要BAPI调用完之后要立即对生成的单据进行后续操作,那么我们最好是通过参数使其内部以同步方式提交,或者使其内部不要提交,由调用者来决定是否已同步方式提交(AND WAIT附加项决定了更新进程是否以同步模式提交),避免BAPI内部异步提交COMMIT WORK(没有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更新和V2更新区别:

V1 and V2 Update Modules | SAP Help Portalicon-default.png?t=N7T8https://help.sap.com/docs/SAP_NETWEAVER_700/10970dcb6c531014af68b7c1d32e9eab/e5de86f335cd11d3acb00000e83539c3.html更新函数模块有两种类型,SAP系统区分了主要、时间关键型(V1)和辅助、非时间关键型(V2)更新模块。该系统还支持对定期使用的功能模块进行集体运行

这种区别允许系统在不太关键的更改之前处理关键的数据库更改。

  • V1 模块包含关键或主要更改;这些影响在 SAP 系统中具有控制功能的对象,例如订单创建或物料库存更改。

  • V2 模块包含不太重要的二次更改。这些是纯粹的统计更新,例如结果计算。

V1 模块在同一应用程序服务器上的单个更新工作进程中连续处理。这意味着它们属于同一个数据库 LUW,并且可以反转。此外,V1 更新是在创建更新的事务的 SAP 锁下执行的(请参阅 SAP 锁概念 )。这确保了数据保持一致;无法同时更改要更新的对象。

所有 V2 更新都在单独的 LUW 中执行,而不是在创建它们的事务的锁定下进行。如果 SAP System 包含 V2 更新的工作流程,则这些更新仅在此工作流程中执行。如果不是这种情况,则 V2 组件由 V1 更新过程处理。

更新的所有 V1 模块都必须在 V2 模块之前处理。

简而言之,V1更新比V2更新优先级更高,V1更新在被触发时,可以根据是否附加AND WAIT来决定是同步更新还是异步更新,并且如果V1和V2更新同时被注册时,V2的同步与否和V1保持一致(如果V1被同步提交触发,则V2也会同步提交);如果仅注册了V2更新,则V2更新始终以异步方式执行(即使通过SET UPDATE LOCAL TASK指定了本地更新,或者使用了COMMIT WORK AND WAIT)。


COMMIT WORK AND WAIT不生效的原因:

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

  • BAPI没有NO_COMMIT以及COMMIT_WORK_AND_WAIT等类似的参数,直接在内部执行了COMMIT WORK语句(没有附加AND WAIT),所以已经注册的SAP LUW将会被异步执行更新,这种时候我们调用BAPI之后写的COMMIT WORK AND WAIT语句针对的提交则是BAPI内部COMMIT WORK语句之后,到我们主程序COMMIT WORK AND WAIT之间产生的数据库更新语句,通常情况下是没有东西可更新的,所以调用BAPI之后的同步提交将不会是我们预期的效果,类似的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内部注册的更新进程均为V2更新,不包含V1更新,这种情况,即使通过SET UPDATE LOCAL 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秒都是不能容忍的,所以当BAPI本身包含NO_COMMIT或者COMMIT_WORK_AND_WAIT这些参数时,我们最好使用这些参数来抑制BAPI内部提交,由调用者通过BAPI_TRANSACTION_COMMITWORK(WAIT = 'X')或者COMMIT WORK AND WAIT来触发同步提交,如果没有这些参数,BAPI内部始终以COMMIT WORK异步提交时,则可以有以下几种替代方式来处理异步提交问题。

至于判断BAPI内部是否直接异步提交最简单的办法就是打个断点,观察执行时断点是否会停留在该语句位置。

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语句来开启本地更新。

该方式仅在BAPI的更新进程包含V1更新时(或者同时包含V1和V2)适用,如果BAPI的更新进程仅包含V2更新或者V1更新中又嵌套了异步RFC更新,则不适用该方式,例如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次,每次等待1秒,由配置参数文件决定,主要参数如下:

enq/client/delay_max:控制最长等待时间,默认5秒;

enq/client/delay_max_refine:控制每秒重试次数,默认1次,最多100次;

enq/client/delay_jitter:防止多用户同时请求导致死锁的偏移量设置;

虽然可以通过配置文件去改变每秒循环次数,但由于此种方式在默认情况下等同于循环等待1秒的操作,并且wait本身就是隐式提交的过程,所以不推荐该方式处理。


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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeveloperMrMeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值