sqlinsight-pro/docs/dynamics-ax.md
Søren Eggert Lundsteen Olsen 31fa89b385 Review 1.4: AX detection survives passing errors; revoke only what the grant gave; smaller fixes
- A lock timeout or deadlock while detecting (e.g. during an AX table
  synchronisation) dropped the database from AX monitoring for an hour, so
  its findings resolved and reopened. The earlier detection is kept and
  retried within 5 minutes. A cancelled collection is no longer reported as
  "læsningen fejlede".
- A lock timeout kept the earlier batch picture forever; now at most 15
  minutes (MeasuredAt), so a job that has started is not reported late from
  an old picture.
- revoke-dynamics-ax-read.sql revoked SELECT on same-named tables in every
  schema, also a DENY set by a DBA. It now finds the AX schema like the grant
  script and revokes only granted SELECT there.
- SQLI-AX-006 skips blocked sequences (as -005/-008 do); forecasts under a
  day say "inden for et døgn" instead of "om ca. 0 dage".
- Integration tests: the SQL Scripts run as-is in main's format, and the
  shared connection has the agent's session settings after the AX reads.
- docs/dynamics-ax.md: history bounds, lock-timeout window, session settings,
  /detection-problems, API redaction.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:45:31 +02:00

14 KiB
Raw Permalink Blame History

Dynamics AX

Fra version 1.4 overvåger SQLInsight Pro Microsoft Dynamics AX-databaser specifikt: batchjob, nummerserier, RecId og hvilken AX-bruger der står bag en blokering. Det findes under Dynamics AX i menuen (vises, så snart en overvåget database er genkendt som AX) og i API'et under /api/v1/dynamics-ax/.

Hvor sikre er vi på skemaet? Axapta 3.0 er bygget og testet mod en rigtig kundedatabase (schema-ax30.tsv: tabeller ejet af bmssa, 32-bit RECID, BATCH med lokal STARTDATE + STARTTIME i sekunder efter midnat). AX 4.0, AX 2009, AX 2012 og Dynamics 365 F&O er baseret på Microsofts dokumenterede skema – ikke verificeret mod kundedata. Det dokumenterede skema står i schema-ax2009.documented.tsv og schema-ax2012.documented.tsv. Leverer en kunde det rigtige skema (se nedenfor), lægges det ind som schema-ax2009.tsv/schema-ax2012.tsv, og testene kører så mod det i stedet.

Genkendelse

En database er AX, når systemtabellerne SQLDICTIONARY og DATAAREA ligger i samme skema. Versionen afgøres af skemaet ét sted (AxDetection.cs), som bruges af Servere og databaser, AX-collectorerne, community-reglerne COM-AX-* og webbrugerfladen:

Kendetegn Version
SQLDICTIONARY.RECID er int (præcision 10) Axapta 2.5/3.0 (kan ikke skelnes; typisk skema bmssa)
bigint, ingen BATCHJOB AX 4.0
BATCHJOB, ingen NUMBERSEQUENCESCOPE AX 2009
NUMBERSEQUENCESCOPE, ingen PARTITION-kolonne AX 2012 RTM/FP
PARTITION og modeldatabasen <db>_model på instansen AX 2012 R2/R3
PARTITION, intet modellager Dynamics 365 F&O on-premises (formodet)

Genkendelsen læser kun metadata (sys.tables, sys.columns, HAS_PERMS_BY_NAME) og kræver ikke mere end grant-least-privilege.sql. Den kører hver time i de databaser, der er valgt til overvågning (hvert 5. minut for en AX-database, hvor agenten endnu ikke har den fulde læseadgang, så en GRANT virker inden for få minutter). AX-tjek kører kun i databaser, der er genkendt som AX og valgt til overvågning; de generelle tjek kører i alle valgte databaser som hidtil.

Valgfri læseadgang til AX-tabellerne

Batchjob, nummerserier og RecId kræver SELECT på nogle få AX-systemtabeller – aldrig kundedata (debitorer, ordrer, posteringer) og aldrig skriverettigheder:

Version Tabeller
Axapta 3.0 og AX 4.0 BATCH, NUMBERSEQUENCETABLE, NUMBERSEQUENCEREFERENCE, SYSTEMSEQUENCES, SQLDICTIONARY, DATAAREA
AX 2009 BATCHJOB, BATCHJOBHISTORY, NUMBERSEQUENCETABLE, NUMBERSEQUENCEREFERENCE, SYSTEMSEQUENCES, SQLDICTIONARY, DATAAREA
AX 2012 og F&O som AX 2009 plus NUMBERSEQUENCESCOPE

