SAP 报工BAPI中的 UPDATA TASK 和 BACKGROUND TASK

前言:

记录这篇文章起因是调查生产订单报工问题引申出来的一个问题,后来再次调查后了解了其中缘由,大概记录以下,如有不对,欢迎指正。问题原贴如下:

SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更新导致COGI解决方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/DeveloperMrMeng/article/details/139811212?spm=1001.2014.3001.5501

当时疑惑的点是为什么更新进程明明是被COMMIT WORK AND WAIT触发的,但为什么仍然会有锁定问题,经过后来再次debug后发现了问题所在,在更新进程中,货物移动的处理是放在一个background task异步tRFC去处理的,是一个立即开始独立运行的进程,主程序中的同步提交只会等更新进程执行完,并不会等这个异步tRFC执行完毕,因为是不同的进程,而这个异步tRFC中在货物移动时会再次对订单上锁,所以当这个异步tRFC没处理完时,立即进行下一次报工就有可能出现锁定问题。

针对于BAPI异步提交问题可以看这篇:

SAP ABAP调用BAPI时COMMIT WORK AND WAIT未按照预期同步提交问题分析-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/DeveloperMrMeng/article/details/140377250?spm=1001.2014.3001.5501


tRFC流程:


问题说明:

针对于报工BAPI,大致的执行流程如上图所示,

  1. 将所有检查通过的数据库提交任务绑定至同一个LUW;
  2. 由COMMIT WORK AND WAIT触发同步提交;
  3. Debug模式可以在SM13中观测到所有注册的更新模块,并且主程序中的锁会被带到更新进程中去,系统将按照注册顺序依次执行,通常这些模块中只包含数据库增删改操作,执行完所有模块后,系统统一提交,并释放所有锁,如果失败,则会统一回滚,并会收到一个dump快件,也会释放所有锁;
  4. 在标准设计中,货物移动的处理是预先存在AFFW表中,由最后一个更新模块(CO_RU_VB_CONFIRMATION_POST)处理,在这个模块中,将货物移动的处理放在了BACKGROUND TASK中,查看F1说明文档可以看到这是一种已过时的用于绑定事务性RFC的技术,通过该方式注册的函数会立即开始,并且独立异步执行,通过进一步debug,发现货物移动的处理涉及生产订单处理时会再次对订单上锁;
  5. 程序结束,因为IN BACKGROUND TASK注册的函数是独立的进程,所以如果该进程还没有执行完毕,在下一次针对该订单进行报工的时候就会出现锁定错误;
    1. 本次执行结束后,第二次针对同一订单处理时,如果异步RFC执行完毕,则第二次将会成功处理(报工成功,货物移动成功);
    2. 如果报工时异步RFC尚未处理完毕,第二次处理则会因为锁定直接报错(报工失败,货物移动失败);
    3. 如果第二次报工时上一次的异步RFC尚未开始处理,但处理第二次货物移动时刚好上一次的异步RFC处理已经开始,则可能会出现报工成功,货物移动因为锁定进入COGI(报工成功,货物移动失败);

这也是为什么调用了BAPI之后COMMIT WORK AND WAIT或者调用BAPI前指定了SET UPDATE LOCAL TASK没有生效的原因。


标准BAPI Debug确认过程:

设置好断点,并打开系统调试和更新调试。

同步提交后进入更新进程:

SM13可以看到注册了以下的更新函数模块,其中对自动货物移动的处理在最后一个函数中:

继续调试:

针对最后一个函数重点关照:

标准逻辑就是在此处触发异步的TRFC去处理货物移动的逻辑,此时打开TRFC调试,则该函数模块不会立即执行,会使其注册到SM58,方便进行后续手工处理调试,如果不勾选则会立刻执行,不会等待手工处理。

F8执行之后,可以看到此时已返回主程序,针对订单的锁也已经释放,但处理货物移动的函数被我们注册到了SM58,等待处理,所以SM58进行下一步调试,看看里面做了些什么。

SM58:

在下面这段处理中将会对订单再次上锁:

只有等该函数执行结束后才会释放锁,所以如果此时再次对该订单发起报工时就会出现锁定问题。

以上差不就是整个关键的debug过程,为了再次验证这个猜想,我重新写了一份测试代码进行验证,以下代码可以复现还原这个问题。


测试代码:

*&---------------------------------------------------------------------*
*& Report ZUPDATE_TEST
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zupdate_test.

DATA:
  lt_test TYPE STANDARD TABLE OF ztest_trfc,
  ls_test TYPE ztest_trfc.


