Hi Mads,
We are enjoying BC Internals, it is very insightful. Lately we have experienced some performance issues running on SQL in Azure in an on prem situation. On investigating, some questions were raised.
We consulted with SQLPerform, but they were not able to fully answer our questions. We just need some security how Business Central is handling certain patterns through Azure SQL Server, and how to approach solving the performance issues.
If you are not the right person to address these questions, please let me know. You can contact me at [email protected]
My Collegae William van den Heuvel has this questions:
Questions
We have performance and concurrency issues in our BC19 solutions. To some extent, the solution to these problems runs parallel because better performance leads to shorter transaction durations so there is less time to get in each other's way, but they can also conflict, for example when using SIFT. A decision will then have to be made on a case-by-case basis. To make a good balance between performance and locking, Newminds has made a number of choices, the question is whether they are optimal. It concerns the following matters.
Flow Fields
We often use flowfields to add up numbers. Using Sumindexfield leads to good reading performance if the key matches the selected filter. However, the disadvantages are delay and locking when updating because the underlying view must be updated. That's why we started using IncudedFields. However, these also appear to cause problems with the locking.
Is it true that the locking works as follows in that case? If a table has a write status within a transaction and this table is then accessed via a flowfield without SIFT, all records that are counted together will be read with the UPDLOCK hint. The status (read or write) of the table that has the flow field as a field has no influence on the hint that is given.
If the flow field has a Sumindexfield with a key that exactly matches the filter of the flow field, then the value can be read directly from the view. Still assuming the table is in write state, what are the implications for locking now? What does this mean for the trade-off between using Sumindexfield or Includefields?
Is it true that what applies to flowfields also applies to the use of Calcsums?
Set based operations.
Set-based operations can be performed with DeleteAll and ModifyAll. We replaced these operations with loops, arguing that this would lead to less locking and lock escalation because a loop would use SQL cursors to retrieve data. Does this argument make sense? Are there other arguments against using set-based operations?
Findset(true, false) or Find('-')
We have replaced Findset(true, false) with Find('-') in most places. Again, the argument was that Findset(true, false) leads to more lock escalation than Find('-') because the latter would use an SQL cursor. Is it true that a loop with Find('-') gives less locking than Findset(true, false)? When is it better to use Find('-') and when to use Findset(true, false)? Is it true that if a table is in write state, there is no longer any difference between Findset(false, false) and Findset(true, false)?
Missing indexes
We regularly run a script in our customers' databases to query the missing indexes. When is it wise to start adding keys in AL based on this? How can we detect detrimental keys?
Other questions
Lock escalation
When does lock escalation occur? Could this cause problems in BC? If so, how can these problems be mitigated?
Deadlocks
Our customers regularly report deadlocks. The victim displays an error message so that we know the stack. Furthermore, we can query the deadlock diagram in the database. Are there possibilities to get even more information, for example the stack of the perpetrator?
Isempty
It is recommended to first query with an IsEmpty if there are any records before applying a DeleteAll as this would prevent unnecessary locking. Are there more cases where it might be useful to apply an Isempty before requesting records?
MARS
In Business Central, records are retrieved using MARS (multiple active result sets). This mechanism works much better than the SQL cursors used in the past. However, MARS can cause problems with long-term transactions. This can be the case, for example, if a heavy procedure is called from the repeat after a findset. We wonder if the following approach is useful in that case: Use the repeat after the findset to build a temporary table or a list without calling the heavy procedure. Later, iterate over this temporary table or list and call the procedure from there.