Adgangen er et tilvalg pr. database. Dynamics AX → AX-databaser viser for hver database, hvilke tabeller agenten må læse, og de nøjagtige GRANT-sætninger med skema (bmssa i AX 3.0) og agentens login udfyldt. Det samme for enhver AX-database: db/permissions/grant-dynamics-ax-read.sql, som selv finder version og skema (fjernes igen med revoke-dynamics-ax-read.sql). SQLInsight kører aldrig scripts selv.

Uden adgangen er tjekket ikke målbart, og årsagen er den nøjagtige GRANT, der mangler – aldrig en fejl.

Sådan læser SQLInsight AX

  • Kun SELECT, kun på standardtabellerne ovenfor, og aldrig i kundens database ud over det: al historik (batchkørsler, målinger af nummerserier) gemmes i SQLInsights eget repository.
  • Begrænset: TOP på hver forespørgsel (1000 batchjob; 1000 historikrækker første gang og derefter 3000 fra seneste læste RECID minus et overlap på 2000, så mindst 1000 nye kørsler læses hver gang; de 5000 mest brugte nummerserier; de 500 højeste RecId-serier), og historikken læses efter RECID, som AX altid har et unikt indeks på.
  • Uden at vente på AX: READ COMMITTED med SET LOCK_TIMEOUT 2000 – er en række låst, opgiver læsningen efter 2 sekunder og prøver igen næste gang (det forrige billede beholdes i op til 15 minutter, så fund ikke blinker). Bagefter sættes agentens egne sessionsindstillinger igen (LOCK_TIMEOUT 5000, READ UNCOMMITTED til DMV-forespørgslerne), så de næste collectors ikke påvirkes. Har databasen ALLOW_SNAPSHOT_ISOLATION, bruges SNAPSHOT; med READ_COMMITTED_SNAPSHOT læses versioner. Aldrig NOLOCK.
  • Uden at skifte database: forespørgslerne køres i AX-databasen med [db].sys.sp_executesql.
  • Kolonnetjek først: hver forespørgsel bygges ud fra de kolonner, databasen faktisk har. Mangler en nødvendig kolonne, er tjekket "ikke målbart" med den præcise kolonne; mangler en valgfri, udelades den med en note.
  • SQL Server 2005 og nyere. På SQL Server 2000 (legacy ODBC) genkendes AX under Servere og databaser, men datatjekkene kører ikke.

Den præcise SQL for hver regel står i SQL Scripts/ (fx SQLI-AX-003.sql), genereret fra koden og klar til at køre i SSMS.

Regler

Regel Hvad Tærskel (SQLInsight:DynamicsAx)
SQLI-AX-001 Batchjob er endt med fejl (nu, eller seneste kørsel af et gentaget job) BatchFailedLookbackHours = 24
SQLI-AX-002 Batchjob kører længere end normalt, målt mod jobbets egne seneste kørsler BatchLongRunningFactor = 2 × 90 %-fraktilen, mindst BatchLongRunningMinimumMinutes = 10, efter BatchHistoryMinimumRuns = 5 kørsler
SQLI-AX-003 Batchjob er ikke startet til tiden; efter et døgn "aldrig startet" BatchLateMinutes = 30, BatchNeverStartedHours = 24
SQLI-AX-004 Batchjob hænger i Udfører BatchStuckHours = 6 (medmindre jobbet normalt kører så længe)
SQLI-AX-005 Nummerserie er ved at løbe tør, nu eller efter prognosen NumberSequenceWarnPercent = 80, NumberSequenceCriticalPercent = 95, ForecastWarnDays = 90, ForecastCriticalDays = 30
SQLI-AX-006 Nummerseriens format har færre cifre end Højeste kræver –
SQLI-AX-007 Spærret nummerserie bruges stadig af en reference –
SQLI-AX-008 Kontinuerlig nummerserie er ikke ryddet op 7 dage eller 2 × seriens interval
SQLI-AX-009 RecId er ved at løbe tør – Axapta 3.0's 32-bit RecId advares langt tidligere RecId32WarnPercent = 50, RecId32CriticalPercent = 75, RecId32ForecastDays = 730; 64-bit: RecId64WarnPercent = 80
SQLI-AX-010 AX-bruger blokerer andre BlockingSeconds = 30

Tærsklerne sættes i agentens appsettings.json:

