镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[DurableTask.ServiceBus] Fix blob container listing and purge for hub names with dashes and culture-dependent container names - #1424

Merged
Bernd Verst (berndverst) merged 8 commits into
Azure:mainfrom
davidemontanari:davidemontanari/dtfx-sb-fix-blob-container-purge-invariant-culture-name
Nov 10, 2026
Merged

Bernd Verst (berndverst) merged 8 commits into
Azure:mainfrom
davidemontanari:davidemontanari/dtfx-sb-fix-blob-container-purge-invariant-culture-name

Conversation

@davidemontanari

@davidemontanari Davide Montanari (davidemontanari) commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes how DurableTask.ServiceBus names, lists and purges overflow blob containers, so that:

  • purge works for hub names with dashes;
  • each hub only lists and deletes its own containers;
  • container names don't depend on the host culture;
  • containers written by earlier versions with a non-Gregorian calendar are never purged by date, but are still removed when the whole store is deleted.

Also includes two small test-related cleanups.

Fixes

Expired containers were never purged for hub names with dashes

Overflow containers are named {hubName}-dtfx-{type}-{date}. BlobStorageClientHelper.IsContainerExpired required exactly 4 dash-separated segments, so every container of a hub like my-hub was skipped. PurgeOrchestrationHistoryAsync never deleted its overflow blobs, and storage grew without limit.

The check now reads the fixed segments from the end and requires the dtfx marker, so any hub name works. A container is expired only if its date:

  • is a valid Gregorian yyyyMMdd date (invariant culture);
  • is on or after 2000-01-01 (see legacy calendar dates below);
  • is earlier than the purge threshold.

Containers whose dates fail these checks are skipped and a warning is traced.

Container listing included other hubs' containers

BlobStorageClient.ListContainersAsync filtered by the {hubName}-dtfx prefix only. Because hub names can contain dashes, this returned:

  • other hubs' containers: for hub alpha, it included alpha-dtfx-backup-dtfx-session-20150516, which belongs to hub alpha-dtfx-backup;
  • names not in the container format: such as alpha-dtfx-session-backup.

Deleting a hub (DeleteBlobStoreContainersAsync) therefore also deleted them, including another hub's overflow messages and session state.

ListContainersAsync now returns only containers owned by this hub (new internal BlobStorageClientHelper.IsContainerOwnedByHub):

  • the name without its trailing -{type}-{date} must equal this hub's {hubName}-dtfx prefix;
  • the type segment must not be empty;
  • the date segment must be 6 to 8 ASCII digits.

Ownership deliberately does not require a valid Gregorian date, so that containers written by earlier versions remain visible to full-store deletion (see below). Any 6–8 digit suffix, e.g. alpha-dtfx-session-20150230, is therefore owned by the hub and deleted by DeleteBlobStoreContainersAsync, but never purged by date.

Both DeleteExpiredContainersAsync and DeleteBlobStoreContainersAsync use this listing.

Container names depended on the host culture

  • Dates: written with the current culture. On hosts using a non-Gregorian calendar, containers got dates such as th-TH 2558xxxx for 2015. Dates are now written with CultureInfo.InvariantCulture. Blob reads and writes are not affected, because the blob key carries the exact container name.
  • Lower-casing: done with ToLower(). On tr-TR, I becomes a dotless ı, which is not valid in a container name. Now uses ToLowerInvariant(), and a redundant ToLower() in BlobStorageClient is removed.

Legacy calendar dates

The invariant formatter only changes newly generated names. Containers created by earlier versions, and the message keys that still reference them, keep the old culture-dependent date. Examples for a message fired on 2026-11-06:

Culture / calendar Old suffix Read as Gregorian
ar-SA (Um Al Qura) 14480525 year 1448
fa-IR (Persian) 14050815 year 1405
th-TH (Thai Buddhist) 25691106 year 2569
ja-JP + JapaneseCalendar 081106 not parseable
zh-TW + TaiwanCalendar 1151106 not parseable

