From 1ea3127a1d0ff3b863224ab5574cb6224734e510 Mon Sep 17 00:00:00 2001 From: Malin Date: Mon, 16 Feb 2026 20:17:28 +0100 Subject: [PATCH] fix: tighten table lock detection regex and log actual errors The broad "locked" pattern was matching non-lock errors, causing false retries. Now only matches specific BC lock messages. Also surfaces the actual error text in WARN log lines for diagnostics. Co-Authored-By: Claude Opus 4.6 --- bc-export.ps1 | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/bc-export.ps1 b/bc-export.ps1 index 67ec91c..6714a36 100755 --- a/bc-export.ps1 +++ b/bc-export.ps1 @@ -120,30 +120,37 @@ function Invoke-BCApi { $errorBody = $_.ErrorDetails.Message } - # Table lock: BC returns 500 with "being updated in a transaction" - $isTableLock = $errorBody -match "transaction done by another session|being updated in a transaction|deadlock|locked" + # Log the actual error so we can diagnose issues + $shortError = if ($errorBody.Length -gt 200) { $errorBody.Substring(0, 200) + "..." } else { $errorBody } + if (-not $shortError) { $shortError = "$_" } + + # Table lock: BC returns 500 with very specific wording + $isTableLock = $errorBody -match "transaction done by another session|being updated in a transaction|deadlock victim" # Rate limit $isThrottled = ($statusCode -eq 429) - # Server error - $isServerError = ($statusCode -ge 500 -and -not $isTableLock) # Timeout $isTimeout = ($_ -match "Timeout") + # Other server errors (500+) + $isServerError = ($statusCode -ge 500 -and -not $isTableLock) $isRetryable = $isTableLock -or $isThrottled -or $isServerError -or $isTimeout if ($isRetryable -and $attempt -lt $MaxRetries) { if ($isTableLock) { - # Table locks can last a while - wait longer with jitter $wait = [math]::Min(30 + ($attempt * 15), 120) - Write-Log " Table lock detected (attempt $attempt/$MaxRetries), waiting ${wait}s..." "WARN" + Write-Log " Table lock (attempt $attempt/$MaxRetries), waiting ${wait}s... Error: $shortError" "WARN" } elseif ($isThrottled) { $wait = [math]::Min(30 * $attempt, 300) Write-Log " Rate limited (attempt $attempt/$MaxRetries), waiting ${wait}s..." "WARN" } + elseif ($isTimeout) { + $wait = [math]::Min(15 * $attempt, 120) + Write-Log " Timeout (attempt $attempt/$MaxRetries), retrying in ${wait}s..." "WARN" + } else { - $wait = [math]::Min(10 * $attempt, 120) - Write-Log " Request failed (attempt $attempt/$MaxRetries), retrying in ${wait}s..." "WARN" + $wait = [math]::Min(15 * $attempt, 120) + Write-Log " HTTP $statusCode (attempt $attempt/$MaxRetries), retrying in ${wait}s... Error: $shortError" "WARN" } Start-Sleep -Seconds $wait continue @@ -212,7 +219,7 @@ function Export-EntityData { } catch { $errorMsg = "$_" - $isTableLock = $errorMsg -match "transaction done by another session|being updated in a transaction|deadlock|locked" + $isTableLock = $errorMsg -match "transaction done by another session|being updated in a transaction|deadlock victim" if ($isTableLock -and $entityAttempt -lt $maxEntityRetries) { $wait = 60 * $entityAttempt @@ -324,7 +331,7 @@ function Export-DocumentWithLines { } catch { $errorMsg = "$_" - $isTableLock = $errorMsg -match "transaction done by another session|being updated in a transaction|deadlock|locked" + $isTableLock = $errorMsg -match "transaction done by another session|being updated in a transaction|deadlock victim" if ($isTableLock -and $entityAttempt -lt $maxEntityRetries) { $wait = 60 * $entityAttempt