Skip to content
This repository has been archived by the owner on Jul 12, 2020. It is now read-only.

Commit

Permalink
Removed the auto scaling logic. It was a stupid idea.
Browse files Browse the repository at this point in the history
  • Loading branch information
Elfocrash committed Dec 15, 2018
1 parent 92fdfc1 commit 45f7992
Show file tree
Hide file tree
Showing 8 changed files with 22 additions and 237 deletions.
6 changes: 6 additions & 0 deletions docs/The-CosmosStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ The CosmosStore's boundaries can be one of two.

The choice to go with one or the other is completely up to you and it comes down to partitioning strategy, cost and flexibility when it comes to scaleability.

### A single mandatory property

In order for an entity to be able to be stored, manipulated and retrieved in a CosmosStore it is required to have a mandatory property. This is the `id` property.

It needs to be a `string` property with name Id (or any capitalisation you want). Even though not neccessary, you should also decorate it with the `[JsonProperty("id")]` attribute. This is neccessary if you want to do any querying based on the id (querying NOT reading). It will also help with unintented behavour when it comes to object mapping and LINQ to SQL transformations.

### CosmosStore's lifetime

CosmosStores should be registered as *singletons* in your system. This will achieve optimal performance. If you are using a dependency injection framework make sure they are registered as singletons and if you don't, just make sure you don't dispose them and you keep reusing them.
Expand Down
2 changes: 1 addition & 1 deletion src/Cosmonaut/Cosmonaut.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<RepositoryUrl>https://github.com/Elfocrash/Cosmonaut</RepositoryUrl>
<PackageTags>azure entitystore entity db orm microsoft cosmos cosmosdb documentdb docdb nosql azureofficial dotnetcore netcore netstandard</PackageTags>
<PackageReleaseNotes>Please report any issues on Github.</PackageReleaseNotes>
<Version>2.7.1</Version>
<Version>2.8.0</Version>
<NeutralLanguage></NeutralLanguage>
<Company>Nick Chapsas</Company>
<PackageIconUrl>https://raw.githubusercontent.com/Elfocrash/Cosmonaut/develop/logo.png</PackageIconUrl>
Expand Down
43 changes: 11 additions & 32 deletions src/Cosmonaut/CosmosStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading;
using System.Threading.Tasks;
using Cosmonaut.Extensions;
using Cosmonaut.Operations;
using Cosmonaut.Response;
using Cosmonaut.Storage;
using Microsoft.Azure.Documents;
Expand All @@ -15,10 +14,6 @@ namespace Cosmonaut
{
public sealed class CosmosStore<TEntity> : ICosmosStore<TEntity> where TEntity : class
{
public int CollectionThrouput { get; internal set; } = CosmosConstants.MinimumCosmosThroughput;

public bool IsUpscaled { get; internal set; }

public bool IsShared { get; internal set; }

public string CollectionName { get; private set; }
Expand All @@ -31,7 +26,6 @@ public sealed class CosmosStore<TEntity> : ICosmosStore<TEntity> where TEntity :

private readonly IDatabaseCreator _databaseCreator;
private readonly ICollectionCreator _collectionCreator;
private readonly CosmosScaler<TEntity> _cosmosScaler;

public CosmosStore(CosmosStoreSettings settings) : this(settings, string.Empty)
{
Expand All @@ -46,7 +40,6 @@ public CosmosStore(CosmosStoreSettings settings, string overriddenCollectionName
if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName));
_collectionCreator = new CosmosCollectionCreator(CosmonautClient);
_databaseCreator = new CosmosDatabaseCreator(CosmonautClient);
_cosmosScaler = new CosmosScaler<TEntity>(this);
InitialiseCosmosStore(overriddenCollectionName);
}

Expand All @@ -71,19 +64,16 @@ public CosmosStore(CosmosStoreSettings settings, string overriddenCollectionName
string databaseName,
string overriddenCollectionName,
IDatabaseCreator databaseCreator = null,
ICollectionCreator collectionCreator = null,
bool scaleable = false)
ICollectionCreator collectionCreator = null)
{
DatabaseName = databaseName;
CosmonautClient = cosmonautClient ?? throw new ArgumentNullException(nameof(cosmonautClient));
Settings = new CosmosStoreSettings(databaseName, cosmonautClient.DocumentClient.ServiceEndpoint.ToString(), string.Empty, cosmonautClient.DocumentClient.ConnectionPolicy,
scaleCollectionRUsAutomatically: scaleable);
Settings = new CosmosStoreSettings(databaseName, cosmonautClient.DocumentClient.ServiceEndpoint.ToString(), string.Empty, cosmonautClient.DocumentClient.ConnectionPolicy);
if (Settings.InfiniteRetries)
CosmonautClient.DocumentClient.SetupInfiniteRetries();
if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName));
_collectionCreator = collectionCreator ?? new CosmosCollectionCreator(CosmonautClient);
_databaseCreator = databaseCreator ?? new CosmosDatabaseCreator(CosmonautClient);
_cosmosScaler = new CosmosScaler<TEntity>(this);
InitialiseCosmosStore(overriddenCollectionName);
}

Expand Down Expand Up @@ -214,12 +204,9 @@ private void InitialiseCosmosStore(string overridenCollectionName)
{
IsShared = typeof(TEntity).UsesSharedCollection();
CollectionName = GetCosmosStoreCollectionName(overridenCollectionName);

Settings.DefaultCollectionThroughput = CollectionThrouput = CosmonautClient.GetOfferV2ForCollectionAsync(DatabaseName, CollectionName).ConfigureAwait(false).GetAwaiter()
.GetResult()?.Content?.OfferThroughput ?? typeof(TEntity).GetCollectionThroughputForEntity(Settings.DefaultCollectionThroughput);


_databaseCreator.EnsureCreatedAsync(DatabaseName).ConfigureAwait(false).GetAwaiter().GetResult();
_collectionCreator.EnsureCreatedAsync<TEntity>(DatabaseName, CollectionName, CollectionThrouput, Settings.IndexingPolicy)
_collectionCreator.EnsureCreatedAsync<TEntity>(DatabaseName, CollectionName, Settings.DefaultCollectionThroughput, Settings.IndexingPolicy)
.ConfigureAwait(false).GetAwaiter().GetResult();
}

Expand All @@ -234,24 +221,16 @@ private string GetCosmosStoreCollectionName(string overridenCollectionName)
private async Task<CosmosMultipleResponse<TEntity>> ExecuteMultiOperationAsync(IEnumerable<TEntity> entities,
Func<TEntity, Task<CosmosResponse<TEntity>>> operationFunc)
{
var multipleResponse = new CosmosMultipleResponse<TEntity>();

var entitiesList = entities.ToList();
if (!entitiesList.Any())
return new CosmosMultipleResponse<TEntity>();

try
{
var multipleResponse = await _cosmosScaler.UpscaleCollectionIfConfiguredAsSuch(entitiesList, DatabaseName, CollectionName, operationFunc);
var results = (await entitiesList.Select(operationFunc).WhenAllTasksAsync()).ToList();
multipleResponse.SuccessfulEntities.AddRange(results.Where(x => x.IsSuccess));
multipleResponse.FailedEntities.AddRange(results.Where(x => !x.IsSuccess));
await _cosmosScaler.DownscaleCollectionRequestUnitsToDefault(DatabaseName, CollectionName);
return multipleResponse;
}
catch (Exception)
{
await _cosmosScaler.DownscaleCollectionRequestUnitsToDefault(DatabaseName, CollectionName);
throw;
}

var results = (await entitiesList.Select(operationFunc).WhenAllTasksAsync()).ToList();
multipleResponse.SuccessfulEntities.AddRange(results.Where(x => x.IsSuccess));
multipleResponse.FailedEntities.AddRange(results.Where(x => !x.IsSuccess));
return multipleResponse;
}

private RequestOptions GetRequestOptions(RequestOptions requestOptions, TEntity entity)
Expand Down
20 changes: 4 additions & 16 deletions src/Cosmonaut/CosmosStoreSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ public class CosmosStoreSettings

