Skip to content

CI: the Azure log shipping test collides across concurrent runs because its backup name is only second-unique #10667

Description

@andreasjordan

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions