Neue Performance-Metriken in `EDMIService` hinzugefügt, inkl. Felder für Job-Dauer, langsame Queries und Timestamps. `ServiceStatusResponse` um diese Metriken erweitert. `DatatableJob` optimiert: `<DisallowConcurrentExecution>` hinzugefügt, Logging für langsame Queries (>10s) und Jobs (>30s) verbessert, sowie Gesamt-Dauer-Messung integriert. `Scheduler` nutzt nun dedizierte MSSQL-Instanz zur Entkopplung von Datenbankoperationen. Thread-Pool-Größe reduziert und gestaffeltes Starten von INIT-Jobs eingeführt. Connection-Strings in `WindowsService` angepasst: `ApplicationName` spezifisch gesetzt und `MaxPoolSize` erhöht. Allgemeine Logging-Verbesserungen für bessere Skalierbarkeit und Stabilität.
129 lines
6.3 KiB
VB.net
129 lines
6.3 KiB
VB.net
Option Explicit On
|
|
|
|
Imports DigitalData.Modules.Database
|
|
Imports DigitalData.Modules.Logging
|
|
|
|
Imports Quartz
|
|
<DisallowConcurrentExecution> Public Class DatatableJob
|
|
Implements IJob
|
|
Private Const COMMAND_SQL_TIMEOUT = 240
|
|
|
|
Public Function Execute(context As IJobExecutionContext) As Task Implements IJob.Execute
|
|
Dim oJobData = context.MergedJobDataMap
|
|
Dim oLogConfig As LogConfig = oJobData.Item("LogConfig")
|
|
Dim oCronJobId As Integer = oJobData.Item("CronJobId")
|
|
Dim oCronJobTitle As String = oJobData.Item("CronJobTitle")
|
|
Dim oLogger As Logger = oLogConfig.GetLogger()
|
|
|
|
Dim oStopWatch As New Stopwatch()
|
|
Dim oResult As New JobResult()
|
|
|
|
oLogger.Info("Running Datatable Job [{0}]/[{1}]", context.JobDetail.Key, oCronJobTitle)
|
|
Dim oJobStopWatch As New Stopwatch() ' ← Gesamt-Messung für den gesamten Job
|
|
oJobStopWatch.Start()
|
|
Try
|
|
Dim oMSSQL As MSSQLServer = oJobData.Item("MSSQL")
|
|
Dim oCronSQL As String = $"SELECT * FROM TBAPPSERV_CRON_DETAIL WITH (NOLOCK) WHERE CRON_ID = {oCronJobId}"
|
|
Dim oCronDetails As DataTable = oMSSQL.GetDatatable(oCronSQL)
|
|
|
|
For Each oRow As DataRow In oCronDetails.Rows
|
|
Dim oDatatableName As String = IIf(IsDBNull(oRow.Item("DT_NAME")), String.Empty, oRow.Item("DT_NAME"))
|
|
Dim oParentColumn As String = IIf(IsDBNull(oRow.Item("DT_COLUMN")), String.Empty, oRow.Item("DT_COLUMN"))
|
|
Dim oChildColumn As String = IIf(IsDBNull(oRow.Item("CHILD_DT_COLUMN")), String.Empty, oRow.Item("CHILD_DT_COLUMN"))
|
|
Dim oConnectionId As Integer = IIf(IsDBNull(oRow.Item("CON_ID")), 0, oRow.Item("CON_ID"))
|
|
Dim oTitle As String = IIf(IsDBNull(oRow.Item("TITLE")), String.Empty, oRow.Item("TITLE"))
|
|
Dim oSQL As String = IIf(IsDBNull(oRow.Item("COMMAND")), String.Empty, oRow.Item("COMMAND"))
|
|
Dim oIndexColumns As String = IIf(IsDBNull(oRow.Item("INDEX_COLUMNS")), String.Empty, oRow.Item("INDEX_COLUMNS"))
|
|
|
|
Try
|
|
oLogger.Debug("Running Command-Job [{0}]", oTitle)
|
|
oLogger.Debug("Datatable Name: {0}", oDatatableName)
|
|
oLogger.Debug("Connection Id: {0}", oConnectionId)
|
|
|
|
oStopWatch.Restart()
|
|
|
|
Dim oConnectionString As String = oMSSQL.Get_ConnectionStringforID(oConnectionId)
|
|
|
|
Dim oTable = oMSSQL.GetDatatableWithConnection(oSQL, oConnectionString, COMMAND_SQL_TIMEOUT)
|
|
oTable.TableName = oDatatableName
|
|
|
|
oStopWatch.Stop()
|
|
Dim oQueryMs = oStopWatch.ElapsedMilliseconds
|
|
|
|
' ✅ Schwellwert-Warnung: Query dauert länger als 10s
|
|
If oQueryMs > 10000 Then
|
|
oLogger.Warn("⚠ SLOW QUERY [{0}] | Datatable=[{1}] | Duration=[{2}ms] | SQL=[{3}]",
|
|
oTitle, oDatatableName, oQueryMs, oSQL.Substring(0, Math.Min(200, oSQL.Length)))
|
|
Else
|
|
oLogger.Debug("[Time] Getting Data From DB: {0}ms", oQueryMs)
|
|
End If
|
|
oStopWatch.Restart()
|
|
|
|
Dim oView As DataView = Nothing
|
|
|
|
' This creates an Index for the columns specified in INDEX_COLUMNS to speed up calls to 'Table.Select'!
|
|
If oIndexColumns <> String.Empty Then
|
|
oLogger.Debug("Adding indexes for Table: [{0}]", oIndexColumns)
|
|
oView = New DataView(oTable) With {
|
|
.Sort = oIndexColumns
|
|
}
|
|
End If
|
|
|
|
oStopWatch.Stop()
|
|
oLogger.Debug("[Time] Creating Indexes: {0}ms", oStopWatch.ElapsedMilliseconds)
|
|
oStopWatch.Restart()
|
|
|
|
oLogger.Debug("Result Datatable [{0}] contains [{1}] rows", oTable.TableName, oTable.Rows.Count)
|
|
|
|
Dim oResultTable = New JobResult.ResultTable() With {
|
|
.Table = oTable,
|
|
.View = oView,
|
|
.DetailRow = oRow
|
|
}
|
|
|
|
Dim oChildTableNAme As String = IIf(IsDBNull(oRow.Item("CHILD_DT_NAME")), String.Empty, oRow.Item("CHILD_DT_NAME"))
|
|
|
|
If oChildTableNAme <> String.Empty Then
|
|
oLogger.Debug("Child Datatable [{0}] defined, Relation: Parent [{1}] -> Child [{2}]", oChildTableNAme, oParentColumn, oChildColumn)
|
|
|
|
Dim oChildTable As DataTable = oMSSQL.GetDatatableWithConnection($"SELECT * FROM {oChildTableNAme}", oConnectionString)
|
|
oChildTable.TableName = oChildTableNAme
|
|
oLogger.Debug("Child Datatable [{0}] contains [{1}] rows", oChildTable.TableName, oChildTable.Rows.Count)
|
|
|
|
oResultTable.ChildTable = oChildTable
|
|
oResultTable.ChildRelationColumn = oChildColumn
|
|
oResultTable.TableRelationColumn = oParentColumn
|
|
End If
|
|
|
|
oStopWatch.Stop()
|
|
oLogger.Debug("[Time] Adding Relations: {0}ms", oStopWatch.ElapsedMilliseconds)
|
|
|
|
oLogger.Info("✔ Fetched Datatable [{0}]", oDatatableName)
|
|
oResult.Tables.Add(oResultTable)
|
|
Catch ex As Exception
|
|
oLogger.Warn("⚠ Error while saving Table: [{0}]", oDatatableName)
|
|
oLogger.Error(ex)
|
|
End Try
|
|
Next
|
|
|
|
' Das Ergebnis speichern
|
|
context.Result = oResult
|
|
oJobStopWatch.Stop()
|
|
' ✅ Gesamt-Performance-Log pro Job-Ausführung
|
|
Dim oTotalMs = oJobStopWatch.ElapsedMilliseconds
|
|
If oTotalMs > 30000 Then
|
|
oLogger.Warn("⚠ SLOW JOB [{0}] | Tables=[{1}] | TotalDuration=[{2}ms]",
|
|
oCronJobTitle, oResult.Tables.Count, oTotalMs)
|
|
Else
|
|
oLogger.Info("✔✔ Fetched [{0}] Datatables in [{1}ms] ✔✔", oResult.Tables.Count, oTotalMs)
|
|
End If
|
|
|
|
Catch ex As Exception
|
|
oLogger.Error(ex)
|
|
oLogger.Warn("⚠ Unhandled exception while executing SQL for Datatable {0}", oCronJobTitle)
|
|
End Try
|
|
|
|
Return Task.FromResult(True)
|
|
End Function
|
|
End Class
|