PackagePackagetypeA library you install and call yourself.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

zconcurrency_api

A Java-inspired ABAP library that runs tasks in parallel over SAP's SPTA framework without an RFC-enabled function module, capping concurrent tasks at 40% of the dialog work processes.

Parallel processing in ABAP traditionally means creating an RFC-enabled function module, implementing the logic inside it, calling it asynchronously in a loop and waiting to retrieve the results; SPTA removes the custom function module, and its low-level API still leaves serialisation, task lifecycle and error handling to the caller. This library takes a Context, Task and Result class the caller defines and an executor modelled on Java's java.util.concurrent package, and handles the rest; a separate facade package, ZCAPI_FACADE_HCM, splits personnel numbers into batches for HCM use cases. The maximum number of tasks running at once is capped automatically at 40% of the available dialog work processes to ensure system stability. SPTA is required, and batch input (BDC) is not supported, because SPTA does not allow BDC sessions in parallel task execution.

Facts

Last pushLicenceMIT

Where it sits

Lives in
Provides
Parallel processingABAPParallel processingcapabilitySplitting one run's work across several work processes so that it finishes sooner, and collecting what each part returned before the run goes on. [Async processing](capability:async-processing) is the neighbouring concern: work deferred past the request with a guarantee that it eventually happens, where parallel processing is throughput inside a run that waits for its results.
Requires
abapGitabapGitartifactInstalling the package into the ABAP system; the README names abapGit as the install method.

Who is behind it

Victor IzbitskiyVictor IzbitskiyPersonMaintains this
In the atlas: maintains 2, contributes to 4
@victorizbitskiy
maintainerLars HvamPersonContributes to this
In the atlas: maintains 13, contributes to 92, wrote 1
@larshp
1 contributor

What it declares

Every artifact

Type
Package
Origin
Independently maintained

As a package

Where it runs
Inside its host
Distributed through
abapGit

Connections2

README

quoted from victorizbitskiy/zconcurrency_api under MIT, read
victorizbitskiy/zconcurrency_api/README.mdOn GitHub

License: MIT ABAP 7.00+ Code Statistics

Translations:

ABAP Concurrency API

ABAP Concurrency API is's a Java-inspired library for parallel task execution in ABAP using the SPTA Framework.

💡 Note: The name references Java’s concurrency model, but under the hood, tasks run in parallel (not just concurrently) via RFC.


Don't forget to click ⭐ if you like the project!


Table of contents

  1. What is it?
  2. Why does it exist?
  3. Installation
  4. Usage
  5. Usage. HCM module
  6. Diagrams
  7. Dependencies
  8. Limitations
  9. How to contribute
  10. Got questions
  11. Logo

What is it?

A set of high-level ABAP utility classes that simplify parallel task execution using SAP’s SPTA Framework, without requiring manual RFC module creation.

Why does it exist?

Parallel processing in ABAP traditionally follows a tedious pattern:

  1. Create an RFC-enabled function module
  2. Implement your business logic inside it
  3. Call it asynchronously in a loop
  4. Wait for completion and retrieve the results

In practice, only steps 2 and 4 matter - the rest is repetitive plumbing that’s error-prone and hard to maintain.

While SAP’s SPTA Framework eliminates the need for custom RFC modules, its low-level API still forces developers to manage serialization, task lifecycle, and error handling manually - often leading to global variables and fragile code.

The ABAP Concurrency API abstracts all that away. You define what to run (your logic) and what to return (your result), and the framework handles how it runs in parallel - cleanly and safely.

Installation

Install the package via abapGit.

Usage

To illustrate the core concepts, consider a minimal example:
Compute the squares of the integers from 1 to 10 in parallel.

Each number is processed independently in its own parallel task.
While this example is intentionally simple, it demonstrates the essential workflow of the API-defining a task, executing it in parallel, and collecting results-without domain-specific complexity.

First, define three classes: Context, Task, and Result. These can be implemented as either local or global classes, depending on whether you need to reuse them across programs.

  1. lcl_context – encapsulates the input parameters for a task. Using a dedicated context class is optional; you could pass parameters directly to the task constructor. However, we strongly recommend using a separate context class to improve code clarity, maintainability, and type safety-especially when dealing with multiple parameters.
Show code...
CLASS lcl_context DEFINITION FINAL.
  PUBLIC SECTION.
    INTERFACES if_serializable_object.

    TYPES: 
      BEGIN OF ty_params,
        param TYPE i,
      END OF ty_params.

    METHODS: 
      constructor IMPORTING is_params TYPE ty_params,
      get RETURNING VALUE(rs_params) TYPE ty_params.

  PRIVATE SECTION.
    DATA ms_params TYPE ty_params.
ENDCLASS.

CLASS lcl_context IMPLEMENTATION.
  METHOD constructor.
    ms_params = is_params.
  ENDMETHOD.

  METHOD get.
    rs_params = ms_params.
  ENDMETHOD.
ENDCLASS.
  1. lcl_task – encapsulates the business logic of a parallel unit of work. This class must inherit from zcl_capi_abstract_task and implement the zif_capi_callable~call method, where you define what the task actually does. In this example, the task computes the square of a number provided via its context.
Show code...
CLASS lcl_task DEFINITION INHERITING FROM zcl_capi_abstract_task FINAL.
  PUBLIC SECTION.

    METHODS: 
      constructor IMPORTING io_context TYPE REF TO lcl_context,
      zif_capi_callable~call REDEFINITION.

  PRIVATE SECTION.
    DATA mo_context TYPE REF TO lcl_context.
    DATA mv_res TYPE i.
ENDCLASS.

CLASS lcl_task IMPLEMENTATION.
  METHOD constructor.
    super->constructor( ).
    mo_context = io_context.
  ENDMETHOD.

  METHOD zif_capi_callable~call.
   
    DATA(ls_params) = mo_context->get( ).
    mv_res = ls_params-param ** 2.
    
    ro_result = new lcl_result( iv_param  = ls_params-param
                                iv_result = mv_res ).
  ENDMETHOD.
ENDCLASS.
  1. lcl_result – represents the output of a completed task. This class must implement the if_serializable_object interface, because result objects are transferred between work processes via serialization. Beyond that requirement, you are free to design the structure of the result as needed for your use case-e.g., by exposing getters, defining internal tables, or formatting output strings.
Show code...
CLASS lcl_result DEFINITION FINAL.
  PUBLIC SECTION.
    INTERFACES if_serializable_object.

    METHODS: 
      constructor IMPORTING iv_param  TYPE i
                            iv_result TYPE i,
      get RETURNING VALUE(rv_result) TYPE string.

  PRIVATE SECTION.
    DATA mv_param TYPE i.
    DATA mv_result TYPE i.
ENDCLASS.

CLASS lcl_result IMPLEMENTATION.
  METHOD constructor.
    mv_param = iv_param.
    mv_result = iv_result.
  ENDMETHOD.

  METHOD get.
    rv_result = |{ mv_param } -> { mv_result }|.
  ENDMETHOD.
ENDCLASS.

⚠️ Important: Avoid static attributes All Context, Task, and Result objects are serialized and transferred between dialog work processes.

Static attributes are not serialized - they belong to the class metadata, not to the instance. Any data stored in them will not be transferred to the target process and may lead to inconsistent or incorrect behavior.

Always use instance attributes to hold task-specific state.

With the three core classes defined, let’s walk through a complete usage example.

Show code...
    CONSTANTS lc_server_group TYPE rfcgr VALUE 'parallel_generators'.
    DATA lo_result TYPE REF TO lcl_result.

    " Create collection of tasks
    DATA(lo_tasks) = NEW zcl_capi_collection( ).

    DO 10 TIMES.
   
      DATA(lo_context) = NEW lcl_context( VALUE lcl_context=>ty_params( param = sy-index ) ).
      DATA(lo_task) = NEW lcl_task( lo_context ).
      lo_tasks->zif_capi_collection~add( lo_task ).
   
    ENDDO.

    DATA(lo_message_handler) = NEW zcl_capi_message_handler( ).
    DATA(lv_max_no_of_tasks) = zcl_capi_thread_pool_executor=>max_no_of_tasks( lc_server_group ).
  
    DATA(lo_executor) = zcl_capi_executors=>new_fixed_thread_pool( iv_server_group         = lc_server_group
                                                                   iv_n_threads            = lv_max_no_of_tasks
                                                                   io_capi_message_handler = lo_message_handler ).
    TRY.
        DATA(lo_results) = lo_executor->zif_capi_executor_service~invoke_all( lo_tasks ).

        IF lo_message_handler->zif_capi_message_handler~has_messages( ) = abap_false.

          DATA(lo_results_iterator) = lo_results->get_iterator( ).

          WHILE lo_results_iterator->has_next( ).
            lo_result ?= lo_results_iterator->next( ).
            WRITE: / lo_result->get( ).
          ENDWHILE.

        ENDIF.

      CATCH zcx_capi_tasks_invocation INTO DATA(lo_capi_tasks_invocation).
        WRITE lo_capi_tasks_invocation->get_text( ).
    ENDTRY.
  1. Create a task collection lo_tasks.
  2. Instantiate a task lo_task and add it to the collection.
  3. (Optional) Create a message handler lo_message_handler to collect errors from parallel executions.
  4. Set up the executor - the core component that runs tasks in parallel.

In the example, we use the static method zcl_capi_executors=>new_fixed_thread_pool, which returns an executor configured to use a fixed number of parallel tasks. This method takes four parameters:

Parameter nameOptionalDescription
iv_server_groupserver group (tcode: RZ12)
iv_max_no_of_tasksmaximum number of parallel tasks
iv_no_resubmission_on_errorflag "true" - don't restart the task in case of an error
io_capi_message_handlerYesan object that will contain error messages (if they occurred)

The lo_executor object exposes a single interface method: zif_capi_executor_service~invoke_all(). This method accepts a collection of tasks and returns a collection of results lo_results, following the design pattern used in Java’s java.util.concurrent package*.

  1. The lo_results collection provides an iterator, which you can use to retrieve each lo_result and call its get() method. As a result, there’s no need to create an RFC function module or manually manage the parallelization logic. All you had to do was define what your Task does and what your Result looks like.

Result of execution:

result

A complete working example is available in the report ZCONCURRENCY_API_EXAMPLE.

Using the API in the HCM Module

To simplify parallel processing in the HCM module, the ZCAPI_FACADE_HCM package provides a Facade implementation (based on the Facade design pattern). This facade abstracts away common boilerplate tasks-such as splitting personnel numbers (pernr) into batches, creating task instances, and collecting results-so you can focus on your core business logic.

Let’s consider a simple use case: Retrieve the full names of employees by their personnel numbers.

As with the general API, you’ll need to define three classes: Context, Task, and Result-but now tailored to the HCM facade’s expectations.

  1. lcl_context – an instance of this class encapsulates the task parameters. It must inherit from the abstract class zcl_capi_facade_hcm_abstr_cntx and override its constructor method to accept and initialize the required parameters.
Show code...
CLASS lcl_context DEFINITION INHERITING FROM zcl_capi_facade_hcm_abstr_cntx FINAL.
PUBLIC SECTION.

  TYPES:
    BEGIN OF ty_params,
      begda TYPE d,
      endda TYPE d,
    END OF ty_params.

  METHODS:
    constructor IMPORTING is_params TYPE ty_params,
    get_params RETURNING VALUE(rs_params) TYPE ty_params.

PRIVATE SECTION.
  DATA: ms_params TYPE ty_params.

ENDCLASS.

CLASS lcl_context IMPLEMENTATION.
METHOD constructor.
  super->constructor( ).
  ms_params = is_params.
ENDMETHOD.

METHOD get_params.
  rs_params = ms_params.
ENDMETHOD.
ENDCLASS.
  1. lcl_task – represents the Task object and encapsulates the business logic (e.g., retrieving an employee’s full name by personnel number). This class must inherit from zcl_capi_facade_hcm_abstr_task, implement a constructor to accept the context, and override the zif_capi_callable~call method to define the actual task execution.
