Files
pgsql-jellyfin/scripts/windows/Find-SyncDatabaseOperations.ps1
T
wjones 9bcb8501ab Add scripts for PostgreSQL migration and SQLite removal; update project references
- Introduced `publish-with-sql.ps1` to publish Jellyfin with SQL files.
- Added `remove-sqlite.ps1` to facilitate the removal of SQLite dependencies for PostgreSQL-only deployment.
- Created `rollback-to-net10.ps1` to revert .NET 11 changes back to .NET 10.
- Implemented `test_api.py` for testing API interactions.
- Added `verify-migration.ps1` to verify PostgreSQL migration steps.
- Updated various `.csproj` files to include `Microsoft.Kiota.Abstractions` package.
- Enhanced `JellyfinDbContext` to handle concurrency exceptions during save operations.
- Updated tests to include new package references and ensure compatibility with changes.
2026-07-07 10:59:40 -04:00

300 lines
9.9 KiB
PowerShell

# Async Migration Analysis Script
# Scans the Jellyfin codebase for synchronous database operations that need conversion
param(
[string]$Path = ".",
[switch]$DetailedReport,
[switch]$ExportCsv
)
Write-Host "🔍 Scanning for synchronous database operations..." -ForegroundColor Cyan
Write-Host ""
# Define patterns to search for
$patterns = @{
"SaveChanges" = @{
Pattern = "\.SaveChanges\s*\(\s*\)"
Replacement = "await .SaveChangesAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous database save operation"
}
"ToList" = @{
Pattern = "\.ToList\s*\(\s*\)"
Replacement = "await .ToListAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous list materialization"
}
"ToArray" = @{
Pattern = "\.ToArray\s*\(\s*\)"
Replacement = "await .ToArrayAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous array materialization"
}
"FirstOrDefault" = @{
Pattern = "\.FirstOrDefault\s*\("
Replacement = 'await .FirstOrDefaultAsync('
Severity = "HIGH"
Description = "Synchronous first element query"
}
"First" = @{
Pattern = "\.First\s*\("
Replacement = 'await .FirstAsync('
Severity = "HIGH"
Description = "Synchronous first element query"
}
"SingleOrDefault" = @{
Pattern = "\.SingleOrDefault\s*\("
Replacement = 'await .SingleOrDefaultAsync('
Severity = "MEDIUM"
Description = "Synchronous single element query"
}
"Single" = @{
Pattern = "\.Single\s*\("
Replacement = 'await .SingleAsync('
Severity = "MEDIUM"
Description = "Synchronous single element query"
}
"Any" = @{
Pattern = "\.Any\s*\("
Replacement = 'await .AnyAsync('
Severity = "MEDIUM"
Description = "Synchronous existence check"
}
"Count" = @{
Pattern = "\.Count\s*\(\s*\)"
Replacement = "await .CountAsync(cancellationToken)"
Severity = "MEDIUM"
Description = "Synchronous count operation"
}
"LongCount" = @{
Pattern = "\.LongCount\s*\("
Replacement = "await .LongCountAsync("
Severity = "MEDIUM"
Description = "Synchronous long count operation"
}
"ExecuteDelete" = @{
Pattern = "\.ExecuteDelete\s*\(\s*\)"
Replacement = "await .ExecuteDeleteAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous bulk delete"
}
"ExecuteUpdate" = @{
Pattern = "\.ExecuteUpdate\s*\("
Replacement = "await .ExecuteUpdateAsync("
Severity = "HIGH"
Description = "Synchronous bulk update"
}
"BeginTransaction" = @{
Pattern = "\.BeginTransaction\s*\(\s*\)"
Replacement = "await .BeginTransactionAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous transaction start"
}
"Commit" = @{
Pattern = "transaction\.Commit\s*\(\s*\)"
Replacement = "await transaction.CommitAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous transaction commit"
}
"Rollback" = @{
Pattern = "transaction\.Rollback\s*\(\s*\)"
Replacement = "await transaction.RollbackAsync(cancellationToken)"
Severity = "MEDIUM"
Description = "Synchronous transaction rollback"
}
"Sum" = @{
Pattern = "\.Sum\s*\("
Replacement = 'await .SumAsync('
Severity = "LOW"
Description = "Synchronous sum aggregation"
}
"Average" = @{
Pattern = "\.Average\s*\("
Replacement = 'await .AverageAsync('
Severity = "LOW"
Description = "Synchronous average aggregation"
}
"Min" = @{
Pattern = "\.Min\s*\("
Replacement = 'await .MinAsync('
Severity = "LOW"
Description = "Synchronous min aggregation"
}
"Max" = @{
Pattern = "\.Max\s*\("
Replacement = 'await .MaxAsync('
Severity = "LOW"
Description = "Synchronous max aggregation"
}
"Load" = @{
Pattern = "\.Load\s*\(\s*\)"
Replacement = 'await .LoadAsync(cancellationToken)'
Severity = "MEDIUM"
Description = "Synchronous navigation property load"
}
}
# Exclude patterns (already async or intentionally sync)
$excludePatterns = @(
"Async\s*\(",
"// ASYNC",
"\.ConfigureAwait",
"AsyncEnumerable"
)
# Get all .cs files, excluding test files and migrations initially
$files = Get-ChildItem -Path $Path -Recurse -Filter "*.cs" |
Where-Object {
$_.FullName -notmatch "\\bin\\" -and
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\Migrations\\" -and
$_.FullName -notmatch "\.Designer\.cs$"
}
$results = @()
$summary = @{}
foreach ($file in $files) {
$content = Get-Content -Path $file.FullName -Raw
foreach ($patternName in $patterns.Keys) {
$patternInfo = $patterns[$patternName]
$pattern = $patternInfo.Pattern
# Find matches
$matches = [regex]::Matches($content, $pattern)
foreach ($match in $matches) {
# Check if this line is already async or should be excluded
$lineStart = $content.Substring(0, $match.Index).LastIndexOf("`n")
if ($lineStart -lt 0) { $lineStart = 0 }
$lineEnd = $content.IndexOf("`n", $match.Index)
if ($lineEnd -lt 0) { $lineEnd = $content.Length }
$line = $content.Substring($lineStart, $lineEnd - $lineStart).Trim()
# Skip if already async or excluded
$shouldExclude = $false
foreach ($excludePattern in $excludePatterns) {
if ($line -match $excludePattern) {
$shouldExclude = $true
break
}
}
if (-not $shouldExclude) {
# Calculate line number
$lineNumber = ($content.Substring(0, $match.Index) -split "`n").Count
$result = [PSCustomObject]@{
File = $file.FullName.Replace($Path, "").TrimStart('\')
LineNumber = $lineNumber
Pattern = $patternName
Severity = $patternInfo.Severity
Description = $patternInfo.Description
MatchedText = $match.Value
Line = $line
Replacement = $patternInfo.Replacement
}
$results += $result
# Update summary
if (-not $summary.ContainsKey($patternName)) {
$summary[$patternName] = 0
}
$summary[$patternName]++
}
}
}
}
# Display results
Write-Host "📊 Analysis Complete!" -ForegroundColor Green
Write-Host ""
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host "SUMMARY" -ForegroundColor Yellow
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host ""
$totalIssues = $results.Count
$highSeverity = ($results | Where-Object { $_.Severity -eq "HIGH" }).Count
$mediumSeverity = ($results | Where-Object { $_.Severity -eq "MEDIUM" }).Count
$lowSeverity = ($results | Where-Object { $_.Severity -eq "LOW" }).Count
Write-Host "Total synchronous operations found: " -NoNewline
Write-Host $totalIssues -ForegroundColor Red
Write-Host ""
Write-Host " 🔴 HIGH severity: " -NoNewline
Write-Host $highSeverity -ForegroundColor Red
Write-Host " 🟡 MEDIUM severity: " -NoNewline
Write-Host $mediumSeverity -ForegroundColor Yellow
Write-Host " 🟢 LOW severity: " -NoNewline
Write-Host $lowSeverity -ForegroundColor Green
Write-Host ""
Write-Host "Breakdown by operation type:" -ForegroundColor Cyan
Write-Host ""
$summary.GetEnumerator() | Sort-Object Value -Descending | ForEach-Object {
$severity = $patterns[$_.Key].Severity
$color = switch ($severity) {
"HIGH" { "Red" }
"MEDIUM" { "Yellow" }
"LOW" { "Green" }
}
Write-Host " $($_.Key): " -NoNewline
Write-Host $_.Value -ForegroundColor $color -NoNewline
Write-Host " ($severity)"
}
Write-Host ""
Write-Host "=" * 80 -ForegroundColor DarkGray
if ($DetailedReport) {
Write-Host ""
Write-Host "DETAILED FINDINGS" -ForegroundColor Yellow
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host ""
# Group by file
$resultsByFile = $results | Group-Object -Property File
foreach ($fileGroup in $resultsByFile | Sort-Object Name) {
Write-Host ""
Write-Host "📄 $($fileGroup.Name)" -ForegroundColor Cyan
Write-Host "-" * 80 -ForegroundColor DarkGray
foreach ($item in $fileGroup.Group | Sort-Object LineNumber) {
$severityColor = switch ($item.Severity) {
"HIGH" { "Red" }
"MEDIUM" { "Yellow" }
"LOW" { "Green" }
}
Write-Host " Line $($item.LineNumber): " -NoNewline
Write-Host "$($item.Pattern) " -NoNewline -ForegroundColor $severityColor
Write-Host "- $($item.Description)"
Write-Host " Current: " -NoNewline
Write-Host $item.MatchedText -ForegroundColor Gray
Write-Host " Replacement: " -NoNewline
Write-Host $item.Replacement -ForegroundColor Green
}
}
}
if ($ExportCsv) {
$csvPath = Join-Path $Path "async_migration_report.csv"
$results | Export-Csv -Path $csvPath -NoTypeInformation
Write-Host ""
Write-Host "📁 Report exported to: " -NoNewline
Write-Host $csvPath -ForegroundColor Cyan
}
Write-Host ""
Write-Host "💡 TIP: Run with -DetailedReport for line-by-line analysis" -ForegroundColor DarkGray
Write-Host "💡 TIP: Run with -ExportCsv to export findings to CSV" -ForegroundColor DarkGray
Write-Host ""
# Return results for further processing
return $results