When working with high-volume Business Central environments in a SaaS environment, one of the most overlooked performance bottlenecks is the automatic cost adjustment process. This blog post is about diagnosing performance impact of automatic cost adjustment using KQL. Especially in intercompany-heavy setups, this background process can silently overload your system—causing job queues to pile up and users to experience slowdowns.
In this post, we’ll walk through how to use Kusto Query Language (KQL) to identify and visualize the impact of automatic cost adjustments using Application Insights telemetry.
Filter for Cost Adjustment records
let _startTime = datetime(2025-05-20T08:30:00Z);
let _endTime = datetime(2025-05-20T17:00:00Z);
let _aadTenantId = "84d30a91-7218-4db6-b27d-acc36edf7c3d";
let _environmentName = "Production";
let _environmentType = "Production";
traces
| where timestamp between (_startTime.._endTime)
| where customDimensions.aadTenantId == _aadTenantId
| where customDimensions.environmentType == _environmentType
| where customDimensions.environmentName == _environmentName
| where severityLevel > 0
| extend messageText = tostring(message)
| where messageText has "cost adjust" or messageText has "cost adjustment" or messageText has "automatic cost"
| extend company = tostring(customDimensions.companyName)
This gives the number of records where telemetry tells us automatic costing was running due to inventory change.
Value | Counted value |
---|---|
Automatic cost adjustment was used | 7596 |
Table with number of records |
As you can see, during one day, there were 7593 times automatic cost adjusting ran. We can also split these per company to get more insights.
let _startTime = datetime(2025-05-20T08:30:00Z);
let _endTime = datetime(2025-05-20T17:00:00Z);
let _aadTenantId = "84d30a91-7218-4db6-b27d-acc36edf7c3d";
let _environmentName = "Production";
let _environmentType = "Production";
traces
| where timestamp between (_startTime.._endTime)
| where customDimensions.aadTenantId == _aadTenantId
| where customDimensions.environmentType == _environmentType
| where customDimensions.environmentName == _environmentName
| where severityLevel > 0
| extend messageText = tostring(message)
| where messageText has "cost adjust" or messageText has "cost adjustment" or messageText has "automatic cost"
| extend company = tostring(customDimensions.companyName)
| summarize CostAdjustCount = count() by (company)
The results are like this.
Value | Counted value |
---|---|
CompanyA | 60 |
CompanyB | 2977 |
CompanyC | 24 |
CompanyD | 1558 |
CompanyE | 78 |
CompanyF | 2899 |
Table with number of records |
What is automatic cost adjustment
Cost adjustment can be performed in two ways:
- Manually, by running the Adjust Cost – Item Entries batch job. You can run this batch job either for all items or for only certain items or item categories. This batch job runs a cost adjustment for the items in inventory for which an inbound transaction has been made, such as a purchase. For items that use the average costing method, the batch job also makes an adjustment if any outbound transactions are created.
- Automatically, by adjusting costs every time that you post an inventory transaction, and when you finish a production order. The cost adjustment is only run for the specific item or items affected by the posting. This is set up when you select the Automatic Cost Adjustment check box on the Inventory Setup page.
It is good practice to run the cost adjustment automatically when you post because unit costs are more frequently updated and therefore more accurate. The disadvantage is that the performance of the database can be affected by running the cost adjustment so often.
Because it is important to keep the unit cost of an item up to date, it is recommend that you run the Adjust Cost – Item Entries batch job as often as possible, during nonworking hours. Alternatively, use automatic cost adjustment. This ensures that the unit cost is updated for items daily.
Impact automatic cost adjustment
Regardless if you run the cost adjustment manually or automatically, the adjustment process and its consequences are the same. Business Central calculates the value of the inbound transaction and forwards that cost to any outbound transactions, such as sales or consumptions, which have been applied to the inbound transaction. The cost adjustment creates value entries that contain adjustment amounts and amounts that compensate for rounding.
The new adjustment and rounding value entries have the posting date of the related invoice. Exceptions are if the value entries fall in a closed accounting period or inventory period or if the posting date is earlier than the date in the Allow Posting From field on the General Ledger Setup page. If this occurs, the batch job assigns the posting date as the first date of the next open period.
Real live scenario
In the scenario, I worked on. There was a company with a few branch offices. The main company had the responsibility of keeping the inventory. Via master data management, the items were replicated to the branches. Via job queues, purchase orders and sales orders were automatically created and synched again. Having said that, the inventory levels at the branches were always 0 while the main company was maintaining the inventory correctly.
Setting the automatic cost adjustment to always was not advised in this scenario as telemetry shows the following events.
Telemetry
timestamp | message | alDetailedErrorMessage | severityLevel | eventId | extension |
2025-05-20 12:01:55.8100058 | Operation exceeded time threshold (SQL query) | 2 | RT0005 | Microsoft |
timestamp: 2025-05-20 12:01:55.810
message: Operation exceeded time threshold (SQL query)
alDetailedErrorMessage:
severityLevel: 2
eventId: RT0005
alObjectId: 472
alStackTrace: AppObjectType: Table
AppObjectId: 472
AL CallStack: "Job Queue Entry"(Table 472).ActivateNextJobInCategory line 11 - Base Application by Microsoft version 26.0.30643.34008
"Job Queue Entry"(Table 472).ActivateNextJobInCategory line 8 - Base Application by Microsoft version 26.0.30643.34008
"Job Queue Activate Next"(CodeUnit 462).OnRun(Trigger) line 2 - Base Application by Microsoft version 26.0.30643.34008
"Job Queue Entry"(Table 472).ActivateNextJobInCategoryIfAny line 9 - Base Application by Microsoft version 26.0.30643.34008
"Job Queue Dispatcher"(CodeUnit 448).OnRun(Trigger) line 25 - Base Application by Microsoft version 26.0.30643.34008
extension: Microsoft
customDimensions: {
"component": "Dynamics 365 Business Central Server",
"alObjectId": "472",
"extensionPublisher": "Microsoft",
"alObjectType": "Table",
"eventId": "RT0005",
"telemetrySchemaVersion": "0.5",
"extensionName": "Base Application",
"companyName": "CompanyA",
"environmentName": "Production",
"componentVersion": "26.0.33865.0",
"clientType": "Background",
"extensionVersion": "26.0.30643.34008",
"aadTenantId": "84d30a91-7218-4db6-b27d-acc36edf7c3d",
"alObjectName": "Job Queue Entry",
"extensionId": "437dbf0e-84ff-417a-965d-ed2bb9650972",
"environmentType": "Production",
"sqlStatement": "SELECT...
}
You can easily view the information about the event here.
RT0005 | Performance | Operation exceeded time threshold (SQL query) |
- Set Automatic Cost Adjustment to “Never”
Every time a purchase order is posted, the system otherwise tries to immediately adjust the cost prices of related items and transactions. With many transactions, this leads to performance issues, delays, and potential locking.
Recommended alternative: Schedule a periodic task (e.g., at night or outside peak hours) that runs the “Adjust Cost – Item Entries” batch job. - Use a batch job for cost adjustment. Run the “Adjust Costs – Item Entries” task (codeunit 5895) regularly as a job queue, for example:
- Daily at night
- After completing a batch of purchase order processing
This keeps costs accurate without putting real-time load on your system.
- Be cautious with FIFO/LIFO/Standard Costing
Especially with FIFO or LIFO costing models, it’s crucial to continue adjusting costs accurately via the batch job, since later sales are linked to earlier purchase prices. - Inform users about delays in cost prices
Since costs are not adjusted immediately, users may temporarily see incorrect cost prices in reports. This is normal behavior as long as the batch job has not yet been executed.
Visualizing the impact of automatic cost adjustment
We can also visualize the above via KQL The query then looks like this showing the impact of automatic cost adjustment
// Customer Name: Contoso
// Tenant Id 84d30a91-7218-4db6-b27d-acc36edf7c3d
let _startTime = datetime(2025-05-19T07:00:00Z);
let _endTime = datetime(2025-05-20T23:59:00Z);
let _aadTenantId = "84d30a91-7218-4db6-b27d-acc36edf7c3d";
let _environmentName = "Production";
let _environmentType = "Production";
traces
| where timestamp between (_startTime.._endTime)
| where customDimensions.aadTenantId == _aadTenantId
| where customDimensions.environmentType == _environmentType
| where customDimensions.environmentName == _environmentName
| where severityLevel > 0
| extend messageText = tostring(message)
| where messageText has "cost adjust" or messageText has "cost adjustment" or messageText has "automatic cost"
| project
timestamp,
message,
alDetailedErrorMessage = customDimensions.alDetailedErrorMessage,
severityLevel,
eventId = customDimensions.eventId,
alObjectId = customDimensions.alObjectId,
alStackTrace = customDimensions.alStackTrace,
extension = customDimensions.extensionPublisher,
company = customDimensions.companyName
|summarize count() by bin(timestamp, 1h)
| render timechart
And the line graph. It is unfortunately not possible with Kusto Explorer to name the X-axis and Y-axis but we are summarizing the number of automatic cost adjustments in a time frame of one day split per hour. You can t hen easily see how much impact automatic cost adjustment may have in the environment.

More information about Business Central can be found here.