Show code...
CLASS lcl_task DEFINITION INHERITING FROM zcl_capi_facade_hcm_abstr_task FINAL.
  PUBLIC SECTION.

    METHODS:
      constructor IMPORTING io_context TYPE REF TO zcl_capi_facade_hcm_abstr_cntx,
      zif_capi_callable~call REDEFINITION.

  PRIVATE SECTION.
    DATA ms_params TYPE lcl_context=>ty_params.

ENDCLASS.

CLASS lcl_task IMPLEMENTATION.
  METHOD constructor.
    DATA lo_context TYPE REF TO lcl_context.

    " Set Pernrs numbers to mt_pernrs of Task
    super->constructor( io_context ).

    " Set Context parameters
    lo_context ?= io_context.
    ms_params = lo_context->get_params( ).

  ENDMETHOD.

  METHOD zif_capi_callable~call.
   
    DATA lt_employees TYPE lcl_result=>ty_t_employees.
    DATA ls_employees LIKE LINE OF lt_employees.
          
    " Simulates retrieving employee full names by personnel number.
    " The `ms_params` attribute (e.g., validity dates) is available here.
    " It’s not used in this example, but you can leverage it in your implementation.

    LOOP AT mt_pernrs ASSIGNING FIELD-SYMBOL(<ls_pernr>).
      ls_employees-pernr = <ls_pernr>-low.

      CASE <ls_pernr>-low.
        WHEN 00000001.
          ls_employees-ename = 'John Doe 1'.
        WHEN 00000002.
          ls_employees-ename = 'John Doe 2'.
        WHEN 00000003.
          ls_employees-ename = 'John Doe 3'.
        WHEN 00000004.
          ls_employees-ename = 'John Doe 4'.
        WHEN 00000005.
          ls_employees-ename = 'John Doe 5'.
        WHEN 00000006.
          ls_employees-ename = 'John Doe 6'.
        WHEN 00000007.
          ls_employees-ename = 'John Doe 7'.
        WHEN 00000008.
          ls_employees-ename = 'John Doe 8'.
        WHEN 00000009.
          ls_employees-ename = 'John Doe 9'.
        WHEN 00000010.
          ls_employees-ename = 'John Doe 10'.
        WHEN OTHERS.
      ENDCASE.

      INSERT ls_employees INTO TABLE lt_employees.
    ENDLOOP.

    ro_result = NEW lcl_result( lt_employees ).

  ENDMETHOD.
ENDCLASS.
  1. lcl_result – represents the result of a task’s execution. It must implement the zif_capi_facade_hcm_result interface. Beyond that requirement, you’re free to define its structure as needed for your use case.
Show code...
CLASS lcl_result DEFINITION FINAL.
  PUBLIC SECTION.
    INTERFACES zif_capi_facade_hcm_result.

    TYPES:
      BEGIN OF ty_employees,
        pernr TYPE n LENGTH 8,
        ename TYPE string,
      END OF ty_employees,

      ty_t_employees TYPE STANDARD TABLE OF ty_employees WITH KEY pernr.

    METHODS constructor IMPORTING it_employees TYPE ty_t_employees.

  PRIVATE SECTION.
    DATA mt_employees TYPE ty_t_employees.

ENDCLASS.

CLASS lcl_result IMPLEMENTATION.
  METHOD constructor.
    mt_employees = it_employees.
  ENDMETHOD.

  METHOD zif_capi_facade_hcm_result~get.
    et_result = mt_employees.
  ENDMETHOD.                    "get
ENDCLASS.

⚠️ Important: Instances of lcl_task and lcl_result are serialized and deserialized at runtime. Therefore, avoid using static attributes-they are not preserved during serialization and can lead to unexpected behavior.

With the Context, Task, and Result classes now defined, let’s walk through a complete example.

