Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Internal] Subpartitioning: Adds SDK changes to support subpartitioning. #1658

Merged
merged 49 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e3c29f0
Test to check for document CRUD with MultiHash Kind.
SrinikhilReddy Jun 23, 2020
4096360
Test update.
SrinikhilReddy Jun 23, 2020
ae6c6f9
Document CRUD With MultiHash
SrinikhilReddy Jun 24, 2020
a74bc39
Merge branch 'master' into users/nanarava/multihash
SrinikhilReddy Jun 24, 2020
6df7605
Revisions based on code review.
SrinikhilReddy Jun 24, 2020
9e56030
Merge branch 'users/nanarava/multihash' of https://github.com/Srinikh…
SrinikhilReddy Jun 24, 2020
98be829
Resolving comments, adding a negative test.
SrinikhilReddy Jun 25, 2020
1c87f5c
Fixed suggested in code review.
SrinikhilReddy Jun 26, 2020
64af5a6
Tidying up the comments and Error messages.
SrinikhilReddy Jun 26, 2020
80c0d59
Implementing suggested changes in PR.
SrinikhilReddy Jul 2, 2020
726eb06
PartitionKey Null/None
SrinikhilReddy Jul 7, 2020
8b70871
Correction to undefined
SrinikhilReddy Jul 7, 2020
2dc6158
Changes suggested in reviews.
SrinikhilReddy Jul 10, 2020
a106adf
Tidying up comments.
SrinikhilReddy Jul 10, 2020
186518f
Correcting the test case. And implementing review suggestions.
SrinikhilReddy Jul 15, 2020
eaefee3
Merge branch 'master' into users/nanarava/multihash
SrinikhilReddy Jul 15, 2020
c81b047
Implementing suggestions from PR review.
SrinikhilReddy Jul 16, 2020
bb157a0
Merge branch 'users/nanarava/multihash' of https://github.com/Srinikh…
SrinikhilReddy Jul 16, 2020
5574bed
Misc cleanup
SrinikhilReddy Jul 16, 2020
3a679c9
Suggestions based on the internal review meeting.
SrinikhilReddy Jul 17, 2020
cd07652
Undo one change.
SrinikhilReddy Jul 17, 2020
fac9372
Moving changes to inside a internal directive
SrinikhilReddy Jul 20, 2020
3cd5172
Saving changes.
SrinikhilReddy Jul 22, 2020
ae54275
Merge branch 'master' of https://github.com/Azure/azure-cosmos-dotnet…
SrinikhilReddy Jul 22, 2020
6718e09
Merge branch 'master' of https://github.com/Azure/azure-cosmos-dotnet…
SrinikhilReddy Jul 22, 2020
69ef4a9
Merge branch 'master' of https://github.com/Azure/azure-cosmos-dotnet…
SrinikhilReddy Jul 23, 2020
07456d9
Changes based on review.
SrinikhilReddy Jul 24, 2020
4fa3f64
Change to IReadOnlyList.
SrinikhilReddy Jul 24, 2020
d1b0299
Resolving comments.
SrinikhilReddy Jul 24, 2020
abb76e8
Merge branch 'master' of https://github.com/Azure/azure-cosmos-dotnet…
SrinikhilReddy Jul 24, 2020
32d75a2
Cleanup
SrinikhilReddy Jul 24, 2020
53214a3
Code review changes.
SrinikhilReddy Jul 24, 2020
3dc5306
update to container properties constructor.
SrinikhilReddy Jul 28, 2020
f6ba9e9
Apply suggestions from code review
SrinikhilReddy Jul 29, 2020
a874ebb
Changes suggested in PR.
SrinikhilReddy Jul 29, 2020
772e35a
Better error message.
SrinikhilReddy Jul 29, 2020
58aa136
Cleaner code comments.
SrinikhilReddy Jul 29, 2020
9c22862
throw error for no value specified case.
SrinikhilReddy Jul 29, 2020
30da3e1
Clean code.
SrinikhilReddy Jul 29, 2020
1151d8f
Emulator startup comfig add for subpartitioning.
SrinikhilReddy Jul 29, 2020
fa03413
Merge branch 'master' into users/nanarava/multihash
j82w Jul 30, 2020
740b203
Update direct version to 3.11.4
SrinikhilReddy Jul 30, 2020
e1d2a85
Merge branch 'users/nanarava/multihash' of https://github.com/Srinikh…
SrinikhilReddy Jul 30, 2020
8c8248e
Fix for the pipeline errors.
SrinikhilReddy Jul 31, 2020
a674d0c
Merge branch 'master' of https://github.com/Azure/azure-cosmos-dotnet…
SrinikhilReddy Jul 31, 2020
cd2d448
Test fix.
SrinikhilReddy Jul 31, 2020
3bebf3c
Merge branch 'master' into users/nanarava/multihash
j82w Jul 31, 2020
407eea7
Override httpversion during test.
SrinikhilReddy Jul 31, 2020
76475ed
Merge branch 'users/nanarava/multihash' of https://github.com/Srinikh…
SrinikhilReddy Jul 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<ClientOfficialVersion>3.11.0</ClientOfficialVersion>
<ClientPreviewVersion>3.11.0</ClientPreviewVersion>
<DirectVersion>3.11.3</DirectVersion>
<DirectVersion>3.11.4</DirectVersion>
<EncryptionVersion>1.0.0-preview4</EncryptionVersion>
<HybridRowVersion>1.0.0-preview</HybridRowVersion>
<AboveDirBuildProps>$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))</AboveDirBuildProps>
Expand Down
10 changes: 10 additions & 0 deletions Microsoft.Azure.Cosmos/src/PartitionKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ internal PartitionKey(object value)
this.IsNone = false;
}

