Je kunt deadlocks binnen Business Central analyseren met behulp van bijvoorbeeld de telemetry van application insights. KQL, ofwel Kusto Query Language, is een krachtige querytaal die je kunt gebruiken om gegevens te doorzoeken en te analyseren binnen Microsoft Azure. In de context van Business Central, kun je KQL vaak inzetten om diepgaande inzichten te krijgen uit de grote hoeveelheden data. Die data noemen we dan ook wel telemetry.
Je kunt KQL overigens dus binnen onder andere Data Explorer binnen Azure gebruiken of je kunt KQL gebruiken binnen Kusto Explorer. Je kunt onderstaand gebruiken als template.
// Customer Name: Contoso
let _startTime = datetime(2025-04-08T07:00:00Z);
let _endTime = datetime(2025-04-08T10:59:00Z);
let _aadTenantId = "";
let _environmentName = "Production";
let _environmentType = "Production";
let _companyName = "CRONUS Nederland BV";
traces
| where timestamp between (_startTime.._endTime)
| where customDimensions.aadTenantId == _aadTenantId
| where customDimensions.environmentType == _environmentType
| where customDimensions.environmentName == _environmentName
| where customDimensions.companyName == _companyName
| where severityLevel == 3
| project
timestamp,
message,
alDetailedErrorMessage = customDimensions.alDetailedErrorMessage,
severityLevel,
eventId = customDimensions.eventId,
alObjectId = customDimensions.alObjectId,
alStackTrace = customDimensions.alStackTrace,
extension = customDimensions.extensionPublisher,
customDimensions
| limit 1000
Analyseren van deadlocks kun je dus doen met behulp van telemetry
Je kunt samples vinden op GitHub. Een ander voorbeeld in combinatie met de template hierboven vind je hieronder.
// Customer Name: Contoso
let _startTime = datetime(2025-04-08T07:00:00Z);
let _endTime = datetime(2025-04-08T10:59:00Z);
let _aadTenantId = "";
let _environmentName = "Production";
let _environmentType = "Production";
let _companyName = "CRONUS Nederland BV";
traces
| where timestamp between (_startTime.._endTime)
| where customDimensions.aadTenantId == _aadTenantId
| where customDimensions.environmentType == _environmentType
| where customDimensions.environmentName == _environmentName
| where customDimensions.companyName == _companyName
| where customDimensions.eventId == 'RT0028'
| where customDimensions.alObjectId > 0 // filter out internal server calls
| extend eventId = customDimensions.eventId
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
, companyName = customDimensions.companyName
, extensionId = customDimensions.extensionId
, extensionPublisher = customDimensions.extensionPublisher
, extensionName = customDimensions.extensionName
, extensionVersion = customDimensions.extensionVersion
, sessionId = customDimensions.sessionId
, alObjectId = customDimensions.alObjectId
, alObjectName = customDimensions.alObjectName
, alObjectType = customDimensions.alObjectType
, alStackTrace = customDimensions.alStackTrace
, clientType = customDimensions.clientType
, sqlStatement = tostring(customDimensions.sqlStatement)
, sqlServerSessionId = customDimensions.sqlServerSessionId
| extend operationType = case(
sqlStatement startswith "UPDATE", "UPDATE"
, sqlStatement startswith "DELETE", "DELETE"
, sqlStatement startswith "BeginTransaction", "BEGIN_TRANS"
, sqlStatement matches regex "INSERT INTO", "INSERT"
, sqlStatement startswith "SELECT", "SELECT"
, sqlStatement matches regex "IF EXISTS \\(SELECT (.*)\\) SELECT", "SELECT'"
, sqlStatement has "SELECT NEXT VALUE FOR", "SELECT_SEQUENCE_VALUE"
, sqlStatement has "SELECT @@SPID", "SELECT_SPID"
, sqlStatement matches regex "(.*)WITH", "SELECT"
, sqlStatement startswith "CREATE TABLE", "CREATE_TABLE"
, sqlStatement startswith "ALTER TABLE", "ALTER_TABLE"
, sqlStatement startswith "exec sp_rename", "RENAME_TABLE"
, sqlStatement matches regex "(.*)DROP TABLE", "DROP_TABLE"
, sqlStatement startswith "SET LOCK_TIMEOUT", "SET_LOCK_TIMEOUT"
, sqlStatement has "sp_getapplock", "GET_APP_LOCK"
, "UNKNOWN"
)
, usertelemetryId = case(
// user telemetry id was introduced in the platform in version 20.0
toint( substring(customDimensions.componentVersion,0,2)) >= 20, user_Id
, 'N/A'
)
De user _Id is gelijk aan de Telemetry Id die op de gebruikerskaart binnen Business Central te vinden is. Je kunt dus heel gemakkelijk deadlocks analyseren met behulp van telemetry van application insights. Op GitHub vind je uiteraard nog veel meer voorbeelden en een cheat sheet.
De deadlock-telemetrie van de database verzamelt informatie over deadlocks die optreden. Deadlocks kunnen voorkomen dat gebruikers taken voltooien in de Business Central-client. Een deadlock treedt op wanneer twee of meer processen elkaar blokkeren omdat elk een databasemiddel heeft vergrendeld. Het systeem beëindigt en rolt een van de sessies terug (bekend als het deadlock-slachtoffer) en zendt vervolgens een telemetriesignaal uit.
Als partner of ontwikkelaar biedt deze telemetrie verschillende voordelen:
- Database lock trace telemetrie. Voor meer informatie, zie Database lock trace telemetrie.
- Maakt u bewust van het feit dat er deadlocks optreden.
- Laat u identificeren wie het slachtoffer was in deadlock-situaties.
- Bij sommige deadlock-problemen zullen het proces dat het slachtoffer is en het proces dat slaagt, willekeurig veranderen. In deze gevallen worden beide opgeslagen in de telemetriebron als verschillende deadlock-gebeurtenissen.
- Voor verdere monitoring en probleemoplossing wordt deze telemetrie aangevuld met andere functies zoals:
- SQL-database deadlock monitoring inschakelen in een sandbox- of on-premises omgeving. Voor meer informatie, zie Monitoring SQL Database Deadlocks.
- Database lock trace telemetrie. Voor meer informatie, zie Database lock trace telemetrie.
Custom dimensions binnen de telemetry nader uitgelegd
Tot slot. Meer informatie over Microsoft Business Central vind je hier. Meer informatie over de auteur van deze blog post vind je hier.