Skip to content

Commit cda2860

Browse files
21 more cosmos db partitions by default (#24)
* Update solution to build on cmd line and VS2017, and hopefully in build pipeline. * Fix versions * Verify signing on .nupkg files only. * Use Cosmos partitions by default, keyed by full sessionId. * Partitioning off by default. Opt-in to mega-partitioning. * Update Cosmos tests for wildcard partitioning. * Prevent using 'id' for partition key. Update Readme. * Fix versioning. * More rebase cleanup. :/
1 parent 1b31b0c commit cda2860

File tree

8 files changed

+164
-14
lines changed

8 files changed

+164
-14
lines changed

Microsoft.Aspnet.SessionState.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
2121
ProjectSection(SolutionItems) = preProject
2222
tools\CosmosDBSessionStateProviderAsync.settings.targets = tools\CosmosDBSessionStateProviderAsync.settings.targets
2323
tools\MicrosoftAspNetSessionState.settings.targets = tools\MicrosoftAspNetSessionState.settings.targets
24+
Readme.md = Readme.md
2425
tools\SessionStateModule.settings.targets = tools\SessionStateModule.settings.targets
2526
tools\SqlSessionStateProviderAsync.settings.targets = tools\SqlSessionStateProviderAsync.settings.targets
2627
tools\version.targets = tools\version.targets

MicrosoftAspNetSessionState.msbuild

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</ItemGroup>
1616

1717
<Target Name="Build" DependsOnTargets="BuildAssemblies;UnitTest;BuildPackages" />
18-
<Target Name="BuildAll" DependsOnTargets="BuildAssemblies;BuildSamples;UnitTest;BuildPackages" />
18+
<Target Name="BuildAll" DependsOnTargets="BuildAssemblies;BuildSamples;BuildPackages;UnitTest" />
1919
<Target Name="Clean" DependsOnTargets="CleanPackages;CleanSamples;CleanTests;CleanAssemblies" />
2020
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
2121

Readme.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,16 @@ Information on contributing to this repo is in the [Contributing Guide](CONTRIBU
5050
<providers>
5151
<add name="CosmosDBSessionStateProviderAsync" cosmosDBEndPointSettingKey="cosmosDBEndPointSetting" cosmosDBAuthKeySettingKey="cosmosDBAuthKeySetting"
5252
databaseId="[DataBaseId]" collectionId="[CollectionId]" offerThroughput="5000" connectionMode="Direct" connectionProtocol="Tcp" requestTimeout="5"
53-
maxConnectionLimit="50" maxRetryAttemptsOnThrottledRequests="10" maxRetryWaitTimeInSeconds="10" preferredLocations="" partitionKey=""
54-
partitionNumUsedByProvider=""
53+
maxConnectionLimit="50" maxRetryAttemptsOnThrottledRequests="10" maxRetryWaitTimeInSeconds="10" preferredLocations="" partitionKey="pKey"
54+
partitionNumUsedByProvider="*"
5555
type="Microsoft.AspNet.SessionState.CosmosDBSessionStateProviderAsync, Microsoft.AspNet.SessionState.CosmosDBSessionStateProviderAsync, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
5656
</providers>
5757
</sessionState>
5858
```
59+
60+
NOTE: For the best scalability, it is recommended to configure your CosmosDB provider with "wildcard" partitioning. For update-compatibility purposes, this is not the default. Please read about
61+
*partitionKeyPath* and *partitionNumUsedByProvider* below.
62+
5963
1. *cosmosDBEndPointSettingKey* - The appsetting key name which points to a CosmosDB end point
6064

6165
2. *cosmosDBAuthKeySettingKey* - The appsetting key name which points to a CosmosDB auth key
@@ -76,6 +80,6 @@ Information on contributing to this repo is in the [Contributing Guide](CONTRIBU
7680

7781
10. *preferredLocations* - Sets the preferred locations(regions) for geo-replicated database accounts in the Azure DocumentDB database service. Use ';' to split multiple locations. e.g. "East US;South Central US;North Europe"
7882

79-
11. *partitionKey* - The partition key name of the collection
83+
11. *partitionKeyPath* - The name of the key to use for logically partitioning the collection. This key name should be different from 'id' unless "wildcard" partitioning is being used.
8084

81-
12. *partitionNumUsedByProvider* - the number of partition can be used for sessionstate
85+
12. *partitionNumUsedByProvider* - The number of partition can be used for sessionstate. This was designed with the thought that multiple Cosmos partitions would be an extra cost. CosmosDB as it stands today encourages as many diverse logical partitions as you can imagine, as more partitions allow for better horizontal scaling. Setting this to an integer value will effectively reduce the partition count to 32 or less, even if the specified value is much greater. This is a result of how session ID's were translated to partition ID's by this provider. ***It is now recommended to specify "\*" for this option if possible.*** This will reuse the full session ID for partitioning, allowing Cosmos maximum ability for horizontal scaling.

src/CosmosDBSessionStateProviderAsync/CosmosDBSessionStateProviderAsync.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ public class CosmosDBSessionStateProviderAsync : SessionStateStoreProviderAsyncB
3939
private static int s_timeout;
4040
private static Uri s_documentCollectionUri;
4141
private static IDocumentClient s_client;
42+
private static IndexingPolicy s_indexNone = new IndexingPolicy()
43+
{
44+
IndexingMode = IndexingMode.Consistent,
45+
ExcludedPaths = new System.Collections.ObjectModel.Collection<ExcludedPath>()
46+
{
47+
new ExcludedPath() { Path = "/*" }
48+
},
49+
IncludedPaths = new System.Collections.ObjectModel.Collection<IncludedPath>()
50+
};
4251

4352
#region CosmosDB Stored Procedures
4453
private static readonly string CreateSessionStateItemSPID = "CreateSessionStateItem";
@@ -862,10 +871,21 @@ internal static void ParseCosmosDbEndPointSettings(NameValueCollection providerC
862871

863872
if (PartitionEnabled)
864873
{
865-
if(!int.TryParse(providerConfig["partitionNumUsedByProvider"], out s_partitionNumUsedBySessionProvider) || s_partitionNumUsedBySessionProvider < 1)
874+
if (providerConfig["partitionNumUsedByProvider"] == "*")
875+
{
876+
s_partitionNumUsedBySessionProvider = -1;
877+
}
878+
else if (!int.TryParse(providerConfig["partitionNumUsedByProvider"], out s_partitionNumUsedBySessionProvider) || s_partitionNumUsedBySessionProvider < 1)
866879
{
867880
s_partitionNumUsedBySessionProvider = 10;
868881
}
882+
883+
// If the value of 'id' and the partition key are the same, it is ok to combine them. Since we're not doing wildcard partitioning
884+
// the values will not be the same. So we should throw if trying to use 'id' as the partition key. It will cause problems.
885+
if (s_partitionNumUsedBySessionProvider >= 0 && s_partitionKey == "id")
886+
{
887+
throw new ConfigurationErrorsException(string.Format(SR.Cant_use_id_for_partition_key, "s_partitionKey"));
888+
}
869889
}
870890
}
871891

@@ -954,6 +974,11 @@ private static RequestOptions CreateRequestOptions(string sessionId)
954974

955975
private static string CreatePartitionValue(string sessionId)
956976
{
977+
// If s_partitionNumUsedBySessionProvider is less than 0, that means the wildcard option was specified.
978+
// In which case, we are not limiting partition count. Just use the entire sessionId.
979+
if (s_partitionNumUsedBySessionProvider < 0)
980+
return sessionId;
981+
957982
Debug.Assert(!string.IsNullOrEmpty(sessionId));
958983
Debug.Assert(PartitionEnabled);
959984
Debug.Assert(s_partitionNumUsedBySessionProvider != 0);
@@ -997,7 +1022,24 @@ private static async Task CreateCollectionIfNotExistsAsync()
9971022
{
9981023
try
9991024
{
1000-
await s_client.ReadDocumentCollectionAsync(DocumentCollectionUri);
1025+
DocumentCollection dc = await s_client.ReadDocumentCollectionAsync(DocumentCollectionUri);
1026+
1027+
// If we're using the updated partition strategy (see comment below) but we haven't updated our indexing policy - do that now.
1028+
if (PartitionEnabled && (s_partitionNumUsedBySessionProvider < 0))
1029+
{
1030+
// Contains doesn't work, presumably because a 'new ExcludePath' is a different object from the existing one, even if the path strings
1031+
// are exactly the same. So we do this the old-fashioned way.
1032+
bool containsWildcard = false;
1033+
foreach (var epath in dc.IndexingPolicy.ExcludedPaths)
1034+
if (epath.Path.Equals("/*", StringComparison.InvariantCultureIgnoreCase))
1035+
containsWildcard = true;
1036+
1037+
if (!containsWildcard)
1038+
{
1039+
dc.IndexingPolicy = s_indexNone;
1040+
await s_client.ReplaceDocumentCollectionAsync(DocumentCollectionUri, dc, new RequestOptions { OfferThroughput = s_offerThroughput });
1041+
}
1042+
}
10011043
}
10021044
catch (DocumentClientException e)
10031045
{
@@ -1008,9 +1050,15 @@ private static async Task CreateCollectionIfNotExistsAsync()
10081050
Id = s_collectionId,
10091051
DefaultTimeToLive = s_timeout
10101052
};
1053+
10111054
if(PartitionEnabled)
10121055
{
10131056
docCollection.PartitionKey.Paths.Add($"/{s_partitionKey}");
1057+
1058+
// If we're using the updated partition strategy - 1 logical partition per sessionId - then there is no
1059+
// need for indexing. It will not speed up lookup, and it only contributes to overhead on writes.
1060+
if (s_partitionNumUsedBySessionProvider < 0)
1061+
docCollection.IndexingPolicy = s_indexNone;
10141062
}
10151063

10161064
await s_client.CreateDocumentCollectionAsync(

src/CosmosDBSessionStateProviderAsync/Resources/SR.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/CosmosDBSessionStateProviderAsync/Resources/SR.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
<data name="ArgumentNull_WithParamName" xml:space="preserve">
121121
<value>Parameter '{0}' cannot be null.</value>
122122
</data>
123+
<data name="Cant_use_id_for_partition_key" xml:space="preserve">
124+
<value>The value '{0}' is invalid for 'partitionKeyPath' when not using wildcard partitioning.</value>
125+
</data>
123126
<data name="EmptyConfig_WithName" xml:space="preserve">
124127
<value>Configuration '{0}' cannot be null.</value>
125128
</data>

0 commit comments

Comments
 (0)