Multithreading Approaches in Dynamics 365 Finance & Operations
Question 6 — Individual Task Modeling, Batch Bundling and Top Picking: how they differ and when to use each.
Interview Question
Model Answer (Short)
Multithreading in D365 F&O is achieved through the batch framework, which spreads work across multiple batch threads (batch tasks) so records are processed in parallel instead of one long row-by-row run. The three common patterns are: Individual Task Modeling (create one batch task per work item), Batch Bundling (split the workload into fixed-size bundles, one task per bundle), and Top Picking (threads pick the next available record from a shared staging table). The right choice depends on the volume and whether the workload is even or uneven.
Detailed Explanation
Individual Task Modeling
You create a separate batch task for every work item and add them to the batch header. The batch framework then distributes those tasks across the available threads. Dependencies between tasks can be defined.
- Pros: works well for a small number of work items of any type; simple to write; best fit when you need dependencies between work items.
- Cons: for a large number of tasks the overhead of the batch throttling mechanism and inter-task delays hurts performance; can increase load on framework tables and affect other batch jobs.
Batch Bundling
The total workload is divided into bundles of a fixed size (for example, 500 records per bundle), and one batch task is created per bundle. Choosing a sensible bundle size bypasses batch throttling.
- Pros: works well for a simple, even workload; no staging table needed; does not over-pollute the batch table; bypasses throttling when the bundle size is chosen well.
- Cons: for an uneven workload, tasks finish at very different times so overall performance degrades; it may not always be possible to distribute the work evenly.
Top Picking
A fixed number of threads each repeatedly "pick the top" unprocessed record from a shared staging table, mark it as in-progress, process it, then pick the next. Work is pulled on demand.
- Pros: bypasses batch throttling; works well with an uneven workload (fast threads simply pick more); does not over-pollute the batch table.
- Cons: needs an extra staging table to track progress; when there are a very large number of small work items, tracking them through the staging table adds some overhead.
Performance Parameters to Consider
- Frequency of the job.
- Batch execution window (day vs. night).
- Approximate and peak data size.
- Expected execution time.
- Synchronous vs. asynchronous processing requirement.
Prerequisites (Rule 5)
- Visual Studio with the Dynamics 365 developer tools.
- A custom model / package for your objects.
- A class extending
RunBaseBatchor built on the SysOperation framework. - For Top Picking: a staging table with a status field and appropriate indexes.
- A batch server configured with enough threads (batch group).
Code Example — Individual Task Modeling
Adding one runtime task per work item to the current batch header:
public void run()
{
BatchHeader batchHeader = BatchHeader::getCurrentBatchHeader();
CustTable custTable;
while select custTable
{
// One batch task per customer
AbcProcessCustomerTask task = new AbcProcessCustomerTask();
task.parmAccountNum(custTable.AccountNum);
batchHeader.addRuntimeTask(task, this.getCurrentBatchTask().RecId);
}
batchHeader.save();
}
Batch Bundling — one task per fixed-size bundle
public void run()
{
BatchHeader batchHeader = BatchHeader::getCurrentBatchHeader();
int bundleSize = 500;
int counter = 0;
RecId fromRecId = 0;
// Pseudocode: create a task for each block of 'bundleSize' records
while (moreDataExists(fromRecId))
{
AbcProcessBundleTask task = new AbcProcessBundleTask();
task.parmFromRecId(fromRecId);
task.parmBundleSize(bundleSize);
batchHeader.addRuntimeTask(task, this.getCurrentBatchTask().RecId);
fromRecId = getNextRecId(fromRecId, bundleSize);
counter++;
}
batchHeader.save();
}
Top Picking — threads pull the next record from a staging table
// Each thread runs this loop, picking one record at a time
public void processNext()
{
AbcStagingTable staging;
ttsbegin;
// Select-for-update the first unprocessed row, skipping locked ones
select firstOnly forUpdate staging
where staging.Status == AbcProcessStatus::NotStarted;
if (staging.RecId)
{
staging.Status = AbcProcessStatus::InProgress;
staging.update();
ttscommit;
// ... process the record here ...
ttsbegin;
staging.selectForUpdate(true);
staging.Status = AbcProcessStatus::Completed;
staging.update();
ttscommit;
}
else
{
ttscommit; // nothing left to pick
}
}
Comparison of the Three Approaches
| Aspect | Individual Task Modeling | Batch Bundling | Top Picking |
|---|---|---|---|
| Best workload | Small number of items | Large & even | Large & uneven |
| Staging table needed | No | No | Yes |
| Batch table pollution | High for many items | Low | Low |
| Bypasses throttling | No | Yes (with right bundle size) | Yes |
| Supports dependencies | Yes | Limited | No |
Points the interviewer wants to hear
- Multithreading runs through the batch framework using multiple threads/tasks.
- Individual Task Modeling — few items or dependencies, but heavy on framework tables at scale.
- Batch Bundling — even, high-volume workloads; bundle size bypasses throttling.
- Top Picking — uneven, high-volume workloads; needs a staging table to track progress.
- Decide based on frequency, data size, execution window and even/uneven distribution.
Likely Follow-up Questions
- What is batch throttling, and how does bundling help avoid it?
- Why does Top Picking handle uneven workloads better than bundling?
- How do you prevent two threads from picking the same record in Top Picking? (pessimistic
forUpdatelock.) - Where do you set the number of threads / batch group?
Key Takeaway
Match the pattern to the workload: Individual Task Modeling for small sets or dependencies, Batch Bundling for large even workloads, and Top Picking for large uneven workloads. All three exploit the batch framework to turn slow row-by-row processing into scalable parallel work.