public int DefaultCollectionThroughput { get; set; } = CosmosConstants.MinimumCosmosThroughput;

public bool ScaleCollectionRUsAutomatically { get; set; }

public int MaximumUpscaleRequestUnits { get; set; } = CosmosConstants.DefaultMaximumUpscaleThroughput;

public JsonSerializerSettings JsonSerializerSettings { get; set; }

public bool InfiniteRetries { get; set; } = true;
Expand All @@ -48,24 +44,20 @@ public class CosmosStoreSettings
AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey));
settings?.Invoke(this);
}

public CosmosStoreSettings(
string databaseName,
string endpointUrl,
string authKey,
ConnectionPolicy connectionPolicy = null,
IndexingPolicy indexingPolicy = null,
int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput,
bool scaleCollectionRUsAutomatically = false,
int maximumUpscaleRequestUnits = CosmosConstants.DefaultMaximumUpscaleThroughput)
int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput)
: this(databaseName,
new Uri(endpointUrl),
authKey,
connectionPolicy,
indexingPolicy,
defaultCollectionThroughput,
scaleCollectionRUsAutomatically,
maximumUpscaleRequestUnits)
defaultCollectionThroughput)
{
}

Expand All @@ -75,17 +67,13 @@ public class CosmosStoreSettings
string authKey,
ConnectionPolicy connectionPolicy = null,
IndexingPolicy indexingPolicy = null,
int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput,
bool scaleCollectionRUsAutomatically = false,
int maximumUpscaleRequestUnits = CosmosConstants.DefaultMaximumUpscaleThroughput)
int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput)
{
DatabaseName = databaseName ?? throw new ArgumentNullException(nameof(databaseName));
EndpointUrl = endpointUrl ?? throw new ArgumentNullException(nameof(endpointUrl));
AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey));
ConnectionPolicy = connectionPolicy;
DefaultCollectionThroughput = defaultCollectionThroughput;
ScaleCollectionRUsAutomatically = scaleCollectionRUsAutomatically;
MaximumUpscaleRequestUnits = maximumUpscaleRequestUnits;
IndexingPolicy = indexingPolicy ?? CosmosConstants.DefaultIndexingPolicy;
}
}
Expand Down
7 changes: 0 additions & 7 deletions src/Cosmonaut/Extensions/CollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,5 @@ internal static bool UsesSharedCollection(this Type entityType)

return hasSharedCosmosCollectionAttribute;
}

internal static int GetCollectionThroughputForEntity(this Type entityType,
int collectionThroughput)
{
if (collectionThroughput < CosmosConstants.MinimumCosmosThroughput) throw new IllegalCosmosThroughputException();
return collectionThroughput;
}
}
}
87 changes: 0 additions & 87 deletions src/Cosmonaut/Operations/CosmosScaler.cs

This file was deleted.

90 changes: 0 additions & 90 deletions tests/Cosmonaut.System/CosmosStoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading.Tasks;
using Cosmonaut.Extensions;
using Cosmonaut.Extensions.Microsoft.DependencyInjection;
using Cosmonaut.Operations;
using Cosmonaut.Response;
using Cosmonaut.System.Models;
using FluentAssertions;
Expand Down Expand Up @@ -370,95 +369,6 @@ public async Task WhenValidEntitiesAreNotAdded_ThenTheyCanNotBeFoundAsync()
alpacaFound.Should().BeNull();
}

[Fact]
public async Task WhenCollectionIsUpScaled_AndAutomaticScalingIsTurnedOff_ThenOfferDoesNotChange()
{
var catStore = new CosmosStore<Cat>(new CosmosStoreSettings(_databaseId, _emulatorUri, _emulatorKey), _collectionName);
var cosmosScaler = new CosmosScaler<Cat>(catStore);

await cosmosScaler.UpscaleCollectionRequestUnitsForRequest(_databaseId, _collectionName, 100, 5);

var offer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

offer.Content.OfferThroughput.Should().Be(400);
}

