Giter VIP home page Giter VIP logo

orleans.cosmosdb's Introduction

Note: This project was merged to Orleans main codebase. Starting on Orleans v7.2.0, all the providers are published to NuGet along with the core Orleans packages. New issues and contributions should be directed to https://github.com/dotnet/orleans.

Orleans.CosmosDB

Orleans CosmosDB Providers

CI NuGet NuGet NuGet NuGet Gitter

Orleans is a framework that provides a straight-forward approach to building distributed high-scale computing applications, without the need to learn and apply complex concurrency or other scaling patterns.

Azure CosmosDB is Microsoft's globally distributed, multi-model database. It is a database for extremely low latency and massively scalable applications anywhere in the world, with native support for NoSQL.

Orleans.CosmosDB is a package that use Azure CosmosDB as a backend for Orleans providers like Cluster Membership, Grain State storage, Streaming and Reminders.

Installation

Installation is performed via NuGet

From Package Manager:

PS> Install-Package Orleans.Clustering.CosmosDB

PS> Install-Package Orleans.Persistence.CosmosDB

PS> Install-Package Orleans.Reminders.CosmosDB

PS> Install-Package Orleans.Streaming.CosmosDB

.Net CLI:

# dotnet add package Orleans.Clustering.CosmosDB

# dotnet add package Orleans.Persistence.CosmosDB

# dotnet add package Orleans.Reminders.CosmosDB

# dotnet add package Orleans.Streaming.CosmosDB

Paket:

# paket add Orleans.Clustering.CosmosDB

# paket add Orleans.Persistence.CosmosDB

# paket add Orleans.Reminders.CosmosDB

# paket add Orleans.Streaming.CosmosDB

Configuration

It is not mandatory to use all the providers at once. Just pick the one you are interested in from the samples and you should be good as they don't depend on each other.

Silo

Example

var silo = new SiloHostBuilder()
    .UseCosmosDBMembership(opt => 
    {
        // Use CosmosDB as the cluster membership table.
        // Configure CosmosDB settings in opt.
    }) 
    .AddCosmosDBGrainStorageAsDefault(opt => 
    {
        // Configure CosmosDB persistence provider.
        // Configure CosmosDB settings in opt.
    }) 
    .UseCosmosDBReminderService(op => 
    {
        // Configure CosmosDB reminders provider.
        // Configure CosmosDB settings in opt.
    }) 
    .Build();
await silo.StartAsync();

Client

Example

var clientConfig = new ClientConfiguration();

var client = new ClientBuilder()
    .UseCosmosDBGatewayListProvider(opt => 
    {
        // Use CosmosDB as the Gateway list provider.
        // Configure CosmosDB settings in opt.
    }) 
    .Build();
    await client.Connect();

Great! Now you have Orleans configured to use Azure CosmosDB as the back storage for its providers!

Custom partition keys for storage provider

By default the grain type is used as partition key, however this limits the storage size for a single grain type as a single logical partition has an upper limit of 10 GB storage.

It is possible to override the default by implementing a custom IPartitionKeyProvider. The custom implementation of IPartitionKeyProvider can be registered by using the dependency injection usual methods like:

services.AddSingleton<IPartitionKeyProvider, MyCustomPKProvider>();

There is also some overloads of AddCosmosDBGrainStorage and AddCosmosDBGrainStorageAsDefault that allow you to pass the IPartitionKeyProvider implementation.

If no IPartitionKeyProvider is used, the default one will be uses which use full name of the grain type will be used as partition key.

Example

public class PrimaryKeyPartitionKeyProvider : IPartitionKeyProvider
{
    public ValueTask<string> GetPartitionKey(string grainType, GrainReference grainReference) 
    {
        return new ValueTask<string>(grainReference.GetPrimaryKey().ToString());
    }
}

The example above use the Primary key as partition key.

In order to prevent cross partition queries the partition key must be available to the client upon reading data, hence the delegate input is limited to the graintype and grainreference. The grain reference contains the grain id, with combination, and a type identifier.

For further details on partitioning in CosmosDB see https://docs.microsoft.com/en-us/azure/cosmos-db/partitioning-overview.

Backwards compatibility

