-
Notifications
You must be signed in to change notification settings - Fork 22
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
Extend LeaseCollection allowed partition key paths to "\partitionKey" to support gremlin accounts #158
Conversation
Fix the below issue |
/// Gets or sets property to be used as partition key path for lease collections created in GremlinAccounts | ||
/// </summary> | ||
[JsonProperty("gremlincompatid")] | ||
public string GremlinCompatId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the user has a property with this name? Wouldn't be better for the user to specify the pk they want to use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note the problem you are specifying, exists even today before this PR. Default implementation assumes it can be only "id"
This is to address the default scenario where end user is using default lease management from this library.
In this case, property names are controlled by this class and there would be no conflict.
In their custom lease management solution, it doesnt hit this path as I understand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand, what I'm saying is that this fails with the same assumption that currently happens with /id
. Giving the user the choice would be better suited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me give an example and you can tell me how it could work so that I can understand the proposal better
lets say I choose FooId as my partitionKey for the lease collection and created the collection. now when I pass it down to this library, what should the library do to the DocumentServiceLease class so that it can stamp the objects with FooId property.
Will look at your reply for above example, but I feel that what you are proposing would not be possible as long as lease implementation and its class definition is controlled by this library.
Unless you are talking reflection and dynamically adding properties. I am not sure whether we want that kind of complexity, only reason customers complain about id as current partition property is because it doesnt work on gremlin.
Ideally this library should create the lease collection since it controls definition of DocumentServiceLease and stores it in the lease collection. That way customer doesn't have to deal with identifying partitionkeyPath looking at the internal implementation of this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please provide better description: explain the problem and outline the design (and why this is going to work).
The description is still not clear. Can we have description that OSS user with minimum knowledge of CFP can reason about? Besides, scenario is not clear as well.
Also for CFP we should not special case gremlin. If we do something, it should be generic and extensible. |
src/DocumentDB.ChangeFeedProcessor/LeaseManagement/DocumentServiceLeaseStore.cs
Outdated
Show resolved
Hide resolved
/// Gets or sets property to be used as partition key path for lease collections created in GremlinAccounts | ||
/// </summary> | ||
[JsonProperty(GremlinCompatIdPropertyName)] | ||
public string GremlinCompatId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not do that by default. Majority of lease collections do not need this overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you suggest, what has to be done. I tried to interpret this comment below but not sure whether I got it right?
I can change gremlincompatId into LeaseId property name so that its not specific to gremlin and hopefully LeaseId doesnt impact any of other types of cosmosdb account. if you have a way where we can change Id into LeaseId thats ideal considering already collections are provisioned with partitionKey as "Id".
When you are saying its overhead, what's the concern. Is it processing cost, storage cost? clone of Id field is not costly right.
Are you saying we should create a derived class DocumentServiceLeaseV2 and add this property LeaseId there. That would be major refactoring to have parallel classes or if else within existing classes e.g DocumentServiceLeaseStoreManager.cs to conditionally create DocumentServiceLease vs DocumentServiceLeaseV2 based on user input. Overhead cost of additional string is cheaper to this entire effort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way I think about is using something like (existing) IRequestOptionsFactory, that would use something like Lease.PartitionKey or Lease.AlternatePartitionKey and all operations would append PK to request options and update lease doc for lease operations. That would only be used when CFP Builder explicitly opts-in for using alternate PK for lease collection. In fact, if we go this way, it should not be much more work than just supporting any property used as PK when lease collection was created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it something along this line, I will fix the names later
internal class PartitionedByAlternatePKCollectionRequestOptionsFactory : IRequestOptionsFactory
{
public RequestOptions CreateRequestOptions(ILease lease) => new RequestOptions { PartitionKey = new PartitionKey(lease.AlternatePK) };
public FeedOptions CreateFeedOptions() => new FeedOptions { EnableCrossPartitionQuery = true };
}
then instantiate that class in CFBuilder based on some flag.
wont this mean the following:
- ILease interface has to be updated with a property name "AlternatePK"
- if somebody implemented their custom lease management, they need to unnecessarily add this property. Is It Ok?
- the moment I touch ILease, i need to update DocumentServiceLease to have that property. so whether I use new RequestOptionsFactory, lease document will still contain the "AlternatePK". so what does it help in?
- if somebody created a lease collection with "FooId" as partitionkey. Still not clear, how I can make it work without touching ILease interface to have "FooId" as a property name. Or is this not a ask at all from you and Matias?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ILease doesn't need to be touched. DocumentServiceLease is only specific to CosmosDB service, e.g. redis or in memory lease implementation wouldn't have partition key concept. I think with these changes we can add properties to document service lease either by adding to the class itself or by calling SetProerty via NewtonsoftJson as you did in your ealier changes. I would prefer the latter approach with use controlling what PK they want to use (in the upper stack we would call API to get PK definition from the lease collection at startup).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you check this pr - https://github.com/Azure/azure-documentdb-changefeedprocessor-dotnet/pull/159/files
and see if that aligns with what you are proposing
this is rough change and ignore the test cases, I haven't fixed or authored new ones yet
/// This property name is compatible to both GremlinAccounts and SqlAccounts | ||
/// </summary> | ||
[JsonProperty(LeasePartitionKeyPropertyName)] | ||
public string LeasePartitionKey { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we name it as just PartitionKey. This class is already a lease.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
@@ -14,6 +14,9 @@ namespace Microsoft.Azure.Documents.ChangeFeedProcessor.LeaseManagement | |||
[Serializable] | |||
internal class DocumentServiceLease : ILease, ILeaseAcquireReasonProvider | |||
{ | |||
internal const string IdPropertyName = "id"; | |||
internal const string LeasePartitionKeyPropertyName = "leasepk"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's already a lease. Can we have this as "pk" or "partitionKey", I would prefer the latter as it more clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
(collection.PartitionKey.Paths.Count != 1 || collection.PartitionKey.Paths[0] != "/id")) | ||
|
||
if (isPartitioned && | ||
(collection.PartitionKey.Paths.Count != 1 || !(collection.PartitionKey.Paths[0].Equals($"/{DocumentServiceLease.IdPropertyName}", StringComparison.OrdinalIgnoreCase) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please break too long lines. Normally 120 chars.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
{ | ||
throw new ArgumentException("The lease collection, if partitioned, must have partition key equal to id."); | ||
throw new ArgumentException($"The lease collection, if partitioned, must have partition key equal to {DocumentServiceLease.IdPropertyName} or {DocumentServiceLease.LeasePartitionKeyPropertyName}."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more correct to say /id or /partitionKey
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
if (isPartitioned && | ||
(collection.PartitionKey.Paths.Count != 1 || !(collection.PartitionKey.Paths[0].Equals($"/{DocumentServiceLease.IdPropertyName}", StringComparison.OrdinalIgnoreCase) || | ||
collection.PartitionKey.Paths[0].Equals($"/{DocumentServiceLease.LeasePartitionKeyPropertyName}", StringComparison.OrdinalIgnoreCase)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't use OrdinalIgnoreCase. JSON is case-sensitive. Just compare using "!=" . If using .Equals also check that Paths[0] is not null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// we allow only id or leasePk partitioning so check only flag | ||
requestOptionsFactory = collection.PartitionKey.Paths[0].Equals($"/{DocumentServiceLease.IdPropertyName}", StringComparison.OrdinalIgnoreCase) ? | ||
(IRequestOptionsFactory)new PartitionedByIdCollectionRequestOptionsFactory() : | ||
(IRequestOptionsFactory)new PartitionedByLeasePkCollectionRequestOptionsFactory(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think cast to interface is needed. Is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gives cs8370 error. conditional target type expression available in c# 9.0
var documentServiceLease = lease as DocumentServiceLease; | ||
if (documentServiceLease == null) | ||
throw new ArgumentException("lease is not of type DocumentServiceLease"); | ||
if (string.IsNullOrWhiteSpace(documentServiceLease.LeasePartitionKey)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is technically OK to have as we control setting PK value in the lease document. But in general null/empty PK value is valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gets, delete of lease were failing if PK value is null. Searches were working fine
using Microsoft.Azure.Documents.Client; | ||
|
||
/// <summary> | ||
/// Used to create request options for partitioned lease collections, when partition key is defined as /leasepk. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/leasepk: please don't forget to adjust this to latest version of what we decided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if (isPartitioned) | ||
{ | ||
// we allow only id or leasePk partitioning so check only flag | ||
requestOptionsFactory = collection.PartitionKey.Paths[0] != PartitionKeyPkPathName ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: better to check for == rather than !=. This will avoid extra churn in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
...ngeFeedProcessor/LeaseManagement/PartitionedByPartitionKeyCollectionRequestOptionsFactory.cs
Show resolved
Hide resolved
/// This is clone of existing Id property to maintain backward compat. | ||
/// This property name is compatible to both GremlinAccounts and SqlAccounts | ||
/// </summary> | ||
[JsonProperty(LeasePartitionKeyPropertyName, NullValueHandling= NullValueHandling.Ignore)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spaces around '='
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
public class LeasePkLeaseCollectionTests : StaticCollectionTests | ||
{ | ||
public LeasePkLeaseCollectionTests() : | ||
base(true,true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space after comma
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@ealsur can you please review last iteration? |
src/DocumentDB.ChangeFeedProcessor/LeaseManagement/DocumentServiceLeaseStoreManager.cs
Show resolved
Hide resolved
src/DocumentDB.ChangeFeedProcessor/LeaseManagement/DocumentServiceLeaseStore.cs
Show resolved
Hide resolved
src/DocumentDB.ChangeFeedProcessor.UnitTests/LeaseManagement/DocumentServiceLeaseTests.cs
Show resolved
Hide resolved
...ngeFeedProcessor/LeaseManagement/PartitionedByPartitionKeyCollectionRequestOptionsFactory.cs
Show resolved
Hide resolved
...ngeFeedProcessor/LeaseManagement/PartitionedByPartitionKeyCollectionRequestOptionsFactory.cs
Show resolved
Hide resolved
/azp run |
No pipelines are associated with this pull request. |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
scenario-
Expected: Just like the following works for a collection with sql api on gremlin account, expectation is changefeedprocessor also works and doesnt special case gremlin accounts which the below apis dont do.
Actual: ChangeFeedProcessor requires LeaseCollection to be created with "id" PartitionKey which is not supported on Gremlin Accounts. We get the following error when the lease collection is created. "“Microsoft.Azure.Documents.DocumentClientException: Partition key path /id is invalid for Gremlin API. The path cannot be '/id', '/label' or a nested path such as '/key/path'.""
Fix:
Tested:
Ran UT, IT locally with cosmosdb emulator in gremlin mode.
ran ut, it against azure sql account and azure graph account and verified the cases passed
Closes #156