Dynamics AX-batchjob i de overvågede AX-databaser: job i fejl, kørende, forsinkede og hængende, sammenlignet med jobbets egen historik. Læses hvert 5. minut fra AX' standardtabeller; historikken gemmes i SQLInsights eget repository.
+ +@if (_views.Count == 0) +{ +Ingen overvågede databaser er genkendt som Dynamics AX. Se AX-databaser.
+} + +@foreach (var view in _views) +{ + if (view.Batch is null) + { +@view.Instance.Name: batchjob er endnu ikke læst (første gang inden for 5 minutter).
+ continue; + } + + var now = view.Batch.CapturedAt; + foreach (var db in view.Batch.Databases) + { +@note
+ } + + @if (db.Jobs.Count == 0) + { +Ingen batchjob.
+ } + else + { +| Status | Batchjob | Firma | Planlagt start | Kører / forsinket | Median | 90 % | Kørsler |
|---|---|---|---|---|---|---|---|
| @DynamicsAxView.StatusText(job.Status) (@job.StatusCode) | +@job.Caption | +@(job.Company ?? "–") | +@Display.Time(job.PlannedStartUtc) | +@(running is { } r ? "kører " + Minutes(r) : late is { } l ? "forsinket " + Minutes(l) : "–") | +@Seconds(job.MedianSeconds) | +@Seconds(job.P90Seconds) | +@job.HistoryRuns | +
STATUS-værdier fundet: @string.Join(", ", db.StatusCounts.OrderBy(s => s.Key).Select(s => $"{s.Key} = {DynamicsAxView.StatusText(AxSchemaAdapter.For(db.Version)?.BatchStatus(s.Key) ?? AxBatchStatus.Unknown)} ({s.Value})")).
+ @if (!AxVersions.HasUtcBatchTimes(db.Version))
+ {
+
| Sluttede | Instans / database | Batchjob | Firma | Status | Varighed |
|---|---|---|---|---|---|
| @Display.Time(run.EndedAt) | +@run.InstanceName / @run.DatabaseName | +@(run.Caption ?? run.JobKey) | +@(run.Company ?? "–") | +@DynamicsAxView.StatusText(run.Status) | +@Minutes(run.Duration) | +
Reglerne SQLI-AX-001 til -004 advarer om job i fejl, job der kører meget længere end deres egne seneste kørsler, job der venter, selv om de skulle være startet, og job der har stået i Udfører i timevis. I AX 2009 og senere kommer kørslerne fra BATCHJOBHISTORY; Axapta 3.0 og AX 4.0 gemmer ingen historik, så SQLInsight bygger den selv ud fra det, den ser (og måler kørselstiden fra første gang, den så jobbet køre). Kun AX' standardtabeller og SQLInsights eget repository bruges.
+Blokering i AX-databaserne (over 5 sekunder) med den AX-bruger, der står bag den blokerende session – så vidt SQL Server kan se det.
+ +Alle AX-brugere går gennem AOS'ens login, så SQL Server-login'et siger ikke, hvem det er. AX 2009 og 2012 kan skrive AX-brugeren i sessionens CONTEXT_INFO, når connectioncontext er slået til i AOS-konfigurationen. Axapta 3.0 og AX 4.0 kan ikke: der vises vært og program, og brugeren må findes under Online brugere i AX ud fra tidspunktet.
+Ingen overvågede databaser er genkendt som Dynamics AX. Se AX-databaser.
+} +else +{ +Ingen blokering i AX-databaserne i perioden.
+ } + else + { +| Tidspunkt | Instans / database | AX-bruger | AX-session | Vært / program | SQL-session | Blokerede | Længste ventetid |
|---|---|---|---|---|---|---|---|
| @Display.Time(r.SampledAt) | +@r.Instance / @r.Database | +@(r.Identity.AxUser ?? "ukendt") (@r.Identity.Source) | +@(r.Identity.AxSessionId?.ToString() ?? "–") | +@r.Head.HostName / @r.Head.ProgramName | +@r.Head.SessionId | +@r.BlockedCount | +@Display.Number(r.MaxWaitMs / 1000.0) s | +
De overvågede databaser, som SQLInsight genkender som Microsoft Dynamics AX: version, skema, firmaer og hvilke AX-tabeller agenten må læse. Genkendelsen læser kun metadata og opdateres hver time.
+ +@if (_views.Count == 0) +{ +Ingen overvågede databaser er genkendt som Dynamics AX endnu. Databaser vælges under Servere og databaser.
+} + +@foreach (var view in _views) +{ + foreach (var ax in view.Databases.Databases) + { + var missing = AxReadAccess.Missing(ax); +| Version | @ax.VersionText |
|---|---|
| Skema | @AxVersions.VerificationText(ax.Version) |
| Tabellernes ejer | @ax.Schema |
| Modeldatabase | @model |
| Firmaer | @(ax.Companies is { } companies ? companies.Count == 0 ? "ingen" : string.Join(", ", companies.Select(c => c.Id + (c.Name is { Length: > 0 } n ? $" ({n})" : string.Empty))) : "ukendt – " + ax.CompaniesReason) |
| Læsning | @DynamicsAxView.IsolationText(ax.Isolation) |
| Genkendt | @Display.Ago(view.Databases.CapturedAt.UtcDateTime) |
@note
+ } + +| Tabel | Agenten må læse |
|---|---|
| @ax.Schema.@table | @(ax.CanRead(table) ? "ja" : "nej") |
Agenten må læse alle de AX-tabeller, tjekkene for batchjob, nummerserier og RecId bruger. Kun SELECT; ingen kundedata.
+ } + else + { +Uden læseadgang er AX-tjekkene for batchjob, nummerserier og RecId ikke målbare i @ax.Database. Adgangen er et tilvalg: kun SELECT på disse systemtabeller, ingen kundedata, ingen skriverettigheder. Kør som sysadmin eller db_owner:
+@AxReadAccess.GrantSql(ax, ax.AgentLogin ?? view.Databases.AgentLogin, missing)+
Det samme for enhver AX-database: db/permissions/grant-dynamics-ax-read.sql (fjernes igen med revoke-dynamics-ax-read.sql).
@view.Instance.Name / @database: @problem
+ } +} + +SQLInsight genkender en AX-database på systemtabellerne SQLDICTIONARY og DATAAREA og finder versionen ud fra skemaet: 32-bit RECID (Axapta 3.0, tabeller ejet af bmssa), BATCHJOB (AX 2009), NUMBERSEQUENCESCOPE (AX 2012) og PARTITION-kolonnen med en separat modeldatabase (AX 2012 R2/R3). Kun Axapta 3.0 er verificeret mod en rigtig kundedatabase; AX 4.0, 2009, 2012 og Dynamics 365 F&O er bygget efter Microsofts dokumenterede skema og ikke verificeret mod kundedata. Hver forespørgsel tjekker først, at tabeller og kolonner findes, og viser ellers "ikke målbar" med den præcise kolonne.
+AX-tjek kører kun i databaser, der er genkendt som AX og valgt til overvågning. AX-fund sendes kun til SQLInsight Hub for databaser, hvor det er slået til under Servere og databaser.
+Hvor meget af hver Dynamics AX-nummerserie der er brugt, og hvornår den løber tør ud fra SQLInsights egne daglige målinger af NEXTREC. Læses hver time; de mest brugte vises først.
+ +@if (_views.Count == 0) +{ +Ingen overvågede databaser er genkendt som Dynamics AX. Se AX-databaser.
+} + +@foreach (var view in _views) +{ + if (view.Sequences is null) + { +@view.Instance.Name: nummerserierne er endnu ikke læst (første gang inden for en time).
+ continue; + } + + var t = view.Sequences.Thresholds; + foreach (var db in view.Sequences.Databases) + { +@note
+ } + +| Firma | Nummerserie | Tekst | Næste | Højeste | Format | Brugt | Pr. dag | Dage tilbage | Bemærk |
|---|---|---|---|---|---|---|---|---|---|
| @(s.Company ?? "delt") | +@s.Code | +@s.Text | +@Display.Number(s.Next) | +@Display.Number(s.Highest) | +@s.Format | +@Display.Number(s.UsedPercent, 1) % | +@(s.Forecast is { } f ? Display.Number(f.PerDay, 1) : "–") | +@(s.Forecast?.DaysLeft is { } d ? Display.Number(d) : "–") | +@Flags(s) | +
Viser de 200 mest brugte af @db.NumberSequences.Count. Alle findes i API'et.
+ } + } +Brugt er (NEXTREC − Laveste) i forhold til Højeste – eller formatets kapacitet (10^antal # − 1), hvis den er mindre. Prognosen bygger på SQLInsights egne målinger (én pr. dag, gemt i repository'et); den kræver mindst et døgns historik. Manuelle og cykliske serier får ingen prognose. Reglerne SQLI-AX-005 til -008 advarer om serier, der løber tør, formater der er for korte, spærrede serier der stadig bruges, og kontinuerlige serier der ikke ryddes op.
+Hvor langt hver tabels RecId-serie (SYSTEMSEQUENCES) er nået, og hvornår den løber tør ud fra SQLInsights egne daglige målinger. Axapta 3.0 har 32-bit RecId (højst 2.147.483.647) og advares derfor langt tidligere end senere versioner.
+ +@if (_views.Count == 0) +{ +Ingen overvågede databaser er genkendt som Dynamics AX. Se AX-databaser.
+} + +@foreach (var view in _views) +{ + if (view.Sequences is null) + { +@view.Instance.Name: RecId er endnu ikke læst (første gang inden for en time).
+ continue; + } + + foreach (var db in view.Sequences.Databases) + { +| Tabel | Firma | Næste RecId | Grænse | Brugt | Pr. dag | Dage tilbage |
|---|---|---|---|---|---|---|
| @(r.TableName ?? $"tabel-id {r.TableId}") | +@(r.Company ?? "–") | +@Display.Number(r.Next) | +@Display.Number(r.Max) @(r.Bits == 32 ? "(32-bit)" : "") | +@Display.Number(r.UsedPercent, 2) % | +@(r.Forecast is { } f ? Display.Number(f.PerDay) : "–") | +@(r.Forecast?.DaysLeft is { } d ? Display.Number(d) : "–") | +