/// <summary>
/// Creates a new partition key value.
/// </summary>
/// <param name="partitionKeyInternal">The value to use as partition key.</param>
internal PartitionKey(PartitionKeyInternal partitionKeyInternal)
{
this.InternalKey = partitionKeyInternal;
this.IsNone = false;
}

/// <summary>
/// Creates a new partition key value.
/// </summary>
Expand Down
130 changes: 130 additions & 0 deletions Microsoft.Azure.Cosmos/src/PartitionKeyBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Routing;

/// <summary>
/// Represents a partition key value list in the Azure Cosmos DB service.
/// </summary>
#if INTERNAL || SUBPARTITIONING
public
#else
internal
#endif
sealed class PartitionKeyBuilder
{
private readonly List<object> partitionKeyValues;

/// <summary>
/// Creates a new partition key value list object.
/// </summary>
public PartitionKeyBuilder()
{
this.partitionKeyValues = new List<object>();
}

/// <summary>
/// Adds a partition key value of type string to the list.
/// </summary>
/// <param name="val">The value of type string to be used as partition key.</param>
/// <returns>An instance of <see cref="PartitionKeyBuilder"/> to use. </returns>
public PartitionKeyBuilder Add(string val)
{
this.partitionKeyValues.Add(val);
return this;
}

/// <summary>
/// Adds a partition key value of type double to the list.
/// </summary>
/// <param name="val">The value of type double to be used as partition key.</param>
/// <returns>An instance of <see cref="PartitionKeyBuilder"/> to use. </returns>
public PartitionKeyBuilder Add(double val)
{
this.partitionKeyValues.Add(val);
return this;
}

/// <summary>
/// Adds a partition key value of type bool to the list.
/// </summary>
/// <param name="val">The value of type bool to be used as partition key.</param>
/// <returns>An instance of <see cref="PartitionKeyBuilder"/> to use. </returns>
public PartitionKeyBuilder Add(bool val)
{
this.partitionKeyValues.Add(val);
return this;
}

/// <summary>
/// Adds a partition key value which is null
/// </summary>
/// <returns>An instance of <see cref="PartitionKeyBuilder"/> to use. </returns>
public PartitionKeyBuilder AddNullValue()
{
this.partitionKeyValues.Add(null);
return this;
}

/// <summary>
/// Adds a None partition key value.
/// </summary>
/// <returns>An instance of <see cref="PartitionKeyBuilder"/> to use. </returns>
public PartitionKeyBuilder AddNoneType()
{
this.partitionKeyValues.Add(PartitionKey.None);
return this;
}

SrinikhilReddy marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// Builds a new instance of the <see cref="PartitionKey"/> with the specified Partition Key values.
/// </summary>
/// <returns>An instance of <see cref="PartitionKey"/> </returns>
public PartitionKey Build()
{
// Why these checks?
// These changes are being added for SDK to support multiple paths in a partition key.
//
// Currently, when a resource does not specify a value for the PartitionKey,
// we assign a temporary value `PartitionKey.None` and later discern whether
// it is a PartitionKey.Undefined or PartitionKey.Empty based on the Collection Type.
// We retain this behaviour for single path partition keys.
//
// For collections with multiple path keys, absence of a partition key values is
// always treated as a PartitionKey.Undefined.
if (this.partitionKeyValues.Count == 0)
{
throw new ArgumentException($"No partition key value has been specifed");
}

if (this.partitionKeyValues.Count == 1 && PartitionKey.None.Equals(this.partitionKeyValues[0]))
{
return PartitionKey.None;
}

PartitionKeyInternal partitionKeyInternal;
object[] valueArray = new object[this.partitionKeyValues.Count];
for (int i = 0; i < this.partitionKeyValues.Count; i++)
{
object val = this.partitionKeyValues[i];
if (PartitionKey.None.Equals(val))
j82w marked this conversation as resolved.
Show resolved Hide resolved
{
valueArray[i] = Undefined.Value;
j82w marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
valueArray[i] = val;
}
}

partitionKeyInternal = new Documents.PartitionKey(valueArray).InternalKey;
return new PartitionKey(partitionKeyInternal);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,12 @@ private async Task<ResponseMessage> ExtractPartitionKeyAndProcessItemStreamAsync
// User specified PK value, no need to extract it
if (partitionKey.HasValue)
{
PartitionKeyDefinition pKeyDefinition = await this.GetPartitionKeyDefinitionAsync();
if (partitionKey.HasValue && partitionKey.Value != PartitionKey.None && partitionKey.Value.InternalKey.Components.Count != pKeyDefinition.Paths.Count)
{
throw new ArgumentException(RMResources.MissingPartitionKeyValue);
}
SrinikhilReddy marked this conversation as resolved.
Show resolved Hide resolved

return await this.ProcessItemStreamAsync(
partitionKey,
itemId,
Expand Down Expand Up @@ -709,7 +715,7 @@ private async Task<ResponseMessage> ProcessItemStreamAsync(

return responseMessage;
}

public override async Task<PartitionKey> GetPartitionKeyValueFromStreamAsync(
Stream stream,
CancellationToken cancellation = default(CancellationToken))
Expand All @@ -734,21 +740,23 @@ public override async Task<PartitionKey> GetPartitionKeyValueFromStreamAsync(
IJsonNavigatorNode jsonNavigatorNode = jsonNavigator.GetRootNode();
CosmosObject pathTraversal = CosmosObject.Create(jsonNavigator, jsonNavigatorNode);

string[] tokens = await this.GetPartitionKeyPathTokensAsync(cancellation);
for (int i = 0; i < tokens.Length - 1; i++)
IReadOnlyList<IReadOnlyList<string>> tokenslist = await this.GetPartitionKeyPathTokensAsync(cancellation);
List<CosmosElement> cosmosElementList = new List<CosmosElement>(tokenslist.Count);

foreach (IReadOnlyList<string> tokenList in tokenslist)
{
if (!pathTraversal.TryGetValue(tokens[i], out pathTraversal))
CosmosElement element;
if (ContainerCore.TryParseTokenListForElement(pathTraversal, tokenList, out element))
{
return PartitionKey.None;
cosmosElementList.Add(element);
}
else
{
cosmosElementList.Add(null);
}
}

if (!pathTraversal.TryGetValue(tokens[tokens.Length - 1], out CosmosElement partitionKeyValue))
{
return PartitionKey.None;
}

return this.CosmosElementToPartitionKeyObject(partitionKeyValue);
return ContainerCore.CosmosElementToPartitionKeyObject(cosmosElementList);
}
finally
{
Expand All @@ -757,18 +765,54 @@ public override async Task<PartitionKey> GetPartitionKeyValueFromStreamAsync(
}
}

private PartitionKey CosmosElementToPartitionKeyObject(CosmosElement cosmosElement) => cosmosElement switch
private static bool TryParseTokenListForElement(CosmosObject pathTraversal, IReadOnlyList<string> tokens, out CosmosElement result)
{
result = null;
for (int i = 0; i < tokens.Count - 1; i++)
{
if (!pathTraversal.TryGetValue(tokens[i], out pathTraversal))
{
return false;
}
}

if (!pathTraversal.TryGetValue(tokens[tokens.Count - 1], out result))
{
return false;
}

return true;
}

private static PartitionKey CosmosElementToPartitionKeyObject(IReadOnlyList<CosmosElement> cosmosElementList)
{
CosmosString cosmosString => new PartitionKey(cosmosString.Value),
CosmosNumber cosmosNumber => new PartitionKey(Number64.ToDouble(cosmosNumber.Value)),
CosmosBoolean cosmosBoolean => new PartitionKey(cosmosBoolean.Value),
CosmosNull _ => PartitionKey.Null,
_ => throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
RMResources.UnsupportedPartitionKeyComponentValue,
cosmosElement)),
};
PartitionKeyBuilder partitionKeyBuilder = new PartitionKeyBuilder();

foreach (CosmosElement cosmosElement in cosmosElementList)
{
if (cosmosElement == null)
{
partitionKeyBuilder.AddNoneType();
}
else
{
_ = cosmosElement switch
{
CosmosString cosmosString => partitionKeyBuilder.Add(cosmosString.Value),
CosmosNumber cosmosNumber => partitionKeyBuilder.Add(Number64.ToDouble(cosmosNumber.Value)),
CosmosBoolean cosmosBoolean => partitionKeyBuilder.Add(cosmosBoolean.Value),
CosmosNull _ => partitionKeyBuilder.AddNullValue(),
SrinikhilReddy marked this conversation as resolved.
Show resolved Hide resolved
_ => throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
RMResources.UnsupportedPartitionKeyComponentValue,
cosmosElement)),
};
}
}