The change will not affect existing systems. Configuring a custom IPartitionKeyProvider for an existing system will throw a BadGrainStorageConfigException stating incompatibility. To start using a custom partition key provider the old documents must be updated with a /PartitionKey property where the value is set using the same functionality as in the GetPartitionKey implementation in the custom IPartitionKeyProvider. Once all documents are updated, the data must be migrated to a new CosmosDB collection using Azure Data Factory, CosmosDB Migration tool or custom code with colletions PartitionKey set to PartitionKey

Migration from 1.3.0 to 3.0.0

With the 3.0.0 release, two breaking changes requires manual changes if you have pre-existent data:

Remove stored procedures

Reminders, Streaming and Persistence providers doesn't rely on Stored Procedures anymore. You can safely remove them from your databases once you migrate to 3.0.0. The only stored procedures that are still used, are the ones from the Clustering packages since Orleans membership protocol requires some atomic operations that are only possible on CosmosDB by using stored procedures. All the rest can be killed.

Reminders collection

Before 3.0.0, the reminders provider used to use a non-partitioned collection. Recently Microsoft requires that everyone using CosmosDB to migrate to partitioned collections. If you have data on your old collection, you need to migrate this data to a new one.

The new structure is defined as follow:

  • id field: $"{grainRef.ToKeyString()}-{reminderName}"
  • PartitionKey field: $"{serviceId}_{grainRef.GetUniformHashCode():X8}"
  • Other fields remain the same.

This data migration can be performed whatever the way you prefer, as long as the id and PartitionKey fields are formated the way described here. The partition key path of the new collection must be /PartitionKey.

Indexing

The current indexing fork relies on CosmosDB stored procedures for lookup. As stored procedures must be executed against a specific partition, the use of custom partition key builders is not compatible with the Orleans indexing fork.

Stream Provider

To use the Stream Provider you need to register it on your ISiloBuilder:

.AddCosmosDBStreaming(config => 
    config.AddStream("<provider name>", configure =>
    {
        // The information on FeedCollectionInfo property is related to the database that will be monitored by the change feed
        configure.FeedCollectionInfo = new DocumentCollectionInfo
        {
            Uri = new Uri("<CosmosDB URI>"),
            MasterKey = "<CosmosDB Master Key>" ,
            DatabaseName = "<CosmosDB Database>",
            CollectionName = "<CosmosDB Collection>" 
        };

        // The information on LeaseCollectionInfo is related to the CosmosDB Change Feed lease collection
        configure.LeaseCollectionInfo = new DocumentCollectionInfo
        {
            Uri = new Uri("<CosmosDB Change Feed Lease URI>"),
            MasterKey = "<CosmosDB Change Feed Lease Master Key>" ,
            DatabaseName = "<CosmosDB Change Feed Lease Database>",
            CollectionName = "<CosmosDB Change Feed Lease Collection>" 
        };
    }, typeof(PartitionKeyBasedStreamMapper)))

Then on your grain, you need to implement IAsyncObserver<Document> in order to receive the document that has changed and published thru Cosmos DB Change Feed.

Contributions

PRs and feedback are very welcome!

orleans.cosmosdb's People

Contributors

bytelabsco avatar cocporn avatar davidvanchu avatar galvesribeiro avatar karamell avatar kevincathcart avatar lilinvictorms avatar mumby0168 avatar panyang1217 avatar reubenbond avatar tedhartms avatar ulriksen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orleans.cosmosdb's Issues

Support multiple persistence providers

Current Orleans provider's configuration APIs are not DI-friendly. I made a work around to have this provider to support DI at some extent but it turns out that doing that, I'm only allowed to have 1 persistence provider registered.

Check with Orleans team when/if we will have:

  1. DI-friendly provider APIs
  2. Named options support

Use proper indexing

To reduce request costs, explicitly set the idexing to only properties being queried by the providers.

Allow customisation on partition Keys

If I understand correctly, the current implementation is partitioning by Grain Type. I do not think this is optimal for most scenarios.

  1. A partition key must be relatively uniformly distributed, to better balance load when storage does not fit in a single partition. Some GrainTypes might have 10 grains while others millions.
  2. A partition key defines the scope of transactions. When your entities share the same transaction key you can do a server-side javascript SProcs. While I understand that a transaction across different grains of the same type could be occasionally useful (build a relation between two People for example), it would be good to allow the liberty to decide on different scopes.

    As such, I think we should be able to mark a property of a GrainState class as the partition Key. This can also enable some advanced scenarios. Example:

    I have a collection of People. There I store 2 types of entities: person and personChanges (an audit trail of JSON patch diffs, modifications on the person that have or have not yet been approved). Both share the same "personId" as partitionKey.

    Let's imagine a user is working on some potential change on a person (it could span days before it's validated, it could be for example a person's divorce). This goes to a PersonChangeGrain (which saves its state nice and easily), until a specific operation is called "ValidateChange". In this particular case the code instead of calling WriteState, it would call the PersonGrain, which would apply the change on its state, and then call a javascript Sproc which (thanks to the fact that both share the same partition key) can in a transactional way 1) save the new PersonState and 2) mark the PersonChange (another entity) as applied. When the operation completes, the Person can request the original PersonChangeGrain to Refresh its State. This last operation might fail in a non transactional way, but it doesn't matter, as the next time the PersonChangeGrain will be reactivated, it will read the correct updated state from storage.


    If you're interested, I could attempt to propose code for declaring which property to use as key (by grain state class), even though I'm a newbie in Orleans (but less so in CosmosDB).

Use custom JsonSerializerSettings

Since the State payload is serialized as object, it goes just fine inside the StateEntity in CosmosDB as you may see in the following sample:

{
    "id": "00000000-0000-0000-0000-000000000000_GrainReference=00000000000000000000000000000000030000004e9c6618",
    "GrainType": "Orleans.CosmosDB.Tests.Grains.TestGrain",
    "State": {
        "Data": [
            "Test 0",
            "Test 0",
            "Test 0"
        ]
    },
    "_etag": "\"0000db02-0000-0000-0000-5a66a9550000\"",
    "_rid": "2UIfAKJEnwACAAAAAAAAAA==",
    "_self": "dbs/2UIfAA==/colls/2UIfAKJEnwA=/docs/2UIfAKJEnwACAAAAAAAAAA==/",
    "_attachments": "attachments/",
    "_ts": 1516677461
}

However, when we deserialize the State field back, it comes as a JObject because Json.Net doesn't know the proper type to deserialize to. That can be fixed if we use a custom JsonSerializerSettings object when initializing the DocumentClient and tell it to include the Type to the object serialized. I tried that, and when sending back the document to the database with that settings applied, the sproc parameters are all undefined.

To workaround the deserialization issue, I introduced the workaround here.

Just logging this issue so we can investigate how to properly use the JsonSerializerSettings and avoid the call to JObject.ToObject<T>() when deserializing the state.

Expose JsonSerializerSettings for the user

I need to use JsonSerializerSettings to set up the serializer for Nodatime Instant and Duration. My issue is probably related to #6. Is custom serializer settings not possible at all?

Limit max. version of DocumentDB used within project

It seems as if there are methods being used by Orleans.CosmosDB that are no longer existing within the 2.x version of Microsoft.Azure.DocumentDB.Core. We should probably patch the nuget package to prevent folks from using v2.0, v2.1, or v2.2 of Microsoft.Azure.DocumentDB.Core. As a subsequent patch, this library should go to v2.0.x and be upgraded to the v2.x Microsoft.Azure.DocumentDB.Core.

Error with CosmosDBGrainStorageFactory.Create in Orleans 3.5.0

Trying to use v3.0.3 with Orleans 3.5.0 and seeing this exception:

{"Type":"System.InvalidOperationException","HResult":-2146233079,"Message":"A suitable constructor for type 'Orleans.Persistence.CosmosDB.CosmosDBGrainStorage' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.","Source":"Microsoft.Extensions.DependencyInjection.Abstractions"}

Partial call stack:

System.InvalidOperationException:
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance (Microsoft.Extensions.DependencyInjection.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance (Microsoft.Extensions.DependencyInjection.Abstractions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
at Orleans.Persistence.CosmosDB.CosmosDBGrainStorageFactory.Create (Orleans.Persistence.CosmosDB, Version=3.0.3.0, Culture=neutral, PublicKeyToken=null)
at Orleans.Runtime.KeyedSingletonService2+<>c__DisplayClass4_0.<.ctor>b__0 (Orleans.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null) at System.Lazy1.ViaFactory (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Lazy1.ExecutionAndPublication (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Lazy1.CreateValue (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
at System.Lazy1.get_Value (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at Orleans.Runtime.KeyedSingletonService2.GetService (Orleans.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null)

I'm not sure if this means we're missing something in our configuration or if there's a mismatch between the CosmosDBGrainStorageFactory and the constructor of CosmosDBGrainStorage.

Implement data chunk

Whenever the GrainStateEntity.State size is bigger than the allowed document/field size, the data should be chunked in more documents.

Is there any reason why the IndexingMode is set to Consistent?

I am referring to:

var stateCollection = new ContainerProperties(this._options.Collection, DEFAULT_PARTITION_KEY_PATH);
stateCollection.IndexingPolicy.IndexingMode = IndexingMode.Consistent;
stateCollection.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
stateCollection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/\"State\"/*" });

From my understanding of the code, the Orleans.Persistence.CosmosDB Storage Provider always does point reads (only using ID and Partition Key); so it uses CosmosDB as a KV Store and never queries using the indexed values.

We are trying to optimize our CosmosDB usage and it looks like for indexing CosmosDB charges you extra RU/s. I was wondering if we could just go ahead and turn off indexing and this library would still work correctly.

As far as I can see it would, but I'm wondering if there is any reason that I'm missing

Implement stored procedures versioning

As this provider is based on JavaScript SProcs, it would be nice to have them deployed and (optionally) updated whenever a new version is published.

One way to do it, is to have a separated entity within the same collection of EntityType == Sproc where we would keep a reference to the current sprocs installed, and which version to use. That table would be read at startup and (optionally) if the version deployed doesn't match the one with the nuget package, it should update them.

This feature is optional as it would require the credentials to have access at the collection to create sprocs and sometimes it isn't desirable.

Fix CI

Make CircleCI build to play the ball with Azure CosmosDB emulator so it can run tests in CI.

Polymorphism does not work properly

I am trying to use a combo of Orleans, Orleans transactions, CosmosDB and polymorphism in my application.

So far I came up to the conclusion that the only way to make polymorphism work is to pass CosmosClient with a custom CosmosSerializer (just copied native implementation with proper TypeHandling) into the storage provider during the initialization. All the other attempts failed as JsonSerializerSettings and TypeHandling values of CosmosDBStorageOptions are ignored during the WriteStateAsync.

However, this custom CosmosClient has one drawback (or maybe a feature). During ReadStateAsync, the value of the State field has already the correct type (in case of Orleans transactions, the state is of TransactionalStateRecord type) when returned from CosmosClient whereas, without the custom implementation, the type of the State is JsonObject. This fact is causing an error on this line as .ToString() returns just a type name on TransactionalStateRecord, not a valid JSON.

grainState.State = JsonConvert.DeserializeObject(doc.Resource.State.ToString(), grainState.State.GetType(), this._options.JsonSerializerSettings);

Fixing is possible by checking if the type is JsonObject (do the current logic) or something else (set the state directly without deserialization) which I can do. However, I don't think this is the most beautiful solution in the world, haven't you already investigated a similar issue? Could be there some other pitfalls?

Unable to run Persistence Tests

Just downloaded the project and tried to run tests and none of the persistence test succeeded.
Same error for each test:
Message:
System.ArgumentException : GenericArguments[0], 'Orleans.Persistence.CosmosDB.CosmosDBGrainStorage', on 'Microsoft.Extensions.Options.OptionsMonitor1[TOptions]' violates the constraint of type 'TOptions'. ---- System.TypeLoadException : GenericArguments[0], 'Orleans.Persistence.CosmosDB.CosmosDBGrainStorage', on 'Microsoft.Extensions.Options.OptionsMonitor1[TOptions]' violates the constraint of type parameter 'TOptions'.
Stack Trace:
RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
RuntimeType.MakeGenericType(Type[] instantiation)
CallSiteFactory.TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, Int32 slot)
CallSiteFactory.TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain)
CallSiteFactory.CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
<>c__DisplayClass7_0.b__0(Type type)
ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
CallSiteFactory.GetCallSite(Type serviceType, CallSiteChain callSiteChain)
ServiceProviderEngine.CreateServiceAccessor(Type serviceType)
ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
ServiceProviderEngineScope.GetService(Type serviceType)
ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
CosmosDBGrainStorageFactory.Create(IServiceProvider services, String name) line 467
<.ctor>b__0()
Lazy1.ViaFactory(LazyThreadSafetyMode mode) Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
Lazy1.CreateValue() Lazy1.get_Value()
KeyedSingletonService2.GetService(IServiceProvider services) KeyedServiceCollection2.GetService(IServiceProvider services, TKey key)
KeyedServiceCollectionExtensions.GetServiceByKey[TKey,TService](IServiceProvider services, TKey key)
KeyedServiceCollectionExtensions.GetRequiredServiceByKey[TKey,TService](IServiceProvider services, TKey key)
KeyedServiceCollectionExtensions.GetRequiredServiceByName[TService](IServiceProvider services, String name)
<>c.b__27_2(IServiceProvider s, String n) line 289
<.ctor>b__0()
Lazy1.ViaFactory(LazyThreadSafetyMode mode) Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
Lazy1.CreateValue() Lazy1.get_Value()
KeyedSingletonService2.GetService(IServiceProvider services) <.ctor>b__64_1(IKeyedService2 s)
SelectArrayIterator2.MoveNext() Silo.ctor(ILocalSiloDetails siloDetails, IServiceProvider services) RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context) CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope scope)
ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
ServiceProviderEngineScope.GetService(Type serviceType)
ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
Host.StartAsync(CancellationToken cancellationToken)
----- Inner Stack Trace -----
RuntimeTypeHandle.Instantiate(Type[] inst)
RuntimeType.MakeGenericType(Type[] instantiation)

Reminders needs a partition key to support shared throughput

With the case of wanting to store both the Application Data and the Orleans data within the same CosmosDB instance, where that CosmosDB instance is using the new "shared" throughput facilities, it is a requirement to set partition keys for each collection, according to the output [below].

Lifecycle start canceled due to errors at stage 20000
warn: Orleans.Runtime.Scheduler *stg/22/00000016.WorkItemGroup[101215]
      Task [Id=11, Status=RanToCompletion] in WorkGroup [SystemTarget: S10.1.1.18:11111:282256074*stg/22/00000016@S00000016] took elapsed time 0:00:01.1214201 for execution, which is longer than 00:00:00.2000000. Running on thread System.Threading.Thread
dbug: Orleans.Runtime.DeploymentLoadPublisher[0]
      UpdateRuntimeStatistics from S10.1.1.18:11111:282256074
   at Orleans.LifecycleSubject.OnStart(CancellationToken ct)
   at Orleans.Runtime.Scheduler.AsyncClosureWorkItem.Execute()
   at Orleans.Runtime.Silo.StartAsync(CancellationToken cancellationToken)
   at .....Silo.Program.StartSilo() in .....Silo\Program.cs:line 154
   at .....Silo.Program.Main(String[] args) in .....Silo\Program.cs:line 130
