diff --git a/src/Orleans.Clustering.CosmosDB/CosmosDBMembershipTable.cs b/src/Orleans.Clustering.CosmosDB/CosmosDBMembershipTable.cs index 1b1b320..a5b9e0f 100755 --- a/src/Orleans.Clustering.CosmosDB/CosmosDBMembershipTable.cs +++ b/src/Orleans.Clustering.CosmosDB/CosmosDBMembershipTable.cs @@ -409,8 +409,18 @@ private async Task TryCreateCosmosDBResources() //} containerProperties.IndexingPolicy.IndexingMode = IndexingMode.Consistent; - await dbResponse.CreateContainerIfNotExistsAsync( - containerProperties, this._options.CollectionThroughput); + if (this._options.UseDedicatedThroughput) + { + ThroughputProperties throughputProperties = this._options.UseAutoscaleThroughput + ? ThroughputProperties.CreateAutoscaleThroughput(this._options.AutoscaleThroughputMax) + : ThroughputProperties.CreateManualThroughput(this._options.CollectionThroughput); + + await dbResponse.CreateContainerIfNotExistsAsync(containerProperties, throughputProperties); + } + else + { + await dbResponse.CreateContainerIfNotExistsAsync(containerProperties); + } } public async Task CleanupDefunctSiloEntries(DateTimeOffset beforeDate) diff --git a/src/Orleans.Clustering.CosmosDB/Options/CosmosDBClusteringOptions.cs b/src/Orleans.Clustering.CosmosDB/Options/CosmosDBClusteringOptions.cs index 40725d4..0eae5ca 100644 --- a/src/Orleans.Clustering.CosmosDB/Options/CosmosDBClusteringOptions.cs +++ b/src/Orleans.Clustering.CosmosDB/Options/CosmosDBClusteringOptions.cs @@ -8,7 +8,10 @@ public class CosmosDBClusteringOptions { private const string ORLEANS_DB = "Orleans"; private const string ORLEANS_CLUSTER_COLLECTION = "OrleansCluster"; + private const bool ORLEANS_CLUSTER_DEDICATED_THROUGHPUT_ENABLED = true; private const int ORLEANS_CLUSTER_COLLECTION_THROUGHPUT = 400; + private const bool ORLEANS_CLUSTER_AUTOSCALE_THROUGHPUT_ENABLED = false; + private const int ORLEANS_CLUSTER_AUTOSCALE_THROUGHPUT_MAX = 4000; public CosmosClient Client { get; set; } public string AccountEndpoint { get; set; } @@ -17,7 +20,11 @@ public class CosmosDBClusteringOptions public bool CanCreateResources { get; set; } public string DB { get; set; } = ORLEANS_DB; public string Collection { get; set; } = ORLEANS_CLUSTER_COLLECTION; + public bool UseDedicatedThroughput { get; set; } = ORLEANS_CLUSTER_DEDICATED_THROUGHPUT_ENABLED; public int CollectionThroughput { get; set; } = ORLEANS_CLUSTER_COLLECTION_THROUGHPUT; + public bool UseAutoscaleThroughput { get; set; } = ORLEANS_CLUSTER_AUTOSCALE_THROUGHPUT_ENABLED; + public int AutoscaleThroughputMax { get; set; } = ORLEANS_CLUSTER_AUTOSCALE_THROUGHPUT_MAX; + [JsonConverter(typeof(StringEnumConverter))] public ConnectionMode ConnectionMode { get; set; } = ConnectionMode.Direct; diff --git a/src/Orleans.Persistence.CosmosDB/CosmosDBGrainStorage.cs b/src/Orleans.Persistence.CosmosDB/CosmosDBGrainStorage.cs index d4074a8..f9a3fa1 100755 --- a/src/Orleans.Persistence.CosmosDB/CosmosDBGrainStorage.cs +++ b/src/Orleans.Persistence.CosmosDB/CosmosDBGrainStorage.cs @@ -405,12 +405,21 @@ private static bool IsNumericType(Type o) private async Task TryCreateCosmosDBResources() { - var offerThroughput = - this._options.DatabaseThroughput >= 400 - ? (int?)this._options.DatabaseThroughput - : null; + DatabaseResponse dbResponse; + + if (this._options.DatabaseUseSharedThroughput) + { + var throughputProperties = this._options.DatabaseUseAutoscaleThroughput + ? ThroughputProperties.CreateAutoscaleThroughput(this._options.DatabaseAutoscaleThroughputMax) + : ThroughputProperties.CreateManualThroughput(this._options.DatabaseThroughput); + + dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB, throughputProperties); + } + else + { + dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB); + } - var dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB, offerThroughput); var db = dbResponse.Database; var stateCollection = new ContainerProperties(this._options.Collection, DEFAULT_PARTITION_KEY_PATH); @@ -430,8 +439,20 @@ private async Task TryCreateCosmosDBResources() const int maxRetries = 3; for (var retry = 0; retry <= maxRetries; ++retry) { - var collResponse = await db.CreateContainerIfNotExistsAsync( - stateCollection, offerThroughput); + ContainerResponse collResponse; + + if (this._options.CollectionUseDedicatedThroughput) + { + var throughputProperties = this._options.CollectionUseAutoscaleThroughput + ? ThroughputProperties.CreateAutoscaleThroughput(this._options.CollectionAutoscaleThroughputMax) + : ThroughputProperties.CreateManualThroughput(this._options.CollectionThroughput); + + collResponse = await db.CreateContainerIfNotExistsAsync(stateCollection, throughputProperties); + } + else + { + collResponse = await db.CreateContainerIfNotExistsAsync(stateCollection); + } if (collResponse.StatusCode == HttpStatusCode.OK || collResponse.StatusCode == HttpStatusCode.Created) { diff --git a/src/Orleans.Persistence.CosmosDB/Options/CosmosDBStorageOptions.cs b/src/Orleans.Persistence.CosmosDB/Options/CosmosDBStorageOptions.cs index 10f6492..c8a24b9 100755 --- a/src/Orleans.Persistence.CosmosDB/Options/CosmosDBStorageOptions.cs +++ b/src/Orleans.Persistence.CosmosDB/Options/CosmosDBStorageOptions.cs @@ -10,7 +10,11 @@ public class CosmosDBStorageOptions { private const string ORLEANS_DB = "Orleans"; internal const string ORLEANS_STORAGE_COLLECTION = "OrleansStorage"; + private const bool ORLEANS_STORAGE_DEDICATED_THROUGHPUT_ENABLED = true; + private const bool ORLEANS_STORAGE_SHARED_THROUGHPUT_ENABLED = true; private const int ORLEANS_STORAGE_COLLECTION_THROUGHPUT = 400; + private const bool ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED = false; + private const int ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX = 4000; public CosmosClient Client { get; set; } @@ -23,11 +27,19 @@ public class CosmosDBStorageOptions /// Database configured throughput, if set to 0 it will not be configured and collection throughput must be set. See https://docs.microsoft.com/en-us/azure/cosmos-db/set-throughput /// public int DatabaseThroughput { get; set; } = ORLEANS_STORAGE_COLLECTION_THROUGHPUT; + public bool DatabaseUseSharedThroughput { get; set; } = ORLEANS_STORAGE_SHARED_THROUGHPUT_ENABLED; + public bool DatabaseUseAutoscaleThroughput { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED; + public int DatabaseAutoscaleThroughputMax { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX; + public string Collection { get; set; } = ORLEANS_STORAGE_COLLECTION; /// /// RU units for collection, can be set to 0 if throughput is specified on database level. See https://docs.microsoft.com/en-us/azure/cosmos-db/set-throughput /// public int CollectionThroughput { get; set; } = ORLEANS_STORAGE_COLLECTION_THROUGHPUT; + public bool CollectionUseDedicatedThroughput { get; set; } = ORLEANS_STORAGE_DEDICATED_THROUGHPUT_ENABLED; + public bool CollectionUseAutoscaleThroughput { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED; + public int CollectionAutoscaleThroughputMax { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX; + public bool CanCreateResources { get; set; } public bool DeleteStateOnClear { get; set; } @@ -95,9 +107,29 @@ public void ValidateConfiguration() throw new OrleansConfigurationException( $"Configuration for CosmosDBStorage {this.name} is invalid. {nameof(this.options.Collection)} is not valid."); - if (this.options.CollectionThroughput < 400 && this.options.DatabaseThroughput < 400) + if (!this.options.CollectionUseDedicatedThroughput && !this.options.DatabaseUseSharedThroughput) + throw new OrleansConfigurationException( + $"Configuration for CosmosDBStorage {this.name} is invalid. Either {nameof(this.options.CollectionUseDedicatedThroughput)} and/or {nameof(this.options.DatabaseUseSharedThroughput)} must be true"); + + if (this.options.CollectionUseAutoscaleThroughput && !this.options.CollectionUseDedicatedThroughput) + throw new OrleansConfigurationException( + $"Configuration for CosmosDBStorage {this.name} is invalid. If {nameof(this.options.CollectionUseAutoscaleThroughput)} is true, {nameof(this.options.CollectionUseDedicatedThroughput)} must also be true."); + + if (this.options.CollectionUseAutoscaleThroughput && this.options.CollectionAutoscaleThroughputMax < 4000) + throw new OrleansConfigurationException( + $"Configuration for CosmosDBStorage {this.name} is invalid. {nameof(this.options.CollectionAutoscaleThroughputMax)} must be 4000 or greater."); + + if (this.options.DatabaseUseAutoscaleThroughput && this.options.DatabaseAutoscaleThroughputMax < 4000) + throw new OrleansConfigurationException( + $"Configuration for CosmosDBStorage {this.name} is invalid. {nameof(this.options.DatabaseAutoscaleThroughputMax)} must be 4000 or greater."); + + if (!this.options.CollectionUseAutoscaleThroughput && this.options.CollectionThroughput < 400) + throw new OrleansConfigurationException( + $"Configuration for CosmosDBStorage {this.name} is invalid. {nameof(this.options.CollectionThroughput)} must be 400 or greater."); + + if (!this.options.DatabaseUseAutoscaleThroughput && this.options.DatabaseThroughput < 400) throw new OrleansConfigurationException( - $"Configuration for CosmosDBStorage {this.name} is invalid. Either {nameof(this.options.DatabaseThroughput)} or {nameof(this.options.CollectionThroughput)} must exceed 400."); + $"Configuration for CosmosDBStorage {this.name} is invalid. {nameof(this.options.DatabaseThroughput)} must be 400 or greater."); } } } diff --git a/src/Orleans.Reminders.CosmosDB/CosmosDBReminderTable.cs b/src/Orleans.Reminders.CosmosDB/CosmosDBReminderTable.cs index b283913..d05e66d 100755 --- a/src/Orleans.Reminders.CosmosDB/CosmosDBReminderTable.cs +++ b/src/Orleans.Reminders.CosmosDB/CosmosDBReminderTable.cs @@ -375,12 +375,21 @@ private async Task TryDeleteDatabase() private async Task TryCreateCosmosDBResources() { - var offerThroughput = - this._options.CollectionThroughput >= 400 - ? (int?)this._options.CollectionThroughput - : null; + DatabaseResponse dbResponse; + + if (this._options.DatabaseUseSharedThroughput) + { + var throughputProperties = this._options.DatabaseUseAutoscaleThroughput + ? ThroughputProperties.CreateAutoscaleThroughput(this._options.DatabaseAutoscaleThroughputMax) + : ThroughputProperties.CreateManualThroughput(this._options.DatabaseThroughput); + + dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB, throughputProperties); + } + else + { + dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB); + } - var dbResponse = await this._cosmos.CreateDatabaseIfNotExistsAsync(this._options.DB, offerThroughput); var db = dbResponse.Database; var remindersCollection = new ContainerProperties(this._options.Collection, PARTITION_KEY_PATH); @@ -394,8 +403,20 @@ private async Task TryCreateCosmosDBResources() const int maxRetries = 3; for (var retry = 0; retry <= maxRetries; ++retry) { - var collResponse = await db.CreateContainerIfNotExistsAsync( - remindersCollection, offerThroughput); + ContainerResponse collResponse; + + if (this._options.CollectionUseDedicatedThroughput) + { + var throughputProperties = this._options.CollectionUseAutoscaleThroughput + ? ThroughputProperties.CreateAutoscaleThroughput(this._options.CollectionAutoscaleThroughputMax) + : ThroughputProperties.CreateManualThroughput(this._options.CollectionThroughput); + + collResponse = await db.CreateContainerIfNotExistsAsync(remindersCollection, throughputProperties); + } + else + { + collResponse = await db.CreateContainerIfNotExistsAsync(remindersCollection); + } if (retry == maxRetries || dbResponse.StatusCode != HttpStatusCode.Created || collResponse.StatusCode == HttpStatusCode.Created) { diff --git a/src/Orleans.Reminders.CosmosDB/Options/CosmosDBReminderStorageOptions.cs b/src/Orleans.Reminders.CosmosDB/Options/CosmosDBReminderStorageOptions.cs index f086482..675a6de 100755 --- a/src/Orleans.Reminders.CosmosDB/Options/CosmosDBReminderStorageOptions.cs +++ b/src/Orleans.Reminders.CosmosDB/Options/CosmosDBReminderStorageOptions.cs @@ -8,14 +8,25 @@ public class CosmosDBReminderStorageOptions { private const string ORLEANS_DB = "Orleans"; private const string ORLEANS_REMINDERS_COLLECTION = "OrleansReminders"; + private const bool ORLEANS_STORAGE_DEDICATED_THROUGHPUT_ENABLED = true; + private const bool ORLEANS_STORAGE_SHARED_THROUGHPUT_ENABLED = true; private const int ORLEANS_STORAGE_COLLECTION_THROUGHPUT = 400; + private const bool ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED = false; + private const int ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX = 4000; public CosmosClient Client { get; set; } public string AccountEndpoint { get; set; } [Redact] public string AccountKey { get; set; } public string DB { get; set; } = ORLEANS_DB; + public int DatabaseThroughput { get; set; } = ORLEANS_STORAGE_COLLECTION_THROUGHPUT; + public bool DatabaseUseSharedThroughput { get; set; } = ORLEANS_STORAGE_SHARED_THROUGHPUT_ENABLED; + public bool DatabaseUseAutoscaleThroughput { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED; + public int DatabaseAutoscaleThroughputMax { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX; public string Collection { get; set; } = ORLEANS_REMINDERS_COLLECTION; public int CollectionThroughput { get; set; } = ORLEANS_STORAGE_COLLECTION_THROUGHPUT; + public bool CollectionUseDedicatedThroughput { get; set; } = ORLEANS_STORAGE_DEDICATED_THROUGHPUT_ENABLED; + public bool CollectionUseAutoscaleThroughput { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_ENABLED; + public int CollectionAutoscaleThroughputMax { get; set; } = ORLEANS_STORAGE_AUTOSCALE_THROUGHPUT_MAX; public bool CanCreateResources { get; set; } [JsonConverter(typeof(StringEnumConverter))]