Compare commits
2 Commits
df988abfd8
...
57ec688996
| Author | SHA1 | Date | |
|---|---|---|---|
| 57ec688996 | |||
| 88bdab08d2 |
208
backupmult.ps1
208
backupmult.ps1
@@ -167,20 +167,49 @@ EXECUTE [dbo].[DatabaseBackup]
|
|||||||
Write-Log "SQL Query: $query"
|
Write-Log "SQL Query: $query"
|
||||||
|
|
||||||
# Function to execute backup job with message capture
|
# Function to execute backup job with message capture
|
||||||
function Start-BackupJob($jobId, $sqlInstance, $query, $baseLogFile) {
|
function Start-BackupJob {
|
||||||
|
param(
|
||||||
|
[int]$jobId,
|
||||||
|
[string]$sqlInstance,
|
||||||
|
[string]$query,
|
||||||
|
[string]$baseLogFile
|
||||||
|
)
|
||||||
$scriptBlock = {
|
$scriptBlock = {
|
||||||
param($JobId, $SqlInstance, $Query, $BaseLogFile)
|
param($JobId, $SqlInstance, $Query, $BaseLogFile)
|
||||||
|
|
||||||
# Create job-specific log file path
|
# Debug the base log file parameter
|
||||||
$jobLogFile = $BaseLogFile -replace '\.log$', "-job$JobId.log"
|
Write-Output "DEBUG: BaseLogFile parameter = '$BaseLogFile'"
|
||||||
|
|
||||||
function Write-JobLog($message) {
|
# Create job-specific log file path with fallback
|
||||||
|
if ($BaseLogFile -and $BaseLogFile.Trim() -ne "") {
|
||||||
|
$jobLogFile = $BaseLogFile -replace '\.log$', "-job$JobId.log"
|
||||||
|
} else {
|
||||||
|
# Fallback log file path
|
||||||
|
$jobLogFile = "C:\Rubrik\backup-multi-job$JobId.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "DEBUG: Job log file will be: '$jobLogFile'"
|
||||||
|
|
||||||
|
function Write-JobLog($message, $suppressConsole = $false) {
|
||||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||||
$logEntry = "$timestamp [JOB-$JobId] $message"
|
$logEntry = "$timestamp [JOB-$JobId] $message"
|
||||||
if ($jobLogFile -and $jobLogFile.Trim() -ne "") {
|
if ($jobLogFile -and $jobLogFile.Trim() -ne "") {
|
||||||
Add-Content -Path $jobLogFile -Value $logEntry -Encoding UTF8
|
try {
|
||||||
|
Add-Content -Path $jobLogFile -Value $logEntry -Encoding UTF8
|
||||||
|
# Output to console for debugging (unless suppressed)
|
||||||
|
if (-not $suppressConsole) {
|
||||||
|
Write-Output "LOGGED TO $jobLogFile : $logEntry"
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Output "LOG ERROR: $($_.Exception.Message) - File: $jobLogFile"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Output "NO LOG FILE: jobLogFile is empty or null"
|
||||||
|
}
|
||||||
|
# Always output to console for job monitoring (unless suppressed)
|
||||||
|
if (-not $suppressConsole) {
|
||||||
|
Write-Output $logEntry
|
||||||
}
|
}
|
||||||
Write-Output $logEntry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -198,13 +227,14 @@ function Start-BackupJob($jobId, $sqlInstance, $query, $baseLogFile) {
|
|||||||
$message = $e.Message
|
$message = $e.Message
|
||||||
if ($message -and $message.Trim() -ne "") {
|
if ($message -and $message.Trim() -ne "") {
|
||||||
$script:infoMessages += $message
|
$script:infoMessages += $message
|
||||||
Write-JobLog "SQL INFO: $message"
|
Write-JobLog "SQL INFO: $message" $true # Suppress console output for verbose messages
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Write-JobLog "Attempting to connect to SQL Server: $SqlInstance"
|
||||||
$connection.Open()
|
$connection.Open()
|
||||||
Write-JobLog "Connected to SQL Server"
|
Write-JobLog "Connected to SQL Server successfully"
|
||||||
|
|
||||||
$command = New-Object System.Data.SqlClient.SqlCommand
|
$command = New-Object System.Data.SqlClient.SqlCommand
|
||||||
$command.Connection = $connection
|
$command.Connection = $connection
|
||||||
@@ -223,13 +253,26 @@ function Start-BackupJob($jobId, $sqlInstance, $query, $baseLogFile) {
|
|||||||
$rowData += "$($reader.GetName($i)): $($reader.GetValue($i))"
|
$rowData += "$($reader.GetName($i)): $($reader.GetValue($i))"
|
||||||
}
|
}
|
||||||
if ($rowData.Count -gt 0) {
|
if ($rowData.Count -gt 0) {
|
||||||
Write-JobLog "SQL RESULT: $($rowData -join ', ')"
|
$resultLine = "SQL RESULT: $($rowData -join ', ')"
|
||||||
|
Write-JobLog $resultLine
|
||||||
|
Write-Output $resultLine # Also output for Receive-Job
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$reader.Close()
|
$reader.Close()
|
||||||
Write-JobLog "Backup completed successfully. Captured $($infoMessages.Count) messages."
|
$summaryMessage = "Backup completed successfully. Captured $($infoMessages.Count) messages."
|
||||||
return @{ Success = $true; JobId = $JobId }
|
Write-JobLog $summaryMessage
|
||||||
|
Write-Output $summaryMessage # Also output for Receive-Job
|
||||||
|
|
||||||
|
# Output all captured SQL messages for debugging (only to log file, not console)
|
||||||
|
Write-JobLog "=== SQL MESSAGES START ===" $true
|
||||||
|
foreach ($msg in $infoMessages) {
|
||||||
|
Write-JobLog "SQL: $msg" $true
|
||||||
|
}
|
||||||
|
Write-JobLog "=== SQL MESSAGES END ===" $true
|
||||||
|
|
||||||
|
# Don't return hashtable - just output success message
|
||||||
|
Write-Output "JOB-${JobId}: SUCCESS"
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if ($connection.State -eq [System.Data.ConnectionState]::Open) {
|
if ($connection.State -eq [System.Data.ConnectionState]::Open) {
|
||||||
@@ -239,21 +282,53 @@ function Start-BackupJob($jobId, $sqlInstance, $query, $baseLogFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-JobLog "ERROR: Backup failed - $($_.Exception.Message)"
|
$errorMessage = "ERROR: Backup failed - $($_.Exception.Message)"
|
||||||
|
Write-JobLog $errorMessage
|
||||||
|
Write-Output $errorMessage # Also output for Receive-Job
|
||||||
|
|
||||||
|
# Check for specific connection errors
|
||||||
|
if ($_.Exception.Message -like "*server*not found*" -or
|
||||||
|
$_.Exception.Message -like "*network-related*" -or
|
||||||
|
$_.Exception.Message -like "*instance*" -or
|
||||||
|
$_.Exception.Message -like "*login*failed*") {
|
||||||
|
$connError = "ERROR: CONNECTION FAILURE - Check SQL Server instance name and connectivity"
|
||||||
|
Write-JobLog $connError
|
||||||
|
Write-Output $connError
|
||||||
|
}
|
||||||
|
|
||||||
# Log SQL Server specific errors
|
# Log SQL Server specific errors
|
||||||
if ($_.Exception -is [System.Data.SqlClient.SqlException]) {
|
if ($_.Exception -is [System.Data.SqlClient.SqlException]) {
|
||||||
Write-JobLog "ERROR: SQL Server Error Details:"
|
Write-JobLog "ERROR: SQL Server Error Details:"
|
||||||
|
Write-Output "ERROR: SQL Server Error Details:"
|
||||||
foreach ($sqlError in $_.Exception.Errors) {
|
foreach ($sqlError in $_.Exception.Errors) {
|
||||||
Write-JobLog "ERROR: Severity: $($sqlError.Class), State: $($sqlError.State), Number: $($sqlError.Number)"
|
$errorDetail = "ERROR: Severity: $($sqlError.Class), State: $($sqlError.State), Number: $($sqlError.Number)"
|
||||||
Write-JobLog "ERROR: Message: $($sqlError.Message)"
|
Write-JobLog $errorDetail
|
||||||
|
Write-Output $errorDetail
|
||||||
|
|
||||||
|
$errorMsg = "ERROR: Message: $($sqlError.Message)"
|
||||||
|
Write-JobLog $errorMsg
|
||||||
|
Write-Output $errorMsg
|
||||||
|
|
||||||
if ($sqlError.Procedure) {
|
if ($sqlError.Procedure) {
|
||||||
Write-JobLog "ERROR: Procedure: $($sqlError.Procedure), Line: $($sqlError.LineNumber)"
|
$procError = "ERROR: Procedure: $($sqlError.Procedure), Line: $($sqlError.LineNumber)"
|
||||||
|
Write-JobLog $procError
|
||||||
|
Write-Output $procError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return @{ Success = $false; JobId = $JobId; ErrorMessage = $_.Exception.Message }
|
# Log full exception details for debugging
|
||||||
|
$fullError = "ERROR: Full Exception Type: $($_.Exception.GetType().Name)"
|
||||||
|
Write-JobLog $fullError
|
||||||
|
Write-Output $fullError
|
||||||
|
|
||||||
|
if ($_.Exception.InnerException) {
|
||||||
|
$innerError = "ERROR: Inner Exception: $($_.Exception.InnerException.Message)"
|
||||||
|
Write-JobLog $innerError
|
||||||
|
Write-Output $innerError
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "JOB-${JobId}: FAILED"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +340,7 @@ Write-Log "Starting $Jobs parallel backup jobs using DatabasesInParallel feature
|
|||||||
[System.Collections.ArrayList]$jobList = @()
|
[System.Collections.ArrayList]$jobList = @()
|
||||||
|
|
||||||
for ($i = 1; $i -le $Jobs; $i++) {
|
for ($i = 1; $i -le $Jobs; $i++) {
|
||||||
$job = Start-BackupJob -jobId $i -sqlInstance $SqlInstance -query $query -logFile $logFile
|
$job = Start-BackupJob -jobId $i -sqlInstance $SqlInstance -query $query -baseLogFile $logFile
|
||||||
$null = $jobList.Add($job)
|
$null = $jobList.Add($job)
|
||||||
Write-Log "Started backup job $i (Job ID: $($job.Id))"
|
Write-Log "Started backup job $i (Job ID: $($job.Id))"
|
||||||
Start-Sleep -Milliseconds 100 # Small delay to stagger job starts
|
Start-Sleep -Milliseconds 100 # Small delay to stagger job starts
|
||||||
@@ -280,28 +355,36 @@ while (-not $allJobsCompleted) {
|
|||||||
Start-Sleep -Seconds 5
|
Start-Sleep -Seconds 5
|
||||||
|
|
||||||
foreach ($job in $jobList) {
|
foreach ($job in $jobList) {
|
||||||
if ($job.Id -notin $completedJobs -and $job.State -ne "Running") {
|
if ($job.Id -notin $completedJobs) {
|
||||||
$null = $completedJobs.Add($job.Id)
|
# Check if job is no longer running
|
||||||
|
if ($job.State -eq "Completed" -or $job.State -eq "Failed" -or $job.State -eq "Stopped") {
|
||||||
|
$null = $completedJobs.Add($job.Id)
|
||||||
|
|
||||||
if ($job.State -eq "Completed") {
|
# Get all job output
|
||||||
$result = Receive-Job -Job $job
|
$jobOutput = Receive-Job -Job $job -Keep # Use -Keep to preserve output
|
||||||
if ($result) {
|
|
||||||
foreach ($line in $result) {
|
if ($job.State -eq "Completed") {
|
||||||
Write-Host $line
|
Write-Log "Job $($job.Id) completed successfully"
|
||||||
|
|
||||||
|
# Log all job output to main log
|
||||||
|
if ($jobOutput) {
|
||||||
|
Write-Log "=== Job $($job.Id) Output ==="
|
||||||
|
foreach ($line in $jobOutput) {
|
||||||
|
Write-Log "$line"
|
||||||
|
}
|
||||||
|
Write-Log "=== End Job $($job.Id) Output ==="
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
Write-Log "Job $($job.Id) completed successfully"
|
Write-Log "ERROR: Job $($job.Id) failed with state: $($job.State)"
|
||||||
} else {
|
if ($jobOutput) {
|
||||||
$jobError = Receive-Job -Job $job
|
Write-Log "=== Job $($job.Id) Error Output ==="
|
||||||
Write-Log "ERROR: Job $($job.Id) failed with state: $($job.State)"
|
foreach ($line in $jobOutput) {
|
||||||
if ($jobError) {
|
Write-Log "ERROR: $line"
|
||||||
foreach ($line in $jobError) {
|
}
|
||||||
Write-Log "ERROR: $line"
|
Write-Log "=== End Job $($job.Id) Error Output ==="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Remove-Job -Job $job
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,13 +399,53 @@ while (-not $allJobsCompleted) {
|
|||||||
|
|
||||||
Write-Log "All backup jobs completed"
|
Write-Log "All backup jobs completed"
|
||||||
|
|
||||||
|
# Collect job states and outputs before cleanup for final status check
|
||||||
|
$jobResults = @{}
|
||||||
|
foreach ($job in $jobList) {
|
||||||
|
$jobOutput = Receive-Job -Job $job -Keep -ErrorAction SilentlyContinue
|
||||||
|
$hasFailed = $false
|
||||||
|
|
||||||
|
# Check if job output contains failure indicator
|
||||||
|
if ($jobOutput) {
|
||||||
|
foreach ($line in $jobOutput) {
|
||||||
|
if ($line -like "*JOB-*: FAILED") {
|
||||||
|
$hasFailed = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$jobResults[$job.Id] = @{
|
||||||
|
State = $job.State
|
||||||
|
Failed = $hasFailed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up jobs
|
||||||
|
Write-Log "Cleaning up completed jobs..."
|
||||||
|
foreach ($job in $jobList) {
|
||||||
|
try {
|
||||||
|
if ($job.State -eq "Running") {
|
||||||
|
Write-Log "WARNING: Job $($job.Id) still running, stopping it..."
|
||||||
|
Stop-Job -Job $job -Force
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
|
||||||
|
Write-Log "Cleaned up job $($job.Id)"
|
||||||
|
} catch {
|
||||||
|
Write-Log "WARNING: Could not clean up job $($job.Id): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Consolidate job logs into main log file
|
# Consolidate job logs into main log file
|
||||||
Write-Log "Consolidating job logs..."
|
Write-Log "Consolidating job logs..."
|
||||||
for ($i = 1; $i -le $Jobs; $i++) {
|
for ($i = 1; $i -le $Jobs; $i++) {
|
||||||
$jobLogFile = $logFile -replace '\.log$', "-job$i.log"
|
$jobLogFile = $logFile -replace '\.log$', "-job$i.log"
|
||||||
|
Write-Log "Checking for job log file: $jobLogFile"
|
||||||
if (Test-Path $jobLogFile) {
|
if (Test-Path $jobLogFile) {
|
||||||
try {
|
try {
|
||||||
$jobContent = Get-Content $jobLogFile -ErrorAction Stop
|
$jobContent = Get-Content $jobLogFile -ErrorAction Stop
|
||||||
|
Write-Log "Found $($jobContent.Count) lines in job $i log"
|
||||||
foreach ($line in $jobContent) {
|
foreach ($line in $jobContent) {
|
||||||
Add-Content -Path $logFile -Value $line -Encoding UTF8
|
Add-Content -Path $logFile -Value $line -Encoding UTF8
|
||||||
}
|
}
|
||||||
@@ -331,14 +454,21 @@ for ($i = 1; $i -le $Jobs; $i++) {
|
|||||||
} catch {
|
} catch {
|
||||||
Write-Log "WARNING: Could not consolidate log from job $i : $($_.Exception.Message)"
|
Write-Log "WARNING: Could not consolidate log from job $i : $($_.Exception.Message)"
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Write-Log "WARNING: Job log file not found for job $i"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Final status check
|
# Final status check using job output analysis
|
||||||
$failedJobs = $jobList | Where-Object { $_.State -ne "Completed" }
|
$failedJobIds = $jobResults.Keys | Where-Object { $jobResults[$_].Failed -eq $true }
|
||||||
if ($failedJobs.Count -gt 0) {
|
|
||||||
Write-Log "ERROR: $($failedJobs.Count) jobs failed"
|
if ($failedJobIds.Count -gt 0) {
|
||||||
|
Write-Log "ERROR: $($failedJobIds.Count) out of $($jobResults.Count) backup jobs failed"
|
||||||
|
foreach ($jobId in $failedJobIds) {
|
||||||
|
Write-Log "ERROR: Job ID $jobId failed"
|
||||||
|
}
|
||||||
|
Write-Log "CRITICAL: Backup operation failed - check errors above"
|
||||||
exit 1
|
exit 1
|
||||||
} else {
|
} else {
|
||||||
Write-Log "SUCCESS: All $($jobList.Count) backup jobs completed successfully"
|
Write-Log "SUCCESS: All $($jobResults.Count) backup jobs completed successfully"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user