Azure-cosmos-dotnet-v3: Managed Debugging Assistant 'PInvokeStackImbalance' : 'A call to PInvoke function 'Microsoft.Azure.Cosmos.Direct!Microsoft.Azure.Cosmos.Core.Trace.EtwNativeInterop::EventWriteString' has unbalanced the stack

Created on 5 Aug 2019  路  9Comments  路  Source: Azure/azure-cosmos-dotnet-v3

We are in the process of upgrading from the 2.x document client to the 3.1 Cosmo client. I have created a repository class to generalize/isolate some of the cosmo sdk code. In my upsert method, I am attempting to construct the cosmo client in a using statement - this seemed to be supported by documentation and runs properly when NOT DEBUGGING.

To Reproduce
I don't want to share the full code base if I can avoid it. My test is set up this this:

        [Fact]
        public async void TestRepositoryExample()
        {
            await activityPlayRepository.UpdateActivityPlayDocument(new ActivityPlayDocument()
            {
                GameID = new Guid("896BB422-5A9F-E511-80E7-000D3A001FE3"),
                TenantUserID = new Guid("896BB422-5A9F-E511-80E7-000D3A001FE3")
            });

            // method has no return - just a bs assertion to prove the test
            Assert.NotEmpty(new List<string>(){"f"});

        }

Which calls a collection specific repo class (basically just a configured wrapper for the actual cosmo repo class we have):

        public async Task UpdateActivityPlayDocument(ActivityPlayDocument activityPlayDocument)
        {
            await _cosmoDbCollectionRepository.Upsert(new CosmoDBCollectionUpsertQuery<ActivityPlayDocument>()
            {
                AccessConfiguration = _accessConfiguration,
                CosmoIDResolver = x => x.GameSetScoreID.ToString(),
                CosmoPartitionResolver = x => new PartitionKey(x.GameID.ToString()),
                Data = activityPlayDocument,
                InsertionRuleType = InsertionRuleType.ErrorIfOldRecordDoesNotExist
            });
        }

Which then calls my actual cosmo connection class:

        public async Task<CosmoDBCollectionUpsertQueryResult> Upsert<T>(CosmoDBCollectionUpsertQuery<T> cosmoDbCollectionUpsertQuery)
        {
            var result = new CosmoDBCollectionUpsertQueryResult();

            try
            {
                using (CosmosClient cosmosClient = new CosmosClient(cosmoDbCollectionUpsertQuery.AccessConfiguration.AccountEndPoint,
                    cosmoDbCollectionUpsertQuery.AccessConfiguration.AccountKey))
                {
                    var propertyResolver =
                        cosmoDbCollectionUpsertQuery.CosmoIDResolver(cosmoDbCollectionUpsertQuery.Data);

                    var partitionResolver = cosmoDbCollectionUpsertQuery.CosmoPartitionResolver(cosmoDbCollectionUpsertQuery.Data);

                    var container = cosmosClient
                        .GetContainer(cosmoDbCollectionUpsertQuery.AccessConfiguration.DatabaseId,
                            cosmoDbCollectionUpsertQuery.AccessConfiguration.ContainerId);
                    try
                    {

                        // see if the record exists already - if it doesn't, an error will be throw
                        await container.ReadItemAsync<T>(propertyResolver, partitionResolver);

                        switch (cosmoDbCollectionUpsertQuery.InsertionRuleType)
                        {
                            case InsertionRuleType.UseImplementationRuleChoice: // impl choice is merge here
                            case InsertionRuleType.MergeOldRecordIfExists:
                                await container.UpsertItemAsync(cosmoDbCollectionUpsertQuery.Data, partitionResolver);
                                break;
                            case InsertionRuleType.DeleteOldRecordIfExists:
                                await container.DeleteItemAsync<T>(propertyResolver, partitionResolver);
                                await container.CreateItemAsync<T>(cosmoDbCollectionUpsertQuery.Data, partitionResolver);
                                break;
                            default:
                                throw new ArgumentOutOfRangeException(nameof(cosmoDbCollectionUpsertQuery
                                    .InsertionRuleType));
                        }
                    }
                    catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound
                                                     && cosmoDbCollectionUpsertQuery.InsertionRuleType != InsertionRuleType.ErrorIfOldRecordDoesNotExist)
                    {
                        await container.CreateItemAsync<T>(cosmoDbCollectionUpsertQuery.Data, partitionResolver);
                    }
                }
            }
            catch (Exception e)
            {
                result.Exception = e;
            }

            return result;
        }


The using statement is where things go bad for me. I get the following error as soon as it hits the using statement:

Managed Debugging Assistant 'PInvokeStackImbalance' : 'A call to PInvoke function 'Microsoft.Azure.Cosmos.Direct!Microsoft.Azure.Cosmos.Core.Trace.EtwNativeInterop::EventWriteString' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.'

Expected behavior
I should be able to debug my methods and skip over unmanaged code or get an exception that I can act on (if my call signature is wrong, etc).

Actual behavior
Exception is thrown when debugging from a unit test but exception does not occur if the test is run without debugging. Information is persisted to cosmo repo as expected when "Run", but fails when "Debug".

Environment summary
SDK Version: Microsoft.Azure.Cosmos 3.1.0
xunit 2.4.0 / .NET Framework 4.71
OS Version: Windows 10 17134.885

Additional context
Please let me know if I've excluded any critical details. Thanks

Most helpful comment

Root cause found. Working on an action plan now.

All 9 comments

Not related to this bug but is there any reason you are creating the CosmosClient for each call? The best practices recommend using a singleton. It avoids getting the caches for each new instance.

Thanks. I saw that but I figured I could pretty quickly refactor to inject it as a singleton instance later. The project that contains this code is a collection of class libraries that we use in a few asp net projects, so I'd rather save that for last. I'm pretty sure you should be able to create the client for each call though, if you want.

Same here, only happens in .net 4.7.2 project, not in netcoreapp2.2

Happens here as well, 4.6.2, without a using statement.

@ausfeldt do you have any suggestions?

Same, .NET Standard 2.0 project.

Root cause found. Working on an action plan now.

This issue was fixed in 3.1.1 .

Thanks for the quick turn around. Appreciate the help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

j82w picture j82w  路  4Comments

jriouxrandstadca picture jriouxrandstadca  路  4Comments

kirankumarkolli picture kirankumarkolli  路  5Comments

lbicknese picture lbicknese  路  5Comments

kirankumarkolli picture kirankumarkolli  路  7Comments