Japanese and Taiwan calendars write the era year with as few as 2 digits, hence the 6–8 digit ownership rule. Some Thai dates are not valid Gregorian dates, e.g. 25670229 for 2024-02-29.

To avoid deleting payloads that may still be referenced:

  • Purge: only Gregorian dates on or after 2000-01-01 can expire. Older-looking, far-future and unparseable dates are never purged by date.
  • Full-store deletion: these containers are still owned by the hub, so DeleteBlobStoreContainersAsync / AzureStorageBlobStore.DeleteStoreAsync removes them, as the previous prefix-based deletion did.

Container name suffix validation accepted invalid characters

The regex @"^[a-z0-9\\-]+$" also matched a backslash (\\ in a verbatim string is a literal backslash), and $ matched before a trailing \n. It is now ^[a-z0-9-]+\z, which accepts only lowercase letters, digits and dashes. It is built once as a static field.

Cleanups

  • ServiceBusOrchestrationService.GetHubQueueMaxDeliveryCountsAsync (internal, used only by tests): Single → SingleOrDefault, so a missing queue returns the intended -1 instead of throwing.
  • WaitForOrchestrationTests no longer uses a fake connection string containing SharedAccessKey=..., which could trigger secret scanners. It now uses a new shared NoOpTokenCredential test helper with the host-name + TokenCredential constructor.

Behaviour changes / upgrade notes

  • BlobStorageClient.ListContainersAsync (public) no longer returns:
    • other hubs' containers that share the prefix;
    • containers whose name isn't {hubName}-dtfx-{type}-{date} with a 6–8 digit date. The framework never creates these, but any created manually are no longer listed or deleted with the hub.
  • Full-store deletion still removes every container the hub owns, including legacy calendar containers and 6–8 digit suffixes that aren't valid Gregorian dates.
  • Purging is on demand. It only runs when the application calls TaskHubClient.PurgeOrchestrationHistoryAsync. For hubs whose names contain dashes, the first purge after upgrading deletes the backlog of the hub's own overflow containers older than the threshold, which may be a large one-off batch.
  • Choosing a threshold: containers are judged by the date in their name. Choose a threshold longer than your longest-idle orchestration, so session state that overflowed long ago is not purged.
  • Legacy calendar containers are never purged by date. Once no longer needed, they must be removed manually or by deleting the hub's store.
  • Scope: hub names with dashes apply to the blob store only. Azure Table names don't allow dashes, so AzureTableInstanceStore needs a dash-free hub name; that is unchanged.

Tests

  • BlobStorageClientHelperTest, offline:
    • IsContainerExpiredHubNameWithDashesTest
    • IsContainerExpiredIgnoresLegacyNonGregorianContainersTest: suffixes generated with the old formatter under ar-SA, fa-IR, th-TH (including 25670229), JapaneseCalendar and TaiwanCalendar (including the pre-2011 6-digit form) are owned by the hub and never expire; the 2000-01-01 boundary.
    • ContainerNamesAreCultureInvariantTest (th-TH, tr-TR)
    • ParseKeyRejectsInvalidSuffixCharactersTest (backslash, trailing newline)
    • IsContainerOwnedByHubTest: overlapping and dashed hubs, empty type, non-digit suffixes, 5- and 9-digit suffixes are rejected; non-Gregorian 6–8 digit suffixes are accepted.
  • BlobStorageClientTest, storage-backed:
    • TestDeleteExpiredContainersPreservesOverlappingHub: with hubs alpha and alpha-dtfx-backup in one account, purging alpha deletes only its own expired container; the other hub's containers remain and its session blob is still readable.
    • TestDeleteBlobStoreContainersPreservesOverlappingHub: alpha lists only its own containers, and deleting the hub keeps the other hub's containers.
    • TestDeleteBlobStoreContainersIgnoresMalformedContainers: {prefix}-session-backup and {prefix}-session-201505160 are neither listed nor deleted with the hub.
    • TestDeleteExpiredContainersPreservesLegacyNonGregorianContainers: ar-SA, Japanese and Taiwan containers survive a purge that deletes a Gregorian expired container; the legacy payload stays readable.
    • TestDeleteBlobStoreContainersDeletesLegacyNonGregorianContainers: payloads in th-TH (25670229), ar-SA, Japanese (081106) and Taiwan (1151106) containers are readable, listed, and deleted with the hub.
  • Each new test fails against the code without the corresponding fix.
  • 47 tests pass on net8.0 and net48:
    • BlobStorageClientHelperTest
    • BlobStorageClientTest (run against Azurite)
    • ServiceBusUtilsTests
    • WaitForOrchestrationTests

