What happened
On 2026-08-31 three pull requests (#10664, #10665, #10666) were pushed within about 30 seconds. Their Run Cross Platform Tests / linux-tests jobs reached the same test at the same second, and the run for #10665 failed while the other two passed:
[-] sets up log shipping to Azure blob storage using SAS token
WARNING: [Backup-DbaDatabase] Backup of [dbatoolsci_logship_azure] failed | Microsoft.Data.SqlClient.SqlError:
The file https://dbatools.blob.core.windows.net/dbatools/dbatoolsci_logship_azure_FullBackup_PreLogShipping_20260831075712.bak
exists on the remote endpoint, and WITH FORMAT was not specified. Backup cannot proceed.
WARNING: [Invoke-DbaDbLogShipping] Something went wrong restoring the secondary database | Cannot bind argument to parameter 'Path' because it is null.
Expected 'Success', but got $null.
Run: https://github.com/dataplat/dbatools/actions/runs/33370468700 (the PR changes three message strings in the SSIS commands and cannot have caused this). A rerun of the job passes.
Why
.github/scripts/gh-actions.ps1 ("sets up log shipping to Azure blob storage using SAS token") uses a fixed database name, dbatoolsci_logship_azure, and the shared container https://dbatools.blob.core.windows.net/dbatools. Invoke-DbaDbLogShipping builds the blob name from the database name and a timestamp with second resolution:
# public/Invoke-DbaDbLogShipping.ps1:1663-1668
$Timestamp = Get-Date -format "yyyyMMddHHmmss"
$AzureBlobName = "$($db.Name)_FullBackup_PreLogShipping_$Timestamp.bak"
Every CI run on every branch therefore writes to the same container with a name that is unique only per second. Two runs that start close together run the same deterministic test sequence and hit the backup within the same second, and the second one fails because the blob already exists. The test's own retry loop does not help: it only retries on the transient "Operating system error 50", and it does not clean up the blob when the assertion fails, so the collision surfaces as a hard failure of an unrelated pull request.
Proposed fix
Make the name unique per run on the test side, so no command change is needed: derive the database name from the run, for example
$dbName = "dbatoolsci_logship_azure_$env:GITHUB_RUN_ID"
(GITHUB_RUN_ID is unique per workflow run; GITHUB_RUN_ATTEMPT could be appended for reruns.) The blob name then contains the run id and cannot collide with another run. The cleanup at the end of the test already lists and deletes the blobs of the test database by name, so it keeps working.
Optionally, the command could also add WITH FORMAT semantics or a finer timestamp, but for a shared CI container the run-unique database name is the fix that actually removes the race.
created by Claude and reviewed by Andreas Jordan
What happened
On 2026-08-31 three pull requests (#10664, #10665, #10666) were pushed within about 30 seconds. Their
Run Cross Platform Tests/linux-testsjobs reached the same test at the same second, and the run for #10665 failed while the other two passed:Run: https://github.com/dataplat/dbatools/actions/runs/33370468700 (the PR changes three message strings in the SSIS commands and cannot have caused this). A rerun of the job passes.
Why
.github/scripts/gh-actions.ps1("sets up log shipping to Azure blob storage using SAS token") uses a fixed database name,dbatoolsci_logship_azure, and the shared containerhttps://dbatools.blob.core.windows.net/dbatools.Invoke-DbaDbLogShippingbuilds the blob name from the database name and a timestamp with second resolution:Every CI run on every branch therefore writes to the same container with a name that is unique only per second. Two runs that start close together run the same deterministic test sequence and hit the backup within the same second, and the second one fails because the blob already exists. The test's own retry loop does not help: it only retries on the transient "Operating system error 50", and it does not clean up the blob when the assertion fails, so the collision surfaces as a hard failure of an unrelated pull request.
Proposed fix
Make the name unique per run on the test side, so no command change is needed: derive the database name from the run, for example
(
GITHUB_RUN_IDis unique per workflow run;GITHUB_RUN_ATTEMPTcould be appended for reruns.) The blob name then contains the run id and cannot collide with another run. The cleanup at the end of the test already lists and deletes the blobs of the test database by name, so it keeps working.Optionally, the command could also add
WITH FORMATsemantics or a finer timestamp, but for a shared CI container the run-unique database name is the fix that actually removes the race.created by Claude and reviewed by Andreas Jordan