feat(cosmos): support custom document IDs and partition keys - #8699
Conversation
774631d to
5c3c5e4
Compare
|
@ReubenBond, Looking ahead at the capabilities for the Microsoft.Extensions.Configuration support you implemented in #9204 should this PR include the ability to configure the type to call from DI for the IDocumentIdProvider of a given provider when configuring via IConfiguration? The wiring might not be there yet but should the configuration value be present on the CosmosGrainStorageOptions class? |
There was a problem hiding this comment.
Pull request overview
This PR updates the Orleans Cosmos DB grain storage provider to support customizing both the document id and partition key via a new IDocumentIdProvider, replacing the prior partition-key-only abstraction (IPartitionKeyProvider) as a breaking change.
Changes:
- Replaced
IPartitionKeyProviderwithIDocumentIdProviderand introducedDefaultDocumentIdProvider. - Updated
CosmosGrainStorageto use the new provider for id/partition key generation and adjusted clear-state behavior. - Updated hosting extensions and Cosmos persistence tests to use the new provider.
Show a summary per file
| File | Description |
|---|---|
| test/Extensions/Tester.Cosmos/PersistenceProviderTests_Cosmos.cs | Updates test setup to construct storage using DefaultDocumentIdProvider. |
| src/Azure/Orleans.Persistence.Cosmos/IPartitionKeyProvider.cs | Removes the old partition-key-only provider interface and default implementation. |
| src/Azure/Orleans.Persistence.Cosmos/IDocumentIdProvider.cs | Adds the new interface for generating document ids and partition keys. |
| src/Azure/Orleans.Persistence.Cosmos/HostingExtensions.cs | Switches DI wiring from partition key provider to document id provider and updates extension methods. |
| src/Azure/Orleans.Persistence.Cosmos/DefaultDocumentIdProvider.cs | Adds the default implementation for document id + partition key generation. |
| src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs | Uses IDocumentIdProvider for read/write/clear and updates legacy /GrainType compatibility checks. |
Copilot's findings
Suppressed comments (2)
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:369
- The /GrainType partition key path compatibility check currently rejects any custom IDocumentIdProvider (anything other than DefaultDocumentIdProvider). That blocks custom document id support for clusters using existing containers partitioned by /GrainType, even if the custom provider returns a compatible partition key (grainType). Consider relaxing this check (or checking compatibility based on the returned partition key) so that custom document ids can be used with legacy /GrainType containers.
if (_partitionKeyPath == GRAINTYPE_PARTITION_KEY_PATH &&
_documentIdProvider is not DefaultDocumentIdProvider)
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:370
- This OrleansConfigurationException message still refers to a "Custom partition key provider", but the code now uses IDocumentIdProvider/DefaultDocumentIdProvider. Updating the message will make misconfiguration easier to understand.
if (_partitionKeyPath == GRAINTYPE_PARTITION_KEY_PATH &&
_documentIdProvider is not DefaultDocumentIdProvider)
throw new OrleansConfigurationException("Custom partition key provider is not compatible with partition key path set to /GrainType");
- Files reviewed: 6/6 changed files
- Comments generated: 4
…s DB grain persistence
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e82f060d-d09b-42f9-8b0f-34da38b386fa
7991079 to
7d3abdd
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e82f060d-d09b-42f9-8b0f-34da38b386fa
There was a problem hiding this comment.
Copilot's findings
Suppressed comments (3)
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:178
- After successfully deleting state (DeleteStateOnClear=true), the code clears ETag/RecordExists but does not reset grainState.State. This differs from ResetGrainState used elsewhere in the provider and can leave stale state in the activation.
{
try
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:202
- When DeleteStateOnClear is false, ClearStateAsync overwrites storage with an empty document and sets RecordExists=false, but it no longer resets grainState.State to a fresh instance. This can leave the activation with the pre-clear values even though the record is cleared.
await _executor.ExecuteOperation(static args =>
{
var (self, id, pk, requestOptions) = args;
return self._container.DeleteItemAsync<GrainStateEntity<T>>(id, pk, requestOptions);
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:168
- When DeleteStateOnClear is enabled and the caller has no ETag, this method returns without clearing the in-memory state (and without normalizing ETag). That can leave the activation holding stale state after a successful ClearStateAsync call. Consider resetting the grain state (without touching storage) before returning.
This issue also appears in the following locations of the same file:
- line 177
- line 199
public async Task ClearStateAsync<T>(string grainType, GrainId grainId, IGrainState<T> grainState)
{
var (id, partitionKey) = await _documentIdProvider.GetDocumentIdentifiers(grainType, grainId);
LogTraceClearingState(grainType, id, grainId, grainState.ETag, _options.DeleteStateOnClear, _options.ContainerName, partitionKey);
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
There was a problem hiding this comment.
Copilot's findings
Suppressed comments (2)
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:333
- The /GrainType partition-key-path guard currently rejects any custom IDocumentIdProvider, even if it only customizes the document id while still returning partitionKey == grainType. Since Cosmos uses GrainType as the partition key when the path is /GrainType, custom document ids can still be compatible as long as the partition key remains the grain type. Consider only rejecting custom partition-key providers here (and/or validating that the returned partition key equals grainType at runtime).
if (_partitionKeyPath == GRAINTYPE_PARTITION_KEY_PATH &&
(_documentIdProvider is not DefaultDocumentIdProvider defaultProvider || defaultProvider.HasCustomPartitionKeyProvider))
throw new OrleansConfigurationException("Custom document id or partition key providers are not compatible with partition key path set to /GrainType");
src/Azure/Orleans.Persistence.Cosmos/HostingExtensions.cs:40
- The generic AddCosmosGrainStorage/AddCosmosGrainStorageAsDefault overloads no longer provide compile-time validation that TProvider implements IDocumentIdProvider or IPartitionKeyProvider (the constraint is now just
class), so invalid types compile and fail only at runtime via AddIdentifierProvider. Consider adding a new constrained generic overload for IDocumentIdProvider and keeping legacy IPartitionKeyProvider-based overloads as [Obsolete] to preserve compile-time safety for the primary API.
public static ISiloBuilder AddCosmosGrainStorageAsDefault<TProvider>(
this ISiloBuilder builder,
Action<CosmosGrainStorageOptions> configureOptions) where TProvider : class
{
return builder.AddCosmosGrainStorage<TProvider>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, configureOptions);
}
/// <summary>
/// Configure silo to use Azure Cosmos DB storage for grain storage using a custom document id provider
/// or a legacy partition key provider.
/// </summary>
/// <typeparam name="TProvider">The document id or partition key provider.</typeparam>
/// <param name="builder">The silo builder.</param>
/// <param name="name">The storage provider name.</param>
/// <param name="configureOptions">The delegate used to configure the provider.</param>
public static ISiloBuilder AddCosmosGrainStorage<TProvider>(
this ISiloBuilder builder,
string name,
Action<CosmosGrainStorageOptions> configureOptions) where TProvider : class
- Files reviewed: 7/7 changed files
- Comments generated: 1
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e82f060d-d09b-42f9-8b0f-34da38b386fa
There was a problem hiding this comment.
Copilot's findings
Suppressed comments (2)
src/Azure/Orleans.Persistence.Cosmos/CosmosGrainStorage.cs:333
- This check rejects any custom IDocumentIdProvider when PartitionKeyPath is "/GrainType", even though customizing only the document id can still be compatible as long as the provider returns PartitionKey == grainType (Cosmos uses the GrainType property as the partition key, independent of document id).
Either relax this restriction for custom IDocumentIdProvider implementations, or validate/enforce that the returned partition key matches grainType when PartitionKeyPath is "/GrainType" (and update the exception message accordingly).
if (_partitionKeyPath == GRAINTYPE_PARTITION_KEY_PATH &&
(_documentIdProvider is not DefaultDocumentIdProvider defaultProvider || defaultProvider.HasCustomPartitionKeyProvider))
throw new OrleansConfigurationException("Custom document id or partition key providers are not compatible with partition key path set to /GrainType");
src/Azure/Orleans.Persistence.Cosmos/HostingExtensions.cs:26
- The generic overloads now accept any class (no IDocumentIdProvider/IPartitionKeyProvider constraint), so mis-typed calls compile and only fail at runtime via AddIdentifierProvider’s ArgumentException. That’s a significant loss of type-safety/IDE guidance for a public API.
Consider restoring compile-time safety by splitting into two overloads (one constrained to IDocumentIdProvider, plus an [Obsolete] overload constrained to IPartitionKeyProvider for legacy), or introducing a shared marker interface. This will require regenerating the API surface file.
public static ISiloBuilder AddCosmosGrainStorageAsDefault<TProvider>(
this ISiloBuilder builder,
Action<CosmosGrainStorageOptions> configureOptions) where TProvider : class
{
return builder.AddCosmosGrainStorage<TProvider>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, configureOptions);
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
|
@OmnipotentOwl Addressed in #10326. Configuration now accepts |
Currently, only custom partition keys are supported. This is a compilation-breaking change, since it changes the interface used to specify the partition key. The usage of that interface was broken in previous releases, and the fix is straightforward: change your implementation of IPartitionKeyProvider to IDocumentIdProvider. Then, if you do not wish to customize the document id, inject
DefaultDocumentIdProviderand callGetIdto get the document id.Microsoft Reviewers: Open in CodeFlow