diff --git a/app/TaskFlow.sln b/app/TaskFlow.sln index 8c23611..8c7cfb2 100644 --- a/app/TaskFlow.sln +++ b/app/TaskFlow.sln @@ -13,6 +13,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EDMI.API", "..\..\..\2_DLL EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EDMIService", "..\..\..\2_DLL Projekte\DDMonorepo\Service.EDMIService\EDMIService.vbproj", "{A8C3F298-76AB-4359-AB3C-986E313B4336}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Database", "..\..\..\2_DLL Projekte\DDModules\Database\Database.vbproj", "{EAF0EA75-5FA7-485D-89C7-B2D843B03A96}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CD_ROM|Any CPU = CD_ROM|Any CPU @@ -127,6 +129,26 @@ Global {A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|Any CPU.Build.0 = Release|Any CPU {A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|x86.ActiveCfg = Release|Any CPU {A8C3F298-76AB-4359-AB3C-986E313B4336}.SingleImage|x86.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.CD_ROM|Any CPU.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.CD_ROM|x86.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.CD_ROM|x86.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|x86.ActiveCfg = Debug|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Debug|x86.Build.0 = Debug|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.DVD-5|Any CPU.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.DVD-5|Any CPU.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.DVD-5|x86.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.DVD-5|x86.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|Any CPU.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|x86.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.Release|x86.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.SingleImage|Any CPU.Build.0 = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.SingleImage|x86.ActiveCfg = Release|Any CPU + {EAF0EA75-5FA7-485D-89C7-B2D843B03A96}.SingleImage|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/app/TaskFlow/frmAdmin_AppserviceTest.vb b/app/TaskFlow/frmAdmin_AppserviceTest.vb index a35f1ed..3b1994a 100644 --- a/app/TaskFlow/frmAdmin_AppserviceTest.vb +++ b/app/TaskFlow/frmAdmin_AppserviceTest.vb @@ -88,8 +88,46 @@ Public Class frmAdmin_AppserviceTest Else MemoEdit1.Text &= " (keine)" & Environment.NewLine End If - End Sub + MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine + MemoEdit1.Text &= $"Kürzlich aktive Benutzer (letzte {RECENT_USER_MAX_AGE_MINUTES} Min.):" & Environment.NewLine + If oStatus2.RecentUsers IsNot Nothing AndAlso oStatus2.RecentUsers.Count > 0 Then + For Each oRecentUser In oStatus2.RecentUsers + MemoEdit1.Text &= $" • {oRecentUser}" & Environment.NewLine + Next + Else + MemoEdit1.Text &= " (keine)" & Environment.NewLine + End If + ' ── Scheduler-Metriken ─────────────────────────────────────────── + MemoEdit1.Text &= "─────────────────────────────" & Environment.NewLine + MemoEdit1.Text &= "Scheduler-Metriken:" & Environment.NewLine + MemoEdit1.Text &= $" Letzter Job: {oStatus2.LastJobName}" & Environment.NewLine + MemoEdit1.Text &= $" Laufzeit: {oStatus2.LastJobDurationMs}ms" & Environment.NewLine + MemoEdit1.Text &= $" Zeitpunkt: {oStatus2.LastJobTimestamp:HH:mm:ss}" & Environment.NewLine + MemoEdit1.Text &= $" Slow Queries: {oStatus2.SlowQueryCount} (> 10s seit Service-Start)" & Environment.NewLine + MemoEdit1.Text &= $" Cache-Alter: {oStatus2.CachedTablesSnapshotAge}s" & Environment.NewLine + + If oStatus2.CachedTablesSnapshotAge > 300 Then + MemoEdit1.Text &= " ⚠ Cache ist veraltet – CronJob prüfen!" & Environment.NewLine + End If + If oStatus2.SlowQueryCount > 0 Then + MemoEdit1.Text &= $" ⚠ {oStatus2.SlowQueryCount} langsame Queries aufgetreten!" & Environment.NewLine + End If + + End Sub + ' ── Nicht-parametrisierte ECM-Selects aus frmMain (für DB-Lasttest) ── + Private Shared ReadOnly _ecmQueries As String() = { + "SELECT * FROM TBPM_KONFIGURATION WITH (NOLOCK) WHERE GUID = 1", + "SELECT * FROM TBDD_CONNECTION WITH (NOLOCK) WHERE AKTIV = 1", + "SELECT CAT_TITLE,CAT_STRING FROM TBDD_CATALOG WITH (NOLOCK)", + "SELECT * FROM TBDD_3RD_PARTY_MODULES WITH (NOLOCK) WHERE ACTIVE = 1", + "SELECT * FROM TBDD_SQL_COMMANDS WITH (NOLOCK)", + "SELECT LANG_CODE FROM TBDD_GUI_LANGUAGE WITH (NOLOCK) WHERE ACTIVE = 1 ORDER BY LANG_CODE", + "SELECT * FROM TBDD_COLUMNS_FORMAT WITH (NOLOCK) WHERE MODULE = 'taskFLOW'", + "SELECT * FROM TBPM_CHART WITH (NOLOCK)", + "SELECT * FROM TBPM_MAIN_VIEW_GROUPS WITH (NOLOCK) WHERE ACTIVE = 1", + "SELECT * FROM TBPM_PROFILE WITH (NOLOCK) WHERE ACTIVE = 1" + } Private Sub SimpleButton2_Click(sender As Object, e As EventArgs) Handles btnLoadStart.Click If _loadTimer IsNot Nothing AndAlso _loadTimer.Enabled Then MessageBox.Show("Lasttest läuft bereits.", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information) @@ -201,7 +239,7 @@ Public Class frmAdmin_AppserviceTest ' ── Timer für zyklische Lastoperationen ─────────────────────────────── Private Sub StartLoadTimer() - _loadTimer = New System.Windows.Forms.Timer With {.Interval = 1000} ' jede Sekunde + _loadTimer = New System.Windows.Forms.Timer With {.Interval = 5000} ' jede 5 Sekunden AddHandler _loadTimer.Tick, AddressOf LoadTimer_Tick _loadTimer.Start() End Sub @@ -218,22 +256,19 @@ Public Class frmAdmin_AppserviceTest oSnapshot = _loadClients.ToList() End SyncLock - ' ← MaxDegreeOfParallelism begrenzt gleichzeitige Service-Calls Parallel.ForEach(oSnapshot, New ParallelOptions With { .CancellationToken = _loadCts.Token, - .MaxDegreeOfParallelism = 10 ' ← NEU: max. 10 gleichzeitige Calls + .MaxDegreeOfParallelism = 10 }, Sub(oClient As Client) Try - Select Case _timerTick Mod 4 + Select Case _timerTick Mod 6 Case 0 - ' ► GetStatus je Client Dim st = oClient.GetStatus() _loadLog.Enqueue($"[{Now:HH:mm:ss}] Status: Online={st.IsOnline}, Conn={st.ActiveConnections}") Case 1 - ' ► Zufällige Tabelle aus dem Cache des jeweiligen Clients laden Dim oCachedTables = oClient.GetStatus().CachedTables If oCachedTables IsNot Nothing AndAlso oCachedTables.Count > 0 Then Dim sTable As String @@ -247,14 +282,35 @@ Public Class frmAdmin_AppserviceTest End If Case 2 - ' ► Cache-Zugriff Dim st = oClient.GetStatus() _loadLog.Enqueue($"[{Now:HH:mm:ss}] Cached Tables: {st.CachedTableCount}") Case 3 - ' ► Channel-State prüfen Dim st = oClient.GetStatus() _loadLog.Enqueue($"[{Now:HH:mm:ss}] Channel: {st.ChannelState}") + + Case 4 + ' ✅ Direkter ECM-Datenbankzugriff – konkurriert mit Scheduler-Pool + Dim oSQL As String + SyncLock _rnd + oSQL = _ecmQueries(_rnd.Next(0, _ecmQueries.Length)) + End SyncLock + Dim oWatch = Stopwatch.StartNew() + Dim oResult = oClient.GetDatatableFromECM(oSQL) + oWatch.Stop() + _loadLog.Enqueue($"[{Now:HH:mm:ss}] ECM-SQL → OK={oResult?.OK} Rows={oResult?.Table?.Rows.Count} [{oWatch.ElapsedMilliseconds}ms] SQL=[{oSQL.Substring(0, Math.Min(50, oSQL.Length))}…]") + + Case 5 + ' ✅ Zweiter ECM-Select mit anderem Client – maximiert Pool-Druck + Dim oSQL As String + SyncLock _rnd + oSQL = _ecmQueries(_rnd.Next(0, _ecmQueries.Length)) + End SyncLock + Dim oWatch = Stopwatch.StartNew() + Dim oResult = oClient.GetDatatableFromECM(oSQL) + oWatch.Stop() + _loadLog.Enqueue($"[{Now:HH:mm:ss}] ECM-SQL(2) → OK={oResult?.OK} Rows={oResult?.Table?.Rows.Count} [{oWatch.ElapsedMilliseconds}ms]") + End Select Catch ex As OperationCanceledException ' Lasttest gestoppt