[Fact]
public async Task WhenCollectionIsUpScaled_AndAutomaticScalingIsTurnedOn_ThenOfferIsUpscaled()
{
var catStore = new CosmosStore<Cat>(new CosmosStoreSettings(_databaseId, _emulatorUri, _emulatorKey,
settings =>
{
settings.DefaultCollectionThroughput = 500;
settings.ScaleCollectionRUsAutomatically = true;
}), _collectionName);
var cosmosScaler = new CosmosScaler<Cat>(catStore);

await cosmosScaler.UpscaleCollectionRequestUnitsForRequest(_databaseId, _collectionName, 100, 5);

var offer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

offer.Content.OfferThroughput.Should().Be(500);
}

[Fact]
public async Task WhenCollectionIsDownScaled_AndAutomaticScalingIsTurnedOff_ThenOfferDoesNotChange()
{
var catStore = new CosmosStore<Cat>(new CosmosStoreSettings(_databaseId, _emulatorUri, _emulatorKey,
settings =>
{
settings.DefaultCollectionThroughput = 500;
}), _collectionName);
var cosmosScaler = new CosmosScaler<Cat>(catStore);

var preScaleOffer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

await cosmosScaler.DownscaleCollectionRequestUnitsToDefault(_databaseId, _collectionName);

var postScaleOffer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

preScaleOffer.Content.OfferThroughput.Should().Be(500);
postScaleOffer.Content.OfferThroughput.Should().Be(500);
}

[Fact]
public async Task WhenCollectionIsDownScaled_AndAutomaticScalingIsTurnedOn_ThenOfferIsDownscaled()
{
var catStore = new CosmosStore<Cat>(new CosmosStoreSettings(_databaseId, _emulatorUri, _emulatorKey,
settings =>
{
settings.DefaultCollectionThroughput = 500;
settings.ScaleCollectionRUsAutomatically = true;
}), _collectionName);
var cosmosScaler = new CosmosScaler<Cat>(catStore);

await cosmosScaler.UpscaleCollectionRequestUnitsForRequest(_databaseId, _collectionName, 100, 6);
var preScaleOffer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

await cosmosScaler.DownscaleCollectionRequestUnitsToDefault(_databaseId, _collectionName);

var postScaleOffer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);

preScaleOffer.Content.OfferThroughput.Should().Be(600);
postScaleOffer.Content.OfferThroughput.Should().Be(500);
}

[Fact]
public async Task WhenRUIntenseOperationHappens_AndAutomaticScalingIsTurnedOn_ThenOfferUpscaledAndDownscaled()
{
var catStore = new CosmosStore<Cat>(new CosmosStoreSettings(_databaseId, _emulatorUri, _emulatorKey,
settings =>
{
settings.DefaultCollectionThroughput = 400;
settings.ScaleCollectionRUsAutomatically = true;
}), _collectionName);
await ExecuteMultipleAddOperationsForType<Cat>(list => catStore.AddRangeAsync(list), 400);


var postScaleOffer = await catStore.CosmonautClient.GetOfferV2ForCollectionAsync(_databaseId, _collectionName);
postScaleOffer.Content.OfferThroughput.Should().Be(400);
}

[Fact]
public async Task WhenPaginatedQueryExecutesWithSkipTake_ThenPaginatedResultsAreReturnedCorrectly()
{
Expand Down

2 comments on commit 45f7992

@Mortana89
Copy link

Choose a reason for hiding this comment

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

What exactly made you go down the route of removing your autoscaler? Any issues you encountered you'd like to share?

@Elfocrash
Copy link
Owner Author

Choose a reason for hiding this comment

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

@Mortana89

There is nothing wrong with it's implementation per se but it was only serving a very specific purpose. Scaling your CosmosDB momentarily for a heavy load. This only works for a single instance running against CosmosDB but if you are using CosmosDB you are probably using it because of it's georepl capabilities so I don't see the point in the feature. I would rather code something in the future that coordinated all the Cosmonaut instances running and scale based on the overall load instead of momentarily.

Please sign in to comment.