Copyright 2020 - BV TallVision IT

An interresting approach to tackling performance problems - is executing steps as background tasks. Parallel processing or starting new tasks can be done from a dialog task as well as a background task. Here's how it works, and here's also why you should NOT do this. 

Parallel processing - in a nutshell

If a job takes too long, it should be scheduled. If that too takes too long it should be run in segments, scheduling parts of the job. Or the task at hand should be executed "starting new tasks", which is an not very well known option on the much loved CALL FUNCTION statement STARTING NEW TASK. The idea is that an RFC-enabled function module performs a heavy part of the task to be executed. Several of these function module calls are done, which will perform their tasks in the background. When results are ready, they are passed back to the original program.

The inventor of this setup is (of course) SAP itself, implemented in report RWSORT61 for example. This report creates material listings which is effectively a lot of work. The work to be done is packaged and executed via a call to function module GENERATE_MATERIAL_SEGMENTS. It's what the big boys do, not necessarily what you should do.

To demonstrate how this works, I've set up a small example program, which read data from the MARA table through RFC-ready function module RFC_READ_TABLE. It simply reads 50.000 lines from the MARA table, to have "something to do". 3 such requests are put out there and the results for these requests are "received" in routine RESULT_RECEIVER. Results can also be received with a method (CALLING method ON END OF TASK).

The system will have limited resources, so the actual function call could fail with an added "system exception" RESOURCE_FAILURE. This exception won't be on the list of exceptions of your RFC function module, but the system may still throw it.

REPORT ABAPCADABRA_STARTING_TASK.
DATA: gt_options TYPE STANDARD TABLE OF rfc_db_opt,
      gt_fields TYPE STANDARD TABLE OF rfc_db_fld,
      gv_task type c length 8,
      gv_rfcgr type bdfields-rfcgr,
      gt_log_messages type standard table of text80,
      gv_message type text80,
      gv_sheep_accounted_for type i.

define log_add.
* A small macro just to log the events in this demo report
  get time.
  write sy-uzeit to gv_message USING EDIT MASK '__:__:__'.
  concatenate gv_message &1 into gv_message SEPARATED BY space.
  replace '&' in gv_message with &2.
  append gv_message to gt_log_messages.
end-of-definition.

log_add 'Start report' ''.
clear: gt_options[].
append 'MTART <> SPACE' to gt_options.
clear: gt_fields[].
append 'MATNR' to gt_fields.

clear: gv_sheep_accounted_for.
do 3 times.
  write: sy-index to gv_task LEFT-JUSTIFIED.
  concatenate 'T' gv_task into gv_task.
  condense gv_task NO-GAPS.
* A function that selects data - that's all..
  CALL FUNCTION 'RFC_READ_TABLE'
    STARTING NEW TASK gv_task
    DESTINATION IN GROUP gv_rfcgr
    PERFORMING result_receiver ON END OF TASK
    EXPORTING
      query_table          = 'MARA'
      rowcount             = 50000
    TABLES
      OPTIONS              = gt_options
      fields               = gt_fields
*      data                 = lt_data
    EXCEPTIONS
      RESOURCE_FAILURE     = 2
      OTHERS               = 4.
  if sy-subrc = 2.
    log_add 'Background task & COULD NOT BE STARTED' gv_task.
  else.
    log_add 'Background task & started' gv_task.
  endif.
enddo.

log_add 'Starting the wait' ''.
wait until gv_sheep_accounted_for = 3.
log_add 'Ending the wait' ''.

loop at gt_log_messages into gv_message.
  write: / gv_message.
endloop.

FORM result_receiver USING pv_task_id TYPE clike.
  data: lt_data TYPE STANDARD TABLE OF tab512.

  RECEIVE RESULTS
    FROM FUNCTION 'RFC_READ_TABLE'
    TABLES
      data                 = lt_data.

  log_add 'Results received from task &' pv_task_id.
  add 1 to gv_sheep_accounted_for.

ENDFORM. 

The job at hand is split into 3 partial jobs, which run in parallel. Very fast and effecient - but only when there are enough Dialog processes available, because a single report run consumes 4 dialog processes when run in the foreground and 3 dialog processes and a background process, when run in the background.

The output of the above report will take 20 seconds to show:

Batchjob tester

16:43:52 Start report
16:43:52 Background task T1 started
16:43:52 Background task T2 started
16:43:52 Background task T3 started
16:43:52 Starting the 20 second wait
16:43:57 Results received from task T1
16:43:57 Results received from task T2
16:43:57 Results received from task T3
16:44:12 Ending the 20 second wait, finish report

Don't wait around these 20 seconds, start transaction SM50 process overview and watch what really happens. Dialog processes are consumed here !

And yes: just to re-confirm: these "spin-off" tasks are spun off as Dialog tasks on our system, even if the original report was started as a background task !

Overloading the system

So the test-setup consumes 3 additional dialog processes. My system has 10 DIA processes, so just for trial sake I ran my test program starting 10 RFC-calls. They all started, but 2 of them throw the RESOURCE_FAILURE exception, so only 8 reported "results received".

Batchjob tester

15:29:27 Start report
15:29:27 Background task T1 started
15:29:27 Background task T2 started
15:29:27 Background task T3 started
15:29:27 Background task T4 started
15:29:27 Background task T5 started
15:29:27 Background task T6 started
15:29:27 Background task T7 started
15:29:27 Background task T8 started
15:29:27 Background task T9 COULD NOT BE STARTED
15:29:27 Background task T10 COULD NOT BE STARTED
15:29:27 Starting the 20 second wait
15:29:33 Results received from task T4
15:29:34 Results received from task T6
15:29:34 Results received from task T8
15:29:34 Results received from task T1
15:29:34 Results received from task T7
15:29:34 Results received from task T3
15:29:34 Results received from task T2
15:29:34 Results received from task T5
15:29:47 Ending the 20 second wait, finish report

The system won't hang - but it's good to realize this is really getting close. Put me on a development quality inspection team and I would never allow this approach. A few hints and tips - if you do decide to utilize these features: 

  • Check the exception RESOURCE_FAILURE when starting your function module call - it's the earliest sign the task is not started
  • Instead of WAIT UNTIL .... you could also use the WAIT FOR ASYNCHRONOUS TASKS, which is available for this purpose
  • The help text on the CALL FUNCTION - STARTINK NEW TASK option holds a link on Thresholds for Resource Allocation for Asynchronous RFC, which explains when the above exception will be thrown. This is clearly aimed at protecting DIA processes (which are very much needed to keep the system alive and chatty)
  • Before starting your forked processing, gather information on available system resources. Modules TH_WPINFO and TH_COUNT_WPS may be helpful.
  • Don't be suprised if your task dumps because of a time out. Remember: the function module calls that are started as their own task will spin off a dialog session, which will be stopped by the system once the time out time has passed...