diff --git a/OrleansShardedStorageProvider/AzureShardedGrainBase.cs b/OrleansShardedStorageProvider/AzureShardedGrainBase.cs new file mode 100644 index 0000000..269fef8 --- /dev/null +++ b/OrleansShardedStorageProvider/AzureShardedGrainBase.cs @@ -0,0 +1,79 @@ +using Orleans.Runtime; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OrleansShardedStorageProvider +{ + public class AzureShardedGrainBase + { + protected readonly string _serviceId; + protected readonly AzureShardedStorageOptions _options; + + public AzureShardedGrainBase(string serviceId, AzureShardedStorageOptions options) + { + _serviceId = serviceId; + _options = options; + } + + protected int GetShardNumberFromKey(string pk) + { + var hash = GetStableHashCode(pk); + var storageNum = Math.Abs(hash % this._options.ConnectionStrings.Count()); + + return storageNum; + } + + /// + /// Take from https://stackoverflow.com/a/36845864/852806 + /// + /// + /// + protected int GetStableHashCode(string str) + { + unchecked + { + int hash1 = 5381; + int hash2 = hash1; + + for (int i = 0; i < str.Length && str[i] != '\0'; i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ str[i]; + if (i == str.Length - 1 || str[i + 1] == '\0') + break; + hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; + } + + return hash1 + (hash2 * 1566083941); + } + } + + + private const string KeyStringSeparator = "__"; + + protected string GetKeyString(GrainId grainId) + { + var key = $"{this._serviceId}{KeyStringSeparator}{grainId.ToString()}"; + + return SanitizeTableProperty(key); + } + + protected string SanitizeTableProperty(string key) + { + // Remove any characters that can't be used in Azure PartitionKey or RowKey values + // http://www.jamestharpe.com/web-development/azure-table-service-character-combinations-disallowed-in-partitionkey-rowkey/ + key = key + .Replace('/', '_') // Forward slash + .Replace('\\', '_') // Backslash + .Replace('#', '_') // Pound sign + .Replace('?', '_'); // Question mark + + if (key.Length >= 1024) + throw new ArgumentException(string.Format("Key length {0} is too long to be an Azure table key. Key={1}", key.Length, key)); + + return key; + } + } +} diff --git a/OrleansShardedStorageProvider/AzureShardedGrainStorage.cs b/OrleansShardedStorageProvider/AzureShardedGrainStorage.cs index c0c472b..d39ef21 100644 --- a/OrleansShardedStorageProvider/AzureShardedGrainStorage.cs +++ b/OrleansShardedStorageProvider/AzureShardedGrainStorage.cs @@ -1,5 +1,6 @@ using Azure; using Azure.Data.Tables; +using Azure.Identity; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Microsoft.Extensions.DependencyInjection; @@ -19,24 +20,21 @@ namespace OrleansShardedStorageProvider /// Origin: https://github.com/JsAndDotNet/OrleansShardedStorage /// Similar to Oreleans:src\Azure\Orleans.Persistence.AzureStorage\Providers\Storage\AzureTableStorage.cs /// - public class AzureShardedGrainStorage : IGrainStorage, ILifecycleParticipant + public class AzureShardedGrainStorage : AzureShardedGrainBase, IGrainStorage, ILifecycleParticipant { - private readonly string _serviceId; private readonly string _name; private readonly ILogger _logger; - private readonly AzureShardedStorageOptions _options; private List _tableClients = new List(); private List _blobClients = new List(); private StorageType _storageType = StorageType.TableStorage; public AzureShardedGrainStorage(string name, AzureShardedStorageOptions options, IOptions clusterOptions, ILoggerFactory loggerFactory) + : base(clusterOptions.Value.ServiceId, options) { this._name = name; var loggerName = $"{typeof(AzureShardedGrainStorage).FullName}.{name}"; this._logger = loggerFactory.CreateLogger(loggerName); - this._options = options; - this._serviceId = clusterOptions.Value.ServiceId; } public void Participate(ISiloLifecycle lifecycle) @@ -66,9 +64,9 @@ public async Task Init(CancellationToken ct) { _storageType = StorageType.TableStorage; - var shareClient = new TableServiceClient( - storage.BaseTableUri, - new AzureSasCredential(storage.SasToken)); + var shareClient = String.IsNullOrEmpty(storage.SasToken) ? + new TableServiceClient(storage.BaseTableUri, new DefaultAzureCredential()) : + new TableServiceClient(storage.BaseTableUri, new AzureSasCredential(storage.SasToken)); var table = await shareClient.CreateTableIfNotExistsAsync(storage.TableOrContainerName); @@ -83,7 +81,9 @@ public async Task Init(CancellationToken ct) { _storageType = StorageType.BlobStorage; - BlobServiceClient blobServiceClient = new BlobServiceClient(storage.BaseBlobUri, storage.SasCredential); + BlobServiceClient blobServiceClient = (null == storage.SasCredential) ? + new BlobServiceClient(storage.BaseBlobUri, new DefaultAzureCredential()) : + new BlobServiceClient(storage.BaseBlobUri, storage.SasCredential); var containerClient = blobServiceClient.GetBlobContainerClient(storage.TableOrContainerName); await containerClient.CreateIfNotExistsAsync(); @@ -401,74 +401,6 @@ public Task Close(CancellationToken ct) return Task.CompletedTask; } - - - - - #region "Utils" - - private int GetShardNumberFromKey(string pk) - { - var hash = GetStableHashCode(pk); - var storageNum = Math.Abs(hash % this._options.ConnectionStrings.Count()); - - return storageNum; - } - - /// - /// Take from https://stackoverflow.com/a/36845864/852806 - /// - /// - /// - public int GetStableHashCode(string str) - { - unchecked - { - int hash1 = 5381; - int hash2 = hash1; - - for (int i = 0; i < str.Length && str[i] != '\0'; i += 2) - { - hash1 = ((hash1 << 5) + hash1) ^ str[i]; - if (i == str.Length - 1 || str[i + 1] == '\0') - break; - hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; - } - - return hash1 + (hash2 * 1566083941); - } - } - - - private const string KeyStringSeparator = "__"; - - private string GetKeyString(GrainId grainId) - { - var key = $"{this._serviceId}{KeyStringSeparator}{grainId.ToString()}"; - - return SanitizeTableProperty(key); - } - - public string SanitizeTableProperty(string key) - { - // Remove any characters that can't be used in Azure PartitionKey or RowKey values - // http://www.jamestharpe.com/web-development/azure-table-service-character-combinations-disallowed-in-partitionkey-rowkey/ - key = key - .Replace('/', '_') // Forward slash - .Replace('\\', '_') // Backslash - .Replace('#', '_') // Pound sign - .Replace('?', '_'); // Question mark - - if (key.Length >= 1024) - throw new ArgumentException(string.Format("Key length {0} is too long to be an Azure table key. Key={1}", key.Length, key)); - - return key; - } - - - - - #endregion } public static class AzureShardedGrainStorageFactory diff --git a/OrleansShardedStorageProvider/OrleansShardedStorageProvider.csproj b/OrleansShardedStorageProvider/OrleansShardedStorageProvider.csproj index a207c2f..05b6e89 100644 --- a/OrleansShardedStorageProvider/OrleansShardedStorageProvider.csproj +++ b/OrleansShardedStorageProvider/OrleansShardedStorageProvider.csproj @@ -8,6 +8,7 @@ +