Copilot AI balanced review requested due to automatic review settings October 22, 2026 17:21
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Purging a hub can delete another hub’s overflow containers when their naming prefixes overlap.

1 open issue
What changed in this PR

This PR fixes Service Bus overflow blob naming and expiration handling for dashed hub names and culture-dependent formatting, with regression tests and small test utilities cleanups.

Changes:

  • Parse expiration suffixes from the end and use invariant date formatting and lowercasing.
  • Tighten suffix validation and add regression tests for dashed hubs, cultures, and invalid characters.
  • Return -1 for missing queues and replace fake test credentials with a token credential stub.
File Description
test/​DurableTask.ServiceBus.Tests/​WaitForOrchestrationTests.cs Replace the fake connection string with a credential stub.
test/​DurableTask.ServiceBus.Tests/​NoOpTokenCredential.cs Add a credential stub for offline tests.
test/​DurableTask.ServiceBus.Tests/​BlobStorageClientHelperTest.cs Add naming, expiration, and suffix validation tests.
src/​DurableTask.ServiceBus/​Tracking/​BlobStorageClientHelper.cs Fix dashed-name parsing, invariant formatting, and suffix validation.
src/​DurableTask.ServiceBus/​Tracking/​BlobStorageClient.cs Delegate lowercasing to the invariant helper.
src/​DurableTask.ServiceBus/​ServiceBusOrchestrationService.cs Handle missing queues in delivery-count lookup.

Review effort: Balanced

@davidemontanari Davide Montanari (davidemontanari) changed the title [DurableTask.ServiceBus] Fix blob container purge for hub names with dashes and culture-dependent container names [DurableTask.ServiceBus] Fix blob container listing and purge for hub names with dashes and culture-dependent container names Oct 22, 2026
Copilot AI balanced review requested due to automatic review settings October 22, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The ownership filter still allows malformed-date containers to be deleted despite the documented exclusion guarantee.

1 open issue
1 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 22, 2026 18:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Static review identified no blocking issues in the focused fixes and regression coverage; reported test results were not independently verified.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@berndverst Bernd Verst (berndverst) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for two upgrade-compatibility regressions involving containers written by the previous culture-dependent formatter. Newly enabling expiry for dashed hub names can delete payloads whose actual dates are newer than the purge cutoff, while Gregorian-only ownership validation prevents complete hub deletion for some legitimate legacy containers.

Both cases reproduce when comparing the base helper with this HEAD. The 30 targeted net8.0 blob-helper and wait-for-orchestration tests pass, but do not cover these legacy-calendar cases. Storage-backed integration tests and net48 were not run.

Copilot AI balanced review requested due to automatic review settings November 4, 2026 21:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are reported for the overflow-container handling changes.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 5, 2026 01:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the overflow blob handling changes.

No open issues 🎉

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 5, 2026 02:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The description, upgrade notes, and stated test coverage need to clarify that eight-digit legacy suffixes are accepted and their containers can be deleted even when the suffix is not a valid Gregorian date.

1 open issue

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 5, 2026 02:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are identified for the overflow blob container handling changes.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 6, 2026 19:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed to evaluate an unpublished edge case where a maximum cutoff can delete legacy-calendar containers despite the intended preservation guarantee.

No open issues 🎉

Review effort: Balanced

@berndverst
Bernd Verst (berndverst) merged commit 0809e38 into Azure:main Nov 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants