diff --git a/Framework/Tracking/BlobStorageClient.cs b/Framework/Tracking/BlobStorageClient.cs index 5b198aba7..53143c62f 100644 --- a/Framework/Tracking/BlobStorageClient.cs +++ b/Framework/Tracking/BlobStorageClient.cs @@ -27,11 +27,11 @@ namespace DurableTask.Tracking /// public class BlobStorageClient { - // use hubName as the container prefix. - // the container full name is in the format of {hubName}-{streamType}-{DateTime}; + // container prefix is in the format of {hubName}-dtfx. It is not part of the blob storage key. + // the container full name is in the format of {hubName}-dtfx-{streamType}-{DateTime}; // the streamType is the type of the stream, either 'message' or 'session'; // the date time is in the format of yyyyMMdd. - readonly string hubName; + readonly string containerNamePrefix; readonly CloudBlobClient blobClient; const int MaxRetries = 3; @@ -59,9 +59,9 @@ public BlobStorageClient(string hubName, string connectionString) this.blobClient.DefaultRequestOptions.RetryPolicy = new ExponentialRetry(DeltaBackOff, MaxRetries); this.blobClient.DefaultRequestOptions.MaximumExecutionTime = MaximumExecutionTime; - // save the lower case since it will be used as the prefix of the container name, + // make the hub name lower case since it will be used as part of the prefix of the container name, // which only allows lower case letters - this.hubName = hubName.ToLower(); + this.containerNamePrefix = BlobStorageClientHelper.BuildContainerNamePrefix(hubName.ToLower()); } /// @@ -99,19 +99,19 @@ public async Task DownloadStreamAsync(string key) async Task GetCloudBlockBlobReferenceAsync(string containerNameSuffix, string blobName) { - string containerName = BlobStorageClientHelper.BuildContainerName(hubName, containerNameSuffix); + string containerName = BlobStorageClientHelper.BuildContainerName(this.containerNamePrefix, containerNameSuffix); var cloudBlobContainer = this.blobClient.GetContainerReference(containerName); await cloudBlobContainer.CreateIfNotExistsAsync(); return cloudBlobContainer.GetBlockBlobReference(blobName); } /// - /// List all containers of the blob storage, whose prefix is hub name. + /// List all containers of the blob storage, whose prefix is containerNamePrefix, i.e., {hubName}-dtfx. /// /// A list of Azure blob containers public IEnumerable ListContainers() { - return this.blobClient.ListContainers(this.hubName); + return this.blobClient.ListContainers(this.containerNamePrefix); } /// @@ -127,7 +127,7 @@ public async Task DeleteExpiredContainersAsync(DateTime thresholdDateTimeUtc) } /// - /// Delete blob containers with the hub name as prefix. + /// Delete blob containers with the containerNamePrefix as prefix. /// /// public async Task DeleteBlobStoreContainersAsync() diff --git a/Framework/Tracking/BlobStorageClientHelper.cs b/Framework/Tracking/BlobStorageClientHelper.cs index b1e798f36..4388f1fe7 100644 --- a/Framework/Tracking/BlobStorageClientHelper.cs +++ b/Framework/Tracking/BlobStorageClientHelper.cs @@ -61,6 +61,18 @@ static string BuildContainerNameSuffix(string containerType, DateTime blobCreati return $"{containerType.ToLower()}{ContainerNameDelimiter}{GetDateStringForContainerName(blobCreationTime)}"; } + /// + /// Build the container name prefix using the lower case hub name. + /// It is in the format of {hubName}-dtfx. + /// The container name prefix is not part of the generated storage key. + /// + /// The hub name. Converted to lower case to build the prefix. + /// The container name prefix + public static string BuildContainerNamePrefix(string hubName) + { + return $"{hubName.ToLower()}{ContainerNameDelimiter}dtfx"; + } + /// /// Build a storage key for the session. /// @@ -98,7 +110,7 @@ public static void ParseKey(string key, out string containerNameSuffix, out stri string[] segments = key.Split(new[] {BlobStorageClientHelper.KeyDelimiter}, 2); if (segments.Length < 2) { - throw new ArgumentException("storage key {key} does not contain required 2 or more segments: containerNameSuffix|blobName.", nameof(key)); + throw new ArgumentException($"storage key {key} does not contain required 2 or more segments: containerNameSuffix|blobName.", nameof(key)); } containerNameSuffix = segments[0]; @@ -134,11 +146,11 @@ static bool IsValidContainerNameSuffix(string containerNameSuffix) public static bool IsContainerExpired(string containerName, DateTime thresholdDateTimeUtc) { string[] segments = containerName.Split(ContainerNameDelimiter); - if (segments.Length != 3) + if (segments.Length != 4) { TraceHelper.Trace( TraceEventType.Warning, - $"container name {containerName} does not contain required 3 segments."); + $"container name {containerName} does not contain required 4 segments."); return false; } diff --git a/FrameworkUnitTests/BlobStorageClientHelperTest.cs b/FrameworkUnitTests/BlobStorageClientHelperTest.cs index d547b4105..23a8c4b9e 100644 --- a/FrameworkUnitTests/BlobStorageClientHelperTest.cs +++ b/FrameworkUnitTests/BlobStorageClientHelperTest.cs @@ -26,14 +26,14 @@ public void IsContainerExpiredTest() { Assert.AreEqual("ab-cd", BlobStorageClientHelper.BuildContainerName("ab", "cd")); - Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubName-message-20100101", DateTime.UtcNow)); - Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubName-session-20990101", DateTime.UtcNow)); + Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-message-20100101", DateTime.UtcNow)); + Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-session-20990101", DateTime.UtcNow)); DateTime dateTime = new DateTime(2015, 05, 17); - Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubName-message-20150516", dateTime)); - Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubName-message-20150517", dateTime)); - Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubName-message-20150518", dateTime)); - Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubName-message-20140518", dateTime)); + Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-message-20150516", dateTime)); + Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-message-20150517", dateTime)); + Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-message-20150518", dateTime)); + Assert.IsTrue(BlobStorageClientHelper.IsContainerExpired("hubname-dtfx-message-20140518", dateTime)); // invalid containers are ignored Assert.IsFalse(BlobStorageClientHelper.IsContainerExpired("invalidContainerName", DateTime.UtcNow)); @@ -63,6 +63,14 @@ public void BuildSessionStorageKeyTest() Assert.IsTrue(regex.Match(key).Success); } + [TestMethod] + public void BuildContainerNamePrefixTest() + { + string hubName = "HubName"; + string containerNamePrefix = BlobStorageClientHelper.BuildContainerNamePrefix(hubName); + Assert.AreEqual("hubname-dtfx", containerNamePrefix); + } + [TestMethod] public void ParseKeyTest() { @@ -92,7 +100,7 @@ public void ParseKeyTest() } catch (ArgumentException e) { - Assert.IsTrue(e.Message.Contains("containerNameSuffix"), "Exception must contain containerNameSuffix."); + Assert.IsTrue(e.Message.Contains("Message-20100319"), "Exception must contain the invalid container name suffix."); } } }