"SQLInsight": {
  "DynamicsAx": { "BatchLateMinutes": 30, "BatchStuckHours": 6, "NumberSequenceWarnPercent": 80, "ForecastWarnDays": 90 }
}

De eksisterende community-regler COM-AX-001 til -007 (support, SQL Server-version, MAXDOP, RecId-indeks, heaps, statistikker) læser kun metadata og kører nu også kun i genkendte og valgte AX-databaser.

Batchjob

Planlagt start er BATCHJOB.ORIGSTARTDATETIME (UTC) i AX 2009 og senere, og BATCH.STARTDATE + STARTTIME (lokal tid, sekunder efter midnat) i Axapta 3.0 og AX 4.0; de lokale tider regnes om til UTC i AX-tidszonen med de sommertidsregler, der gjaldt på datoen. AX-tidszonen er SQLInsight:DynamicsAx:LocalTimeZone (fx Romance Standard Time; AOS'ens tidszone), ellers agentens egen, når den lige nu har samme forskel til UTC som SQL Server. Passer ingen af dem, bruges SQL Servers aktuelle forskel, og en datanote siger, at tider fra før et sommertidsskift kan være en time forkerte. En tid i timen, der springes over om foråret, eller som kommer to gange om efteråret, regnes med normaltid (om efteråret den sidste af de to), så et job aldrig ser forsinket ud pga. skiftet.

Hvert jobs historik er SQLInsights egen (insight.AxBatchRuns, gemt EventDays = 90 dage): i AX 2009 og senere fra BATCHJOBHISTORY, læst efter RECID; Axapta 3.0 og 4.0 gemmer ingen historik, så SQLInsight registrerer selv de kørsler, den ser blive afsluttet (kørselstiden måles fra første gang, den så jobbet køre, og korte kørsler, der starter og slutter mellem to målinger, kommer ikke med). Et job i Axapta 3.0 er klasse + firma + batchgruppe, fordi AX 3.0 laver en ny række for hver gentagelse.

STATUS i Axapta 3.0 er ikke verificeret mod kundedata. SQLInsight antager 0 Venter, 1 Udfører, 2 Fejl, 3 Afsluttet, 4 Tilbageholdt. Siden Batchjob viser de rå værdier, der findes, så de kan sammenlignes med batchlisten i AX. AX 2009 og senere følger Microsofts dokumenterede BatchStatus (0 Tilbageholdt, 1 Venter, 2 Udfører, 3 Fejl, 4 Afsluttet, 5 Klar, 6 Ikke kørt, 7 Annullerer, 8 Annulleret).

Nummerserier og RecId

Forbrug = (NEXTREC − LOWEST) i forhold til HIGHEST – eller formatets kapacitet (10^antal # − 1), hvis den er lavere. Prognoserne bygger på SQLInsights egne målinger: én pr. serie pr. dag af NEXTREC og SYSTEMSEQUENCES.NEXTVAL (insight.AxSequenceSamples, gemt HourlyDays = 400 dage). Forbruget pr. dag regnes fra den ældste måling inden for ForecastBasisDays (30) dage, der er mindst et døgn gammel; prognosen kommer altså tidligst et døgn efter, at læseadgangen er givet. Manuelle og cykliske serier får ingen prognose.

RecId-grænsen er typens maksimum – int (2.147.483.647) når NEXTVAL er int som i Axapta 3.0, ellers bigint – eller MAXVAL, hvis den er sat lavere. I Axapta 3.0 er RecId pr. tabel og firma.

AX-brugere bag blokering

Alle AX-brugere går gennem AOS'ens login. AX 2009 og 2012 (og F&O) kan skrive AX-brugeren og AX-sessionen i sessionens CONTEXT_INFO, når connectioncontext er slået til i AOS-konfigurationen (registreringsdatabasen på AOS-serveren; kræver genstart af AOS). SQLInsight læser den fra blokeringsmålingerne (første ord med bogstaver = bruger, første tal = AX-session; formatet er Microsofts for AX 2012 og ikke verificeret mod kundedata). Axapta 3.0 og AX 4.0 sætter aldrig CONTEXT_INFO: brugeren kan ikke ses fra SQL Server, og SQLInsight siger det og viser vært (AOS'en ved 3-lags-installation, brugerens pc ved 2-lags-klient) og program. AX-brugeren er en følsom oplysning.

SQLInsight Hub

AX-fund (SQLI-AX-* og COM-AX-*) sendes kun til SQLInsight Hub for databaser, hvor AX-fund til SQLInsight Hub er slået til under Servere og databaser (standard fra). Hvert fund bærer databasen i sit emne (database: AX30 · batch: …), så det følger databasens valg. AX-reglernes udfald sendes kun, når mindst én database har valget slået til, og deres årsag (som kan nævne tabeller, logins og GRANT-tekst) erstattes af en fast tekst. AX-brugere er følsomme og sendes kun med SendSensitiveEvidence.

API

Endpoint Indhold
GET /api/v1/dynamics-ax/databases Genkendte AX-databaser: version, verificeret, skema, modeldatabase, firmaer, læseadgang pr. tabel, manglende tabeller og GRANT
GET /api/v1/dynamics-ax/detection-problems Overvågede databaser, AX-genkendelsen ikke kunne læse (fx ingen databasebruger før SQL Server 2014)
GET /api/v1/dynamics-ax/grant-script?instance=&database= GRANT-sætningerne og hele grant-dynamics-ax-read.sql udfyldt for databasen
GET /api/v1/dynamics-ax/batch-jobs Batchjob nu: status, planlagt/faktisk start, forsinkelse, kørselstid, median og 90 %-fraktil, rå STATUS-værdier og hvad de betyder i versionen
GET /api/v1/dynamics-ax/batch-runs?database=&job=&days=7 Afsluttede kørsler fra repository'et
GET /api/v1/dynamics-ax/number-sequences Nummerserier med forbrug, format, prognose og flag
GET /api/v1/dynamics-ax/recids RecId pr. tabel med grænse og prognose
GET /api/v1/dynamics-ax/sequence-samples?instance=&database=&kind=N|R&key=&days=30 De daglige målinger bag prognoserne (højst 10.000; X-Truncated: true, når der er flere)
GET /api/v1/dynamics-ax/blocking?days=7 AX-brugere bag blokering

Alle tager ?instance= for én instans; fjernede servere er ikke med. AX-bruger, vært, program, sætning og rå CONTEXT_INFO er [udeladt], og agentens login og databasebruger erstattes af pladsholderen DOMÆNE\svc-sqlinsight$ (også i GRANT-teksterne i "ikke målbar" og i /api/v1/instances/{name}/rule-states), medmindre SQLInsight:Web:ApiIncludesSensitiveEvidence er slået til.

Nyt kundeskema

Får vi et rigtigt skema fra en AX 2009- eller 2012-kunde, eksporteres det med

SELECT s.name AS skema, t.name AS tabel, c.name AS kolonne, TYPE_NAME(c.system_type_id) AS type, c.max_length, c.column_id
FROM sys.tables t JOIN sys.schemas s ON s.schema_id = t.schema_id JOIN sys.columns c ON c.object_id = t.object_id
WHERE t.name IN ('SQLDICTIONARY', 'DATAAREA', 'SYSCONFIG', 'BATCH', 'BATCHJOB', 'BATCHJOBHISTORY', 'BATCHGROUP',
                 'NUMBERSEQUENCETABLE', 'NUMBERSEQUENCESCOPE', 'NUMBERSEQUENCEREFERENCE', 'SYSTEMSEQUENCES')
ORDER BY s.name, t.name, c.column_id;

og gemmes tabulatorsepareret som docs/dynamics-ax/schema-ax2009.tsv eller schema-ax2012.tsv. Testene (AxSchemaAdapterTests, DynamicsAxSqlTests, SqlScriptTests) bruger så det rigtige skema i stedet for det dokumenterede, og viser præcis hvilke kolonner versionens adapter (AxSchemaAdapter.cs) forventer, men som mangler. Al viden om en versions tabeller ligger i dens adapter; ret den dér, og opdatér teksterne "ikke verificeret mod kundedata" for versionen (AxVersions.IsVerified).

Genbrug i andre tjek

Et syntetisk AX-logon-tjek (fase 1.5) hører ikke til her, men kan genbruge AX-viden herfra i stedet for at kende AX' tabeller selv: GET /api/v1/dynamics-ax/databases (eller snapshot'et dynamics-ax i repository'et, typen AxDatabaseSnapshot) giver for hver overvåget AX-database version, skema, firmaer (DATAAREA) og modeldatabase; AxDetection og AxSchemaAdapter i SQLInsight.Core.DynamicsAx er den eneste kilde til, hvilke tabeller og kolonner en version har; og AxSessionMapper oversætter en SQL-session til AX-bruger og AX-session, hvor AOS'en sætter CONTEXT_INFO.