Show code...
    DATA lt_employees TYPE lcl_result=>ty_t_employees.

    " 2 Pernr number per task. For example only.
    " '200' will be fine.
    DATA(lv_package_size) = 2.
   
    DATA(ls_params) = VALUE lcl_context=>ty_params( begda = sy-datum
                                                    endda = sy-datum ).
    DATA(lo_context) = NEW lcl_context( ls_params ).

    DATA(lo_capi_facade_hcm) = NEW zcl_capi_facade_hcm( io_context         = lo_context
                                                        it_pernrs          = gt_pernrs
                                                        iv_task_class_name = 'LCL_TASK'
                                                        iv_package_size    = lv_package_size ).
    TRY.
        lo_capi_facade_hcm->execute( IMPORTING et_result = lt_employees ).

        WRITE `PERNR    ENAME`.
        LOOP AT lt_employees ASSIGNING FIELD-SYMBOL(<ls_employees>).
          WRITE: / <ls_employees>-pernr, <ls_employees>-ename.
        ENDLOOP.

      CATCH zcx_capi_tasks_invocation INTO DATA(lo_capi_tasks_invocation).
        WRITE lo_capi_tasks_invocation->get_text( ).
    ENDTRY.
  1. Create a context object lo_context that holds the parameters to be passed to each task (e.g., validity dates).
  2. Instantiate the HCM facade lo_capi_facade_hcm using its constructor, which accepts four parameters:
Parameter nameDescription
io_contextAn instance of your context class, containing task parameters
it_pernrsA list of personnel numbers (pernr) to process
iv_task_class_nameThe name of your task class (e.g., 'LCL_TASK')
iv_package_sizeThe number of personnel numbers assigned to each parallel task
  1. Call the execute() method on the facade object. This triggers parallel execution of tasks and returns the aggregated result.

💡 The maximum number of concurrently running tasks is automatically capped at 40% of available dialog work processes (visible in transaction SM50) to ensure system stability.

https://github.com/victorizbitskiy/zconcurrency_api/blob/a0507da3ef44c6a6a3da64e216a71e13655594ba/src/zcl_capi_thread_pool_executor.clas.abap#L70

That’s it-no manual task orchestration required.

Result of execution:

result

A complete working example is available in the report ZCAPI_FACADE_HCM_EXAMPLE.

Diagrams

UML Class Diagram ABAP Concurrency API

UML Class Diagram

UML Class Diagram ABAP Concurrency API for HCM module

UML Class Diagram HCM

UML Sequence Diagram

UML Sequence Diagram

Dependencies

SPTA package required.

Limitations

Batch input (BDC) is not supported. This restriction stems from the underlying use of SAP’s SPTA Framework, which does not allow BDC sessions in parallel task execution. For further details, refer to SAP Notes 734205 and 710920.

How to contribute

For guidance on contributing to this project, please follow the abapGit contribution guidelines.

Got questions?

If you have any questions or suggestions, feel free to open a new (GitHub issue).

Project logo designed by macrovector/Freepik

MIT, read
Parallel processing
  1. abapify/promisePromise and async/await implementation for ABAPfrom ABAP
  2. ABAPParallelizationServiceFramework for running ABAP work units in parallel tasksfrom ABAP
  3. COROUTINES4ABAPCoroutine framework for ABAP with awaitable resultsfrom ABAP
  4. RFC_TOOLSA wrapper class for calling asynchronous RFC function modules in parallel from ABAP, one instance per parallel task.from ABAP
  5. zthreadThreading library for ABAP modelled on Java's Thread and Runnablefrom ABAP

Details

Sources3
dotabapdiscovered herechecked
github.com/victorizbitskiy/zconcurrency_apitree, pushes, contributors, CIchecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
discovered viadotabap
first seen in the atlas
ecosystem and domainabap, operations-and-runtime
abaplint
issues under its own abaplint configuration0
lines1,873
statements1,042
objects28
files54
analysed, main
abaplint version2.120.84
Contributors1

From the repository’s contributor list, .

History in the atlas1
  1. First seen.
For your agent
Ref
artifact:victorizbitskiy-zconcurrency-api
JSON
/artifacts/victorizbitskiy-zconcurrency-api.json
MCP
Connecting an agent