--------------------------------------------------------------------------------
Provisioned throughput collection should have a partition key
ActivityId: 44fa21ea-8e0a-43ad-a6e2-a5bf0d909d80, Microsoft.Azure.Documents.Common/2.2.0.0, Linux/9 documentdb-netcore-sdk/1.9.1
   at Microsoft.Azure.Documents.Client.ClientExtensions.ParseResponseAsync(HttpResponseMessage responseMessage, JsonSerializerSettings serializerSettings)
   at Microsoft.Azure.Documents.GatewayStoreModel.<>c__DisplayClass20_0.<<InvokeAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<>c__DisplayClass1_0.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetry(Func`1 callbackMethod, Func`3 callShouldRetry, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetry(Func`1 callbackMethod, Func`3 callShouldRetry, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteAsync(Func`1 callbackMethod, IRetryPolicy retryPolicy, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.GatewayStoreModel.InvokeAsync(DocumentServiceRequest request, ResourceType resourceType)
   at Microsoft.Azure.Documents.GatewayStoreModel.ProcessMessageAsync(DocumentServiceRequest request)
   at Microsoft.Azure.Documents.Client.DocumentClient.CreateAsync(DocumentServiceRequest request)
   at Microsoft.Azure.Documents.Client.DocumentClient.CreateDocumentCollectionPrivateAsync(String databaseLink, DocumentCollection documentCollection, RequestOptions options)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.<>c__DisplayClass1_0.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetry(Func`1 callbackMethod, Func`3 callShouldRetry, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteRetry(Func`1 callbackMethod, Func`3 callShouldRetry, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.BackoffRetryUtility`1.ExecuteAsync(Func`1 callbackMethod, IRetryPolicy retryPolicy, CancellationToken cancellationToken, Action`1 preRetryCallback)
   at Microsoft.Azure.Documents.Client.DocumentClient.CreateDocumentCollectionIfNotExistsPrivateAsync(Uri databaseUri, DocumentCollection documentCollection, RequestOptions options)
   at Orleans.Reminders.CosmosDB.CosmosDBReminderTable.TryCreateCosmosDBResources()
   at Orleans.Reminders.CosmosDB.CosmosDBReminderTable.Init()
   at Orleans.OrleansTaskExtentions.WithTimeout(Task taskToComplete, TimeSpan timeout, String exceptionMessage)
   at Orleans.Runtime.ReminderService.LocalReminderService.Start()
   at Orleans.Runtime.Scheduler.AsyncClosureWorkItem.Execute()
   at Orleans.OrleansTaskExtentions.WithTimeout(Task taskToComplete, TimeSpan timeout, String exceptionMessage)
   at Orleans.Runtime.Silo.<OnActiveStart>g__StartReminderService|74_0()
   at Orleans.Runtime.Silo.StartAsyncTaskWithPerfAnalysis(String taskName, Func`1 task, Stopwatch stopWatch)
   at Orleans.Runtime.Silo.OnActiveStart(CancellationToken ct)
   at Orleans.Runtime.SiloLifecycleSubject.MonitoredObserver.OnStart(CancellationToken ct)
   at Orleans.LifecycleSubject.WrapExecution(CancellationToken ct, Func`2 action)
   at Orleans.LifecycleSubject.OnStart(CancellationToken ct)
warn: Orleans.Runtime.Silo[100220]

TrasportException with Microsoft.Azure.DocumentDB.Core 2.2.3 and 2.2.2 drivers

After a bit of load something goes wrong in the CosmosDB driver, we're getting a lot of TransportExpcetion. The TransportException type was introduced in Microsoft.Azure.DocumentDB.Core 2.2.0, but we did not see this exception before upgrading to 2.2.2. Version 2.2.1 works fine.

We have created our own package built with Microsoft.Azure.DocumentDB.Core 2.2.1 to get around this problem. We've had an initial chat with our MSFT contact and are trying to get the CosmosDB team involved.

We're interested if anyone else experience this problem.

Exception with ConsistencyLevel

I get this error when starting Silos:
ArgumentException: ConsistencyLevel Strong specified in the request is invalid when service is configured with consistency level Session. Ensure the request consistency level is not stronger than the service consistency level.

Samples for Orleans 2.0

Hi, I try to use Cosmos DB in Orleans 2.0, but it doesn't work.
Maybe you can provide samples how to I can config it?

Cannot resolve scoped service 'CosmosDBStorageOptions'

In a fresh .NETCore 3.1 project I'm trying to build an Orleans silo using the new generic host builder.
Unfortunately I keep running into the following error:

System.InvalidOperationException: 'Cannot resolve scoped service 'Microsoft.Extensions.Options.IOptionsSnapshot`1[Orleans.Persistence.CosmosDB.CosmosDBStorageOptions]' from root provider.'

UseCosmosDBMembership and UseCosmosDBReminderService work perfectly. As soon as I add GrainStorage(AsDefault) the error occurs.

As the DI cannot resolve CosmosDBStorageOptions I suspect I'm missing a package or reference.

Besides a few Serilog and Swashbuckle nuget-packages the following Orleans packages are installed:

  • Microsoft.Orleans.Server v3.0.2
  • Orleans.Clustering.CosmosDB v3.0.0
  • Orleans.Persistence.CosmosDB v3.0.0
  • Orleams.Reminders.CosmosDB v3.0.0

My HostBuilder looks as follows:

var Host = new HostBuilder()
    .UseContentRoot(Directory.GetCurrentDirectory())

    .UseDefaultServiceProvider((Context, Options) =>
    {
        var IsDevelopment = Context.HostingEnvironment.IsDevelopment();
        Options.ValidateScopes = IsDevelopment;
        Options.ValidateOnBuild = IsDevelopment;
    })
    .UseSerilog()
    .UseOrleans(SiloBuilder =>
    {
        _ = SiloBuilder.Configure<ClusterOptions>(ClusterOptions =>
        {
            ClusterOptions.ClusterId = "SomeId";
            ClusterOptions.ServiceId = "SomeId"; 
        });
        
        _ = SiloBuilder.ConfigureEndpoints(siloPort: x, gatewayPort: x);

        // Silo clustering persistance provider.
        _ = SiloBuilder.UseCosmosDBMembership(Options =>
        {
            // Configuration
        });

        // Default grain persistance storage provider.
        _ = SiloBuilder.AddCosmosDBGrainStorageAsDefault(Options =>
        {
            // Configuration
        });

        // Reminder persistance storage provider.
        _ = SiloBuilder.UseCosmosDBReminderService(Options =>
        {
            // Configuration
        });
    })
    .ConfigureWebHostDefaults(WebHostBuilder =>  
        _ = WebHostBuilder.UseStartup<HostStartup>()                        
        .UseUrls("SomeUrl"))
    .Build();

Any clue as to what I'm doing wrong? Or am I facing a compatibility bug with the newer Orleans versions?

Kind regards,
Jan van Linge

Fix tests

As Orleans don't have a black box unit test kit for providers, I tried to copy the tests from main Orleans repo to this one in order to make some basic testing.

It turns out that it will be way much complicated than I expected to make it work.

Need to get some tests working as the current ones are getting complicated to.

Support serialization providers

Currently only JSON serialization is supported but Orleans allows configuring any other serializer - can this be updated to use other configured serializers?

Support serverless (preview)

Currently when creating resources, serverless cosmos will return a 400 error

Membership failed to start: Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: 400 Substatus: 0 Reason: (Microsoft.Azure.Documents.DocumentClientException: Setting offer throughput or autopilot on container is not supported for serverless accounts.ActivityId: 4360c6e4-0533-441c-936a-97591ad4ed95, Microsoft.Azure.Documents.Common/2.11.0, {"RequestStartTimeUtc":"2020-11-03T16:21:20.2286515Z","RequestEndTimeUtc":"2020-11-03T16:21:20.2731706Z","RequestLatency":"00:00:00.0445191","IsCpuOverloaded":false,"NumberRegionsAttempted":1,"ResponseStatisticsList":[],"AddressResolutionStatistics":[{"StartTime":"2020-11-03T16:21:20.2287812Z","EndTime":"2020-11-03T16:21:20.2731706Z","TargetEndpoint":"https://sharedc2c-southcentralus.documents.azure.com/dbs/orleans-local/colls"}],"SupplementalResponseStatistics":[],"FailedReplicas":[],"RegionsContacted":[],"ContactedReplicas":[]}, Windows/10.0.19042 cosmos-netstandard-sdk/3.4.2 at Microsoft.Azure.Cosmos.GatewayStoreClient.ParseResponseAsync(HttpResponseMessage responseMessage, JsonSerializerSettings serializerSettings, DocumentServiceRequest request) at Microsoft.Azure.Cosmos.GatewayStoreClient.InvokeAsync(DocumentServiceRequest request, ResourceType resourceType, Uri physicalAddress, CancellationToken cancellationToken) at Microsoft.Azure.Cosmos.GatewayStoreModel.ProcessMessageAsync(DocumentServiceRequest request, CancellationToken cancellationToken) at Microsoft.Azure.Cosmos.Handlers.TransportHandler.SendAsync(RequestMessage request,

Request charge and stored procedures

As part of our scaling efforts we are looking at our CosmosDB utilization and costs. We noticed the procedure calling JSON.parse and wanted to see if we could send the extracted values (graintype, id and etag) as parameters instead of parsing it, and how this would affect the request charge (RC). Short version it doesn't.

We did however find the RU cost somewhat high compared to calling Cosmos operations directly. Based on the document below[1], here's our findings

Average RC read using stored procedure: 5.63
Average RC create using stored procedure: 11.68
Average RC update using stored procedure: 14.89

RC read using GET: 1
RC create using PUT: 6.29
RC update using POST: 10.29

This seems rather excessive, and we're wondering if one can improve the stored procedures or skip it and call Cosmos db directly.

{
  "id": "b780de47-a7d2-44c5-8a8f-7eea29e9561f__GrainReference=4f3d549833293c5e5d419486ec1f04a30600000031c1478e+7054fbd0-9dce-48ed-ab5c-4ed85ba0c66c",
  "GrainType": "Ps.Personlig.Grains.FavorittGrainRadio",
  "State": {
    "State": {
      "Case": "V1",
      "Fields": [
        {
          "FavoriserbarId": "K|saann_er_du",
          "Medium": "radio",
          "KildeType": "manuell",
          "Registrert": 15481904238110000,
          "Slettet": {
            "Case": "Some",
            "Fields": [
              15518746264096320
            ]
          }
        }
      ]
    }
  },
  "PartitionKey": "testh"
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.