bin/Public/Get-sqmOperationStatus.ps1
|
<#
.SYNOPSIS Zeigt den Fortschritt und die geschaetzte Restdauer fuer aktive Backup-, Restore- und AutoSeed-Operationen an. .DESCRIPTION Die Funktion ueberwacht aktive SQL Server-Operationen (Backup, Restore, AutoSeed) und berechnet den Fortschritt sowie die geschaetzte verbleibende Zeit. Sie kombiniert Informationen aus: - Backup- und Restore-Fortschritt: sys.dm_exec_requests - AutoSeed-Fortschritt: sys.dm_hadr_physical_seeding_stats Die Funktion kann auf einer bestimmten Instanz ausgefuehrt werden und zeigt standardmaessig alle aktiven Operationen an. ueber Parameter kann nach Vorgangstyp (Backup, Restore, AutoSeed) gefiltert werden. Wenn kein SqlInstance-Parameter angegeben wird, wird standardmaessig der aktuelle Computername ($env:COMPUTERNAME) verwendet. Diese Regel gilt fuer alle zukuenftigen Versionen. .PARAMETER SqlInstance Die Ziel-SQL Server-Instanz (Standard: aktueller Computername). .PARAMETER SqlCredential Alternative Anmeldeinformationen. .PARAMETER OperationType Filtert nach Vorgangstyp. Moegliche Werte: 'Backup', 'Restore', 'AutoSeed'. Standardmaessig werden alle aktiven Operationen angezeigt. .PARAMETER Continuous Wenn gesetzt, wird die Ausgabe kontinuierlich aktualisiert (aehnlich wie 'watch'). Beenden mit Strg+C. .PARAMETER RefreshSeconds Intervall fuer die kontinuierliche Aktualisierung in Sekunden (Standard: 5). Nur in Verbindung mit -Continuous. .PARAMETER EnableException Schalter, um Ausnahmen durchzulassen (standardmaessig werden Fehler als Warnung protokolliert). .EXAMPLE # Alle aktiven Operationen auf der lokalen Instanz anzeigen Get-sqmOperationStatus .EXAMPLE # Nur aktive AutoSeed-Vorgaenge auf einer entfernten Instanz Get-sqmOperationStatus -SqlInstance "SQL01" -OperationType AutoSeed .EXAMPLE # Kontinuierliche Aktualisierung alle 10 Sekunden Get-sqmOperationStatus -Continuous -RefreshSeconds 10 .NOTES Erfordert dbatools und Invoke-sqmLogging. Die Berechnung der geschaetzten Restdauer basiert auf dem bisherigen Fortschritt und der vergangenen Zeit. Die Genauigkeit verbessert sich mit fortschreitender Operation. #> function Get-sqmOperationStatus { [CmdletBinding()] param ( [Parameter(Mandatory = $false, Position = 0)] [string]$SqlInstance, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential]$SqlCredential, [Parameter(Mandatory = $false)] [ValidateSet('Backup', 'Restore', 'AutoSeed')] [string]$OperationType, [Parameter(Mandatory = $false)] [switch]$Continuous, [Parameter(Mandatory = $false)] [int]$RefreshSeconds = 5, [Parameter(Mandatory = $false)] [switch]$EnableException ) begin { $functionName = $MyInvocation.MyCommand.Name if (-not $PSBoundParameters.ContainsKey('SqlInstance') -or [string]::IsNullOrWhiteSpace($SqlInstance)) { $SqlInstance = $env:COMPUTERNAME } if (-not $script:dbatoolsAvailable) { throw "dbatools-Modul nicht gefunden. Bitte installieren: Install-Module dbatools" } Invoke-sqmLogging -Message "Starte $functionName auf $SqlInstance" -FunctionName $functionName -Level "INFO" } process { do { try { # 1. Backup/Restore Operationen ueber sys.dm_exec_requests abrufen $backupRestoreQuery = @" SELECT r.session_id, r.command, r.percent_complete, r.estimated_completion_time, r.start_time, DB_NAME(r.database_id) AS database_name, DATEADD(ms, r.estimated_completion_time, GETDATE()) AS expected_completion_time FROM sys.dm_exec_requests r WHERE r.command IN ('BACKUP DATABASE', 'RESTORE DATABASE', 'BACKUP LOG', 'RESTORE LOG') AND r.percent_complete > 0 "@ $backupRestoreOps = Invoke-DbaQuery -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Query $backupRestoreQuery -Database master -ErrorAction Stop # 2. AutoSeed Operationen ueber sys.dm_hadr_physical_seeding_stats abrufen # Diese DMV existiert ab SQL Server 2016 $autoSeedQuery = @" SELECT local_database_name AS database_name, role_desc, internal_state_desc, transfer_rate_bytes_per_second, transferred_size_bytes, total_size_bytes, start_time, estimated_completion_time_ms, CASE WHEN total_size_bytes > 0 THEN (transferred_size_bytes * 100.0 / total_size_bytes) ELSE 0 END AS percent_complete FROM sys.dm_hadr_physical_seeding_stats WHERE internal_state_desc IN ('RUNNING', 'IN_PROGRESS') "@ $autoSeedOps = @() try { $autoSeedOps = Invoke-DbaQuery -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Query $autoSeedQuery -Database master -ErrorAction Stop } catch { # DMV existiert moeglicherweise nicht (z.B. SQL Server 2014 oder aelter) if ($_.Exception.Message -match 'Invalid object name') { Write-Verbose "sys.dm_hadr_physical_seeding_stats nicht verfuegbar (SQL Server < 2016)" } else { throw } } # Ergebnisse kombinieren und filtern $allOps = @() # Backup/Restore Operationen verarbeiten foreach ($op in $backupRestoreOps) { $operationType = if ($op.command -match 'BACKUP') { 'Backup' } else { 'Restore' } if ($OperationType -and $OperationType -ne $operationType) { continue } $percentComplete = [math]::Round($op.percent_complete, 2) $remainingSeconds = [math]::Round($op.estimated_completion_time / 1000, 0) $remainingTimeFormatted = if ($remainingSeconds -gt 0) { Format-sqmTimeSpan -Seconds $remainingSeconds } else { "Unbekannt" } $allOps += [PSCustomObject]@{ OperationType = $operationType DatabaseName = $op.database_name SessionId = $op.session_id PercentComplete = $percentComplete RemainingTime = $remainingTimeFormatted RemainingSeconds = $remainingSeconds ExpectedCompletion = if ($op.expected_completion_time) { $op.expected_completion_time } else { $null } StartTime = $op.start_time TransferRate = $null TransferredSize = $null TotalSize = $null Status = $op.command } } # AutoSeed Operationen verarbeiten foreach ($op in $autoSeedOps) { if ($OperationType -and $OperationType -ne 'AutoSeed') { continue } $percentComplete = [math]::Round($op.percent_complete, 2) $remainingSeconds = if ($op.estimated_completion_time_ms -gt 0) { [math]::Round($op.estimated_completion_time_ms / 1000, 0) } else { $null } $remainingTimeFormatted = if ($remainingSeconds -gt 0) { Format-sqmTimeSpan -Seconds $remainingSeconds } else { "Unbekannt" } $transferRateFormatted = if ($op.transfer_rate_bytes_per_second -gt 0) { Format-sqmFileSize -Bytes $op.transfer_rate_bytes_per_second } else { "N/A" } $allOps += [PSCustomObject]@{ OperationType = 'AutoSeed' DatabaseName = $op.database_name SessionId = $null PercentComplete = $percentComplete RemainingTime = $remainingTimeFormatted RemainingSeconds = $remainingSeconds ExpectedCompletion = $null StartTime = $op.start_time TransferRate = $transferRateFormatted TransferredSize = Format-sqmFileSize -Bytes $op.transferred_size_bytes TotalSize = Format-sqmFileSize -Bytes $op.total_size_bytes Status = $op.internal_state_desc } } # Ausgabe if ($Continuous) { Clear-Host Write-Host "=== SQL Server Operation Status auf $SqlInstance ===" -ForegroundColor Cyan Write-Host "Aktualisierung alle $RefreshSeconds Sekunden (Strg+C zum Beenden)" -ForegroundColor Gray Write-Host "" } if ($allOps.Count -eq 0) { $msg = "Keine aktiven $($OperationType -replace 'AutoSeed', 'AutoSeed-')Operationen gefunden." Write-Host $msg -ForegroundColor Yellow Invoke-sqmLogging -Message $msg -FunctionName $functionName -Level "INFO" } else { # Formatierte Ausgabe als Tabelle $displayProps = @( @{ Name = 'Type'; Expression = { $_.OperationType } }, @{ Name = 'Database'; Expression = { $_.DatabaseName } }, @{ Name = 'Status'; Expression = { $_.Status } }, @{ Name = 'Progress'; Expression = { "$($_.PercentComplete)%" } }, @{ Name = 'Remaining'; Expression = { $_.RemainingTime } } ) # Zusaetzliche Spalten fuer AutoSeed if ($allOps | Where-Object { $_.OperationType -eq 'AutoSeed' }) { $displayProps += @{ Name = 'Rate'; Expression = { $_.TransferRate } } $displayProps += @{ Name = 'Transferred'; Expression = { $_.TransferredSize } } } $allOps | Select-Object -Property ($displayProps | ForEach-Object { $_.Name }) | Format-Table -AutoSize } # Kontinuierliche Ausfuehrung if ($Continuous) { Start-Sleep -Seconds $RefreshSeconds } } catch { $errMsg = "Fehler beim Abrufen der Operationen: $($_.Exception.Message)" Invoke-sqmLogging -Message $errMsg -FunctionName $functionName -Level "ERROR" if ($EnableException) { throw } Write-Error $errMsg if ($Continuous) { Start-Sleep -Seconds $RefreshSeconds } } } while ($Continuous) } end { Invoke-sqmLogging -Message "$functionName abgeschlossen." -FunctionName $functionName -Level "INFO" } } |