return partitionKeyBuilder.Build();
}

private string GetResourceUri(RequestOptions requestOptions, OperationType operationType, string itemId)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public override async Task<string> GetRIDAsync(CancellationToken cancellationTok
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>Returns the partition key path</returns>
public override async Task<string[]> GetPartitionKeyPathTokensAsync(CancellationToken cancellationToken = default(CancellationToken))
public override async Task<IReadOnlyList<IReadOnlyList<string>>> GetPartitionKeyPathTokensAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ContainerProperties containerProperties = await this.GetCachedContainerPropertiesAsync(cancellationToken);
if (containerProperties == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public abstract Task<ThroughputResponse> ReplaceThroughputIfExistsAsync(
public abstract Task<ContainerProperties> GetCachedContainerPropertiesAsync(
CancellationToken cancellationToken = default(CancellationToken));

public abstract Task<string[]> GetPartitionKeyPathTokensAsync(CancellationToken cancellationToken = default(CancellationToken));
public abstract Task<IReadOnlyList<IReadOnlyList<string>>> GetPartitionKeyPathTokensAsync(
CancellationToken cancellationToken = default(CancellationToken));

public abstract Task<Documents.Routing.PartitionKeyInternal> GetNonePartitionKeyValueAsync(
CancellationToken cancellationToken);
Expand Down
Loading