Using runAsync(), parallelism and D365FO batch jobs

I have always thought that the code always knows if it is running in batch job and can get the batch job context. But it has turned out that it is not so clear 😉.

While troubleshooting some problem we have found out that the standard runAsync() method is executed in another session (using another thread). If it is called from code running in a batch job, the standard batch API cannot find the right batch info based on the session ID. Therefore, the code does not know the batch context, but it is aware that it runs in batch.

In this article we will explain how runAsync() works, how to run operations in parallel, wait for them to finish, and collect the results. Our use case will be parallel document report generation resulting in one ZIP file. We will show also technical details and tricks for using it in interactive session or in batch job.

runAsync()

The global method runAsyn() can be used to run any static method in parallel session (running in separate thread). In X++, this means a separate session ID, transaction scope, and static variables. Therefore, it is usually thread safe, but it is hard to communicate with the caller. If such communication is needed use the database, global cache or .NET static objects.

Notes:

  • Parameters are passed as a container (official documentation is inaccurate), which means they are serialized. If the called method does not accept it, make a wrapper.
  • Optionally you can also set userID, company and language.
  • It accepts also optional callback parameter where you can handle the method result or exception, but you can achieve the same by adding this code in the called method.
  • It returns System.Threading.Tasks.Task that you can wait for or simply ignore and forget it.

Returning result

Async session typically does some processing and stores the results in the database. In our sample we do not need it and use instead global cache (SysGlobalObjectCache). It is a key-value dictionary shared among all sessions on the same AOS (async session is always on the same node as the main session). We pass the main session ID as async method parameter and then it stores the result in global cache using that ID in the key.

Batch jobs

runAsync() can be called from an interactive session or from a batch job. The batch job processor executes each batch task in one of the available batch (worker) sessions. If the batch job code calls runAsync(), it executes the called method in a separate async session, which is not one of the available batch sessions.

Here comes the catch. The standard batch API (for example, BatchHeader::getCurrentBatchHeader()) looks up current batch info by the current session ID in the Batch and SysServerSessions tables. As the async session has a different session ID, it returns null. There is no way to get the batch info from async session itself, but you can use the workaround passing the caller session ID as async method parameter and use it to get the batch info.

Parallelism

You can use runAsync() to run several operations in parallel. In our sample we generate several documents in parallel, wait for them to complete and then ZIP them in one file. Note that each parallel session runs in additional thread and you should keep the number of all running threads under certain limits. If you exceed the limits the overall system efficiency will first decrease, then the system might block or even crash.

The general rule is that number of running threads <= 0,5-2 threads/logical CPU core.

As on D365FO cloud environments we do not know infrastructure details the general rule is 8 threads/AOS, but you can test with slightly higher number as long as it improves the results.

Note that the total number of running threads includes also other threads on interactive or batch AOS. For batch jobs it is usually better to use runtime batch tasks where execution is controlled by batch processing.

Sample code

Summary

The article explains how runAsync() works and the specifics that you need to handle. The sample code demonstrates parallel document report generation in multiple async sessions, waiting for their completion and using the result. It shows the use of global object cache for inter-session communication and is designed to run both in batch job or interactively.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Docentric respects your privacy. Learn how your comment data is processed >>

Docentric respects your privacy. Learn how your comment data is processed >>