当前位置: 首页 > news >正文

SAP中的 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没处理完时,立即进行下一次报工就有可能出现锁定问题。


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'EXPORTINGi_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       = ' 'EXCEPTIONSforeign_lock   = 1system_failure = 2OTHERS         = 3.IF sy-subrc <> 0.MESSAGE 'Lock Failed!' TYPE 'E'.ENDIF.CALL FUNCTION 'ZCALL_UPDATE_TASK' IN UPDATE TASKEXPORTINGi_test = i_test.ENDFUNCTION.

FUNCTION zcall_update_task.
*"----------------------------------------------------------------------
*"*"更新函数模块:
*"
*"*"本地接口:
*"  IMPORTING
*"     VALUE(I_TEST) TYPE  ZTEST_TRFC
*"----------------------------------------------------------------------CALL FUNCTION 'ZBACKGROUND_TASK_TEST' IN BACKGROUND TASKEXPORTINGi_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       = ' 'EXCEPTIONSforeign_lock   = 1system_failure = 2OTHERS         = 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

http://www.lryc.cn/news/393244.html

相关文章:

  • UDP协议:独特之处及其在网络通信中的应用
  • 支持向量机(Support Vector Machine,SVM)及Python和MATLAB实现
  • 【RT-thread studio 下使用STM32F103-学习sem-信号量-初步使用-线程之间控制-基础样例】
  • 使用nodejs输出著作权申请所需的word版源码
  • [Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解
  • 记一次使用“try-with-resources“的语法导致的BUG
  • 用Excel处理数据图像,出现交叉怎么办?
  • SpringBoot | 大新闻项目后端(redis优化登录)
  • ESP32——物联网小项目汇总
  • flutter:监听路由的变化
  • Linux多进程和多线程(六)进程间通信-共享内存
  • ruoyi后台修改
  • macOS查看系统日志的方法
  • 数字信号处理及MATLAB仿真(3)——采样与量化
  • 云端AI大模型群体智慧后台架构思考
  • 算法系列--分治排序|再谈快速排序|快速排序的优化|快速选择算法
  • 强化学习编程实战-1-一个及其简单的强化学习实例(多臂赌博机)
  • Golang语法规范和风格指南(一)——简单指南
  • 数据机构记录顺序表-笔记1
  • 考研必备~总结严蔚敏教授《数据结构》课程的重要知识点及考点
  • 【数据分享】国家级旅游休闲街区数据(Excel/Shp格式/免费获取)
  • Linux开发:进程间通过Unix Domain Socket传递数据
  • Redis基础教程(九):redis有序集合
  • Servlet与Servlet容器
  • 腾讯centos mysql安装
  • c_各个unsigned int 和 int的取值范围
  • C#/WPF 自制截图工具
  • 以腾讯为例,手把手教你搭建产品帮助中心
  • 计算机网络概述--自我学习用
  • 超级好用的java http请求工具