DO 1 TIMES.
  TRY .
      ls_test-uuid  = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ).
    CATCH cx_uuid_error.

  ENDTRY.

  ls_test-uname = sy-uname.

  CALL FUNCTION 'ZNOMAL_TEST'
    EXPORTING
      i_test = ls_test.

  COMMIT WORK AND WAIT.

  IF sy-subrc = 0.
    WRITE:'Update success:',ls_test-uuid.
  ELSE.
    WRITE:'Update failed:',ls_test-uuid.
  ENDIF.
ENDDO.

FUNCTION znomal_test.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     VALUE(I_TEST) TYPE  ZTEST_TRFC
*"----------------------------------------------------------------------
  CALL FUNCTION 'ENQUEUE_E_TABLE'
    EXPORTING
*     MODE_RSTABLE   = 'E'
      tabname        = 'ZTEST_TRFC'
*     VARKEY         =
*     X_TABNAME      = ' '
*     X_VARKEY       = ' '
*     _SCOPE         = '2'
*     _WAIT          = ' '
*     _COLLECT       = ' '
    EXCEPTIONS
      foreign_lock   = 1
      system_failure = 2
      OTHERS         = 3.
  IF sy-subrc <> 0.
    MESSAGE 'Lock Failed!' TYPE 'E'.
  ENDIF.

  CALL FUNCTION 'ZCALL_UPDATE_TASK' IN UPDATE TASK
    EXPORTING
      i_test = i_test.

ENDFUNCTION.

FUNCTION zcall_update_task.
*"----------------------------------------------------------------------
*"*"更新函数模块:
*"
*"*"本地接口:
*"  IMPORTING
*"     VALUE(I_TEST) TYPE  ZTEST_TRFC
*"----------------------------------------------------------------------

  CALL FUNCTION 'ZBACKGROUND_TASK_TEST' IN BACKGROUND TASK
    EXPORTING
      i_test = i_test.

ENDFUNCTION.

FUNCTION zbackground_task_test.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     VALUE(I_TEST) TYPE  ZTEST_TRFC
*"----------------------------------------------------------------------

  SET UPDATE TASK LOCAL.

  DO.
    SELECT SINGLE * INTO @DATA(ls_lock1) FROM z13065_lock WHERE upd_id = '13065'.
    IF ls_lock1-zlock = '1'.
      EXIT.
    ENDIF.
  ENDDO.

  CALL FUNCTION 'ENQUEUE_E_TABLE'
    EXPORTING
*     MODE_RSTABLE   = 'E'
      tabname        = 'ZTEST_TRFC'
*     VARKEY         =
*     X_TABNAME      = ' '
*     X_VARKEY       = ' '
*     _SCOPE         = '2'
*     _WAIT          = ' '
*     _COLLECT       = ' '
    EXCEPTIONS
      foreign_lock   = 1
      system_failure = 2
      OTHERS         = 3.

  IF sy-subrc <> 0.
    MESSAGE 'Lock Failed!' TYPE 'E'.
  ENDIF.

  DO.
    SELECT SINGLE * INTO @DATA(ls_lock2) FROM z13065_lock WHERE upd_id = '13065'.
    IF ls_lock2-zlock = '2'.
      EXIT.
    ENDIF.
  ENDDO.

*  CALL FUNCTION 'ZUPDATE_TASK_TEST' IN UPDATE TASK
*    EXPORTING
*      is_test = i_test.

  MODIFY ztest_trfc FROM i_test.

  COMMIT WORK.

ENDFUNCTION.

通过LOCK表中的标识字段可以控制进程执行位置,测试过程就不贴图了,感兴趣的可以自己debug看看,根据观察SM12,SM50,SM13,SM58等标准事务代码中的信息就可以理解整个执行过程。


总结: 

大多数标准BAPI的更新都是放在更新进程(CALL FUNCTION XXX IN UPDATE TASK)中去统一提交数据库更新的,可以根据COMMIT WORK是否添加AND WAIT附加项来决定是否同步更新,而CALL FUNCTION XXX IN BACKGROUND TASK则以已单独进程进行异步更新的,独立于对话框程序,属于两种互相独立的更新进程。


参考资料:

在后台任务中调试 FM - SAP Community

什么是LUW?LUW如何运作?不同类型的 LUW ... - SAP 社区

SAP LUW - ABAP 关键字文档

Transactional RFC (tRFC) | SAP Help Portal

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DeveloperMrMeng

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

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

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

打赏作者

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

抵扣说明:

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

余额充值