October 3, 2010 at 13:57
filed under Dynamics AX
Tagged Batch, Dynamics AX, Multithreading
Do you have batches that run for hours? After reading this post (and doing some coding), they might just run in minutes.
How? By creating multiple tasks in your batch job at runtime. Read on :-).
Normally, when you schedule your RunBaseBatch class as a batch job, it will create one task.
You can check this under Basic – Periodic – Batch Job when you have scheduled a batch job.
In a diagram, it looks like this:
This batch task will execute everything on one batch session on one batch server.
But in AX 2009, your batch group can be added to multiple AOS server, and an AOS server can process multiple tasks simultaneously. So how can we use this to speed up our batch job?
We do it by splitting up our task in multiple smaller ones, which are then distributed over multiple batch sessions (threads) and over multiple AOS servers.
Like this:
In this example, I will loop all my customers, and simulate a process that runs for 3 seconds by using the sleep() function.
Note: download the .xpo file below.
KlForUpdateCustomers
This class contains this in its run method (see xpo file for other methods):
KlForUpdateCustomersSingleTheadBatch
This class is our batch class so it extends RunBaseBatch. It has a queryRun, and has this run method:
It loops all customers, and makes a call to the class we created earlier for each customer. To keep it simple, I did not add exception handling to this example.
When we run this in batch, a single batch task is created in the batch job that runs for about 3 minutes (according to the batch job screen).
KlForUpdateCustomers
This class is the same as before and processes one customer (CustTable record).
KlForUpdateCustomersMultiThreadTask
This class extends RunBaseBatch and represents one of the many batch tasks we will be creating. In this example it processes one CustTable record, so a task will be created for each CustTable record.
The run method looks like this:
KlForUpdateCustomersMultiThreadBatch
This is our batch job class, and this is where the real magic happens. This class will create multiple instances of KlForUpdateCustomersMultiThreadTask and add them as a task to our job at run time.
This is how the run method looks:
As you can see, for each custTable record, we create a new task. When the batch job doesn’t run in batch, we process it as we otherwise would using one session.
In the batch tasks screen (Basic – Inquiries – Batch Job – (select you batch job) – View Tasks), you can clearly see what happens.
First, the status is waiting, and one task has been created.
Then, when the batch job is executing, we can see that multiple tasks have been added to our batch job.
We can also see that it processes 8 tasks at the same time! This is bacause the maximum number of batch threads is set to 8 on the batch server schedule tab of the Server configuration screen (Administration – Setup – Server configuration).
Finally, we can see that the job has ended, and that all runtime tasks have been moved to the batch job history:
You can view the history and the log of this batch job:
Notice how the executing time has gone from 3 minutes to 1 minute according to the batch job screen.
Alternatively, you could use a queryrun in you batch tasks class too. You could for example create a batch group per customer group if that makes more sense to you. The ‘per record’ approach is just an example, just do what makes the most sense in your situation. Sometimes it is better if the runtime tasks have a bit more to do, to counter the overload of creating the tasks.
Spread the word :-).
Download the XPO file by clicking here.
Update 2011/02/11:
There were two errors in the example:
This line:
Has been replaced with:
And this line:
Has been replaced with:
Todo on my part: update the XPO.
casperkamal
on October 3, 2010 at 23:30
good one… that helps :)
Jeroen Doens
on October 5, 2010 at 19:51
I didn’t know this was possible. I always create a batch that launches a new batch with several tasks (http://www.doens.be/2010/04/batch-job-performance-boost), but I like your solution more.
Klaas Deforche
on October 10, 2010 at 23:23
I’m glad you agree Jeroen.
This really shows how powerful the batch framework in AX 2009 is.
Dilip
on February 5, 2011 at 07:07
Nice article Klaas and thanks for the detailed steps. We have around 130 batches in our implementation and batch tasks with multi-threading running on an AOS Cluster will help boost up the performance.
Shailesh Kumar
on February 19, 2011 at 22:20
Nice article and elaborated very well…
kittu
on January 8, 2013 at 17:04
System.NullReferenceException: Object reference not set to an instance of an object.
at Dynamics.Ax.Application.SalesLineforUpdateMultithreadBatch.Run() in SalesLineforUpdateMultithreadBatch.run.xpp:line 40
at Dynamics.Ax.Application.BatchRun.runJobStaticCode(Int64 batchId) in BatchRun.runJobStaticCode.xpp:line 54
hi ,
i am creating a multithreadbatch class , got one error , could u help me.
at Dynamics.Ax.Application.BatchRun.runJobStatic(Int64 batchId) in BatchRun.runJobStatic.xpp:line 13
at BatchRun::runJobStatic(Object[] )
at Microsoft.Dynamics.Ax.Xpp.ReflectionCallHelper.MakeStaticCall(Type type, String MethodName, Object[] parameters)
at BatchIL.taskThreadEntry(Object threadArg)
Klaas Deforche
on January 8, 2013 at 18:09
Hi Kittu,
What version of AX are you using?
Could you post the line that throws this error?
What object is not set to an instance of an object?
kittu
on January 8, 2013 at 21:33
I am working on Ax2012 R2. when ever i run my Multitheadbatch status will be going on executing then status Will be change on Error
the Error Message Is:
System.NullReferenceException: Object reference not set to an instance of an object.
at Dynamics.Ax.Application.SalesLineforUpdateMultithreadBatch.Run() in SalesLineforUpdateMultithreadBatch.run.xpp:line 40
at Dynamics.Ax.Application.BatchRun.runJobStaticCode(Int64 batchId) in BatchRun.runJobStaticCode.xpp:line 54
at Dynamics.Ax.Application.BatchRun.runJobStatic(Int64 batchId) in BatchRun.runJobStatic.xpp:line 13
at BatchRun::runJobStatic(Object[] )
at Microsoft.Dynamics.Ax.Xpp.ReflectionCallHelper.MakeStaticCall(Type type, String MethodName, Object[] parameters)
at BatchIL.taskThreadEntry(Object threadArg)
Klaas Deforche
on January 8, 2013 at 22:17
Hi Kittu,
If you are using AX2012, I would suggest you use runtime tasks in the way described here:
http://www.artofcreation.be/2012/02/27/adding-runtime-tasks-to-a-business-operation-framework-service/
I will analyze the stacktrace and get back to you :) (via email).
kittu
on January 9, 2013 at 14:54
thanks for Replay,
I am New in ax .could u give how to implement MultitheadingBatchJobs In Ax2012 R2 with an examples like previoes post.
Thanks u.
Klaas Deforche
on January 9, 2013 at 17:12
Hi Kittu,
In AX 2012, the prefered way of creating batch jobs is using services (Sysoperation aka BOF), not runbasebach.
If you want to know how to create a service, start here:
http://www.artofcreation.be/2011/08/21/ax2012-sysoperation-introduction/
When you are familiar with creating services, you can add runtimetasks (aka multi threading) as described here:
http://www.artofcreation.be/2012/02/27/adding-runtime-tasks-to-a-business-operation-framework-service/
Also, I have co-authored a book about services an sysoperation where this is explained in detail: Microsft Dynamics AX 2012 Services. There is a chapter that deals with batches an multithreading with examples you can download.
Check the link to amazon on top :).
Kishore
on April 11, 2013 at 22:27
Hi,
Thanks a lot for demo. From the above description i’ve created a batch Job with multi threading capability.
I’m facing the issue while logging the error in the error log.
I’m not able to see any errors in the error log.
Could anyone help me identify the issue.
Thanks,
Kishore.
Herman Kanter
on October 1, 2013 at 05:24
I noticed that in your multi-tasking infolog, that there are no CustIds listed “updated customer”. Does this mean that the CustTable is not getting passed?
Thank you.
Klaas Deforche
on October 1, 2013 at 08:20
Hi Herman,
You are correct, they didn’t get passed when I ran the code that time, but I only noticed it afterwards. I updated the code when I noticed it didn’t work but not the screenshot.
I changed this line:
To this:
I’m not exactly sure why, but it (sometimes?) doesn’t work if you just pass the record buffer. It does work if you pass a copy of it using the data() method.
Passing buffers is a personal preference of mine, because I don’t have to reselect the record in the thread afterwards (as apposed to passing a recid of a natural key). Although I’m always in doubt if it’s the best way. It might be better to pass a recid when constructing the class. That way there is no need to pack the full record in the batch table. I’m unsure what is the best/quickest, it’s something to think about :).
I hope this helped.
Manikanta
on September 23, 2015 at 12:48
Hi Klaas Deforche,
Thanks a lot for the information. It’s great. I’ve one small doubt.
For suppose, If I want to create batch header for every group of records and then add task for each record in that group.
At present, I’ve created a query, added data source and grouped by few fields. Now I want to create batch header for each of that group and tasks for each of the record in that group. Can you please help me how to solve this.
Thanks in advance.
Prasad
on February 20, 2016 at 08:00
Thank you very much for this document. But a small doubt. What is the maximum number of tasks can we create? Is there any limit? I was able to create about 25,000 tasks without any problems but it took a while to execute.
Asking out of curiosity. Is there any way to halt the
KlForUpdateCustomersMultiThreadBatch until all the tasks are completed. (I have added dependencies for the task execution priority.) But I would like to add another functionality at the end in KlForUpdateCustomersMultiThreadBatch class to write to some custom table that all tasks are finished execution with its endtime as an example.
Presently I am using this method and not sure weather my approach is right or wrong.
while(!(createdTaskCount – (finishedTaskCount + errorTaskCount)) <= 1)
{
finishedTaskCount = this.endedThreadCount(thisBatchJobID);
errorTaskCount = this.errorThreadCount(thisBatchJobID);
break;
}
I am getting the total count from batch and batchjob table for particular job id to halt the execution untill all the tasks are finished. I know it is really resource intensive, but I do not have any other option. Please advise.
Any thoughts.
Prasad
on February 20, 2016 at 08:09
For better understanding of my reqs
KlForUpdateCustomersMultiThreadBatch.run()
{
CustomLogTable logTable;
—-;
—-;
if(batchHeader)
{
// save the batchheader with added tasks
batchHeader.save();
}
while(!(createdTaskCount – (finishedTaskCount + errorTaskCount)) <= 1)
{
//Get count of tasks finished from batch table
finishedTaskCount = this.endedThreadCount(thisBatchJobID);
//Get count of tasks error from batch table
errorTaskCount = this.errorThreadCount(thisBatchJobID);
break;
}
logTable.notes = strfmt("All Tasks have finished execution @ %1",dateTimeStamp);
logTable.insert();
}
Hope code will help you understand what I am looking for!!!
Mark Kopan
on March 17, 2016 at 19:51
this does not work.
batchHeader.addRuntimeTask(klForUpdateCustomersMultiThreadTask, this.parmCurrentBatch().RecId); throws an nondescript error .
Task is within SalesJournalChangeType.Run(); this should run a KitExplosion routine that is normally fired on the SalesTable Form. SalesType field.
initiated a batch run, but when it gets to the addRuntimeTask(), there appears to be something missing in the base()
I did mess with the parameters and got it to run a couple of times, now it will not run. any ideas why the Task i s not initializing or receiving the BatchInfo from the batchHeader?
thanks
Eduardo Román Becerra
on March 12, 2018 at 19:14
// add tasks to the batch header
batchHeader.addRuntimeTask(klForUpdateCustomersMultiThreadTask, this.parmCurrentBatch().RecId);
// new line to save the subtask
batchHeader.save();
nidhi
on June 2, 2021 at 08:36
can we update executing status in one original line ? instead of showing multiple executing line in batch job form
Trackbacks
on June 3, 2023 at 04:36