This MongoDB deployment does not support retryable writes Exception on ASP.net core - asp.net

I am working on asp.net core and mongo db. Here I am trying to implement transaction support using the below code
using (var session = await _mongoClient.StartSessionAsync())
{
try
{
session.StartTransaction();
await _dbCollection.InsertOneAsync(session,obj);
//throw new Exception("No document found");
await session.CommitTransactionAsync();
}
catch (Exception ex)
{
//session.AbortTransaction();
await session.AbortTransactionAsync();
}
}
But it is throwing exception
MongoDB.Driver.MongoCommandException: This MongoDB deployment does not
support retryable writes. Please add retryWrites=false to your
connection string.
The below code, I am using to create connection
_mongoClient = new MongoClient(config.GetConnectionString("CarGalleryDb"));
//MongoClientSettings mongoClientSettings = new MongoClientSettings();
//mongoClientSettings.RetryWrites = false;
_db = _mongoClient.GetDatabase(config.GetConnectionString("DBName"));
And my connection string is
"ConnectionStrings": {
"CarGalleryDb": "mongodb://localhost:27017?retryWrites=false",
"DBName": "MongoTestDB"
},
Can anybody support me to resolve this one?

You need to add retryWrites=false at the end of the your connection string.
See Retryable Writes in Mongo
The official MongoDB 3.6 and 4.0-compatible drivers required including
the retryWrites=true option in the connection string to enable
retryable writes for that connection.
The official MongoDB 4.2-compatible drivers enable Retryable Writes by
default. Applications upgrading to the 4.2-compatible drivers that
require retryable writes may omit the retryWrites=true option.
Applications upgrading to the 4.2-compatible drivers that require
disabling retryable writes must include retryWrites=false in the
connection string.
Or you can set in code by using MongoClientSettings.RetryWrites:
https://mongodb.github.io/mongo-csharp-driver/2.7/apidocs/html/P_MongoDB_Driver_MongoClientSettings_RetryWrites.htm

This error is produced when you are using the deprecated MMAPv1 storage engine. If you can you should upgrade to WiredTiger (and a newer MongoDB version since MMAPv1 is removed in 4.2 altogether).

Related

Realm doesn’t work with xUnite and .net core

I’m having issues running realm with xUnite and Net core. Here is a very simple test that I want to run
public class UnitTest1
{
[Scenario]
public void Test1()
{
var realm = Realm.GetInstance(new InMemoryConfiguration("Test123"));
realm.Write(() =>
{
realm.Add(new Product());
});
var test = realm.All<Product>().First();
realm.Write(() => realm.RemoveAll());
}
}
I get different exceptions on different machines (Windows & Mac) on line where I try to create a Realm instace with InMemoryConfiguration.
On Mac I get the following exception
libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.
On Windows I get the following exception when running
ERROR Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. at
System.Net.Sockets.NetworkStream.Read(Span1 destination) at
System.Net.Sockets.NetworkStream.ReadByte() at
System.IO.BinaryReader.ReadByte() at
System.IO.BinaryReader.Read7BitEncodedInt() at
System.IO.BinaryReader.ReadString() at
Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable() at
Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action1 errorHandler, CancellationToken cancellationToken) Source: System.Net.Sockets HResult: -2146232800 Inner Exception: An existing connection was forcibly closed by the remote host HResult: -2147467259
I’m using Realm 3.3.0 and xUnit 2.4.1
I’ve tried downgrading to Realm 2.2.0, and it didn’t work either.
The solution to this problem was found in this Github post
The piece of code from that helped me to solve the issue
Realm GetInstanceWithoutCapturingContext(RealmConfiguration config)
{
var context = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
Realm realm = null;
try
{
realm = Realm.GetInstance(config);
}
finally
{
SynchronizationContext.SetSynchronizationContext(context);
}
return realm;
}
Though it took a while for me to apply this to my solution.
First and foremost, instead of just setting the context to null I am using Nito.AsyncEx.AsyncContext. Because otherwise automatic changes will not be propagated through threads, as realm needs a non-null SynchronizationContext for that feature to work. So, in my case the method looks something like this
public class MockRealmFactory : IRealmFactory
{
private readonly SynchronizationContext _synchronizationContext;
private readonly string _defaultDatabaseId;
public MockRealmFactory()
{
_synchronizationContext = new AsyncContext().SynchronizationContext;
_defaultDatabaseId = Guid.NewGuid().ToString();
}
public Realm GetRealmWithPath(string realmDbPath)
{
var context = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
Realm realm;
try
{
realm = Realm.GetInstance(new InMemoryConfiguration(realmDbPath));
}
finally
{
SynchronizationContext.SetSynchronizationContext(context);
}
return realm;
}
}
Further, this fixed a lot of failing unit tests. But I was still receiving that same exception - Realm accessed from incorrect thread. And I had no clue why, cause everything was set correctly. Then I found that the tests that were failing were related to methods where I was using async realm api, in particular realm.WriteAsync. After some more digging I found the following lines in the realm documentation.
It is not a problem if you have set SynchronisationContext.Current but
it will cause WriteAsync to dispatch again on the thread pool, which
may create another worker thread. So, if you are using Current in your
threads, consider calling just Write instead of WriteAsync.
In my code there was no direct need of using the async API. I removed and replaced with sync Write and all the tests became green again! I guess if I find myself in a situation that I do need to use the async API because of some kind of bulk insertions, I'd either mock that specific API, or replace with my own background thread using Task.Run instead of using Realm's version.

LoadBalancingChannel exception on DocumentClient.Dispose()

We have hit an issue where after using CosmosDb, an exception occurs if we try to dispose of the DocumentClient shortly after. Waiting a few seconds before disposing causes no exceptions. We have confirmed that we are using await with every asynchronous call.
Psuedo-code:
using(DocumentClient documentClient = new DocumentClient(...params)) {
IOrderedQueryable<T> query = this.documentClient.CreateDocumentQuery<T>(...params);
IList<T> documents;
using (IDocumentQuery<T> documentQuery = query.AsDocumentQuery()) {
documents = (await documentQuery.ExecuteNextAsync<T>()).ToList();
}
// Processing...
}
The exception states:
LoadBalancingChannel rntbd://[ip].documents.azure.com:[port]/ in use
The API that makes the call successfully returns before DocumentClient.Dispose is called (all of the documents are correctly returned).
Has anyone seen this exception before? A search revealed no hits.
It can happen if the DocumentClient is disposed while pending requests. this was addressed in SDK version 2.2.2, please upgrade to latest SDK version.

Identity Server EF Core DB Configuration with Azure MSI

I am trying to configure my Identity Server instance that is hosted as an Azure Web App to connect to my database using the Managed Service Identity (https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi).
At the time of this article linked above being written, EF Core did not support Access Tokens for SQL Server connections. It appears this has changed with release 2.2 that is currently in preview.
Currently my configuration store setup looks like this:
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
{
b.UseSqlServer(
connectionString,
sql => { sql.MigrationsAssembly(migrationsAssembly); }
);
};
})
With the EF Core 2.2 preview package, how would I go about adding the Access Token value equivalently as the guide linked above?
public MyDatabaseContext(SqlConnection conn) : base(conn, true)
{
conn.ConnectionString = WebConfigurationManager.ConnectionStrings["MyDbConnection"].ConnectionString;
// DataSource != LocalDB means app is running in Azure with the SQLDB connection string you configured
if(conn.DataSource != "(localdb)\\MSSQLLocalDB")
conn.AccessToken = (new AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
Database.SetInitializer<MyDatabaseContext>(null);
}
At initial glance, it looks like I would have to wait for the IdentityServer4.EntityFramework package to support this?
I had a similar issue,
Try the overload of UseSqlServer that accepts a DbConnection, and pass it a SqlConnection with the token set.
It worked for me

Is Azure Function to Function authentication with MSI supported

I created 2 Azure Function Apps, both setup with Authentication/Authorization so an AD App was created for both. I would like to setup AD Auth from one Function to the other using MSI. I setup the client Function with Managed Service Identity using an ARM template. I created a simple test function to get the access token and it returns: Microsoft.Azure.Services.AppAuthentication: Token response is not in the expected format.
try {
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://myapp-registration-westus-dev.azurewebsites.net/");
log.Info($"Access Token: {accessToken}");
return req.CreateResponse(new {token = accessToken});
}
catch(Exception ex) {
log.Error("Error", ex);
throw;
}
Yes, there is a way to do this. I'll explain at a high level, and then add an item to the MSI documentation backlog to write a proper tutorial for this.
What you want to do is follow this Azure AD authentication sample, but only configure and implement the parts for the TodoListService: https://github.com/Azure-Samples/active-directory-dotnet-daemon.
The role of the TodoListDaemon will be played by a Managed Service Identity instead. So you don't need to register the TodoListDaemon app in Azure AD as instructed in the readme. Just enable MSI on your VM/App Service/Function.
In your code client side code, when you make the call to MSI (on a VM or in a Function or App Service), supply the TodoListService's AppID URI as the resource parameter. MSI will fetch a token for that audience for you.
The code in the TodoListService example will show you how to validate that token when you receive it.
So essentially, what you want to do is register an App in Azure AD, give it an AppID URI, and use that AppID URI as the resource parameter when you make the call to MSI. Then validate the token you receive at your service/receiving side.
Please check that the resource id used "https://myapp-registration-westus-dev.azurewebsites.net/" is accurate. I followed steps here to setup Azure AD authentication, and used the same code as you, and was able to get a token.
https://learn.microsoft.com/en-us/azure/app-service/app-service-mobile-how-to-configure-active-directory-authentication
You can also run this code to check the exact error returned by MSI. Do post the error if it does not help resolve the issue.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
var response = await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), "https://myapp-registration-westus-dev.azurewebsites.net/", "2017-09-01"));
string msiResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
log.Info($"MSI Response: {msiResponse}");
Update:-
This project.json file and run.csx file work for me. Note: The project.json refers to .NET 4.6, and as per Azure Functions documentation (link in comments), .NET 4.6 is the only supported version as of now. You do not need to upload the referenced assembly again. Most probably, incorrect manual upload of netstandard assembly, instead of net452 is causing your issue.
Only the .NET Framework 4.6 is supported, so make sure that your
project.json file specifies net46 as shown here. When you upload a
project.json file, the runtime gets the packages and automatically
adds references to the package assemblies. You don't need to add #r
"AssemblyName" directives. To use the types defined in the NuGet
packages, add the required using statements to your run.csx file.
project.json
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.Azure.Services.AppAuthentication": "1.0.0-preview"
}
}
}
}
run.csx
using Microsoft.Azure.Services.AppAuthentication;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net/");
log.Info($"Access Token: {accessToken}");
return req.CreateResponse(new {token = accessToken});
}
catch(Exception ex)
{
log.Error("Error", ex);
throw;
}
}

SignalR self host connection issue

I recently created a proof of concept console application using SignalR (self host). It worked a treat for our use. The client connected fine and I was able to send updates from the server to the client. Lovely!
I've now transferred the code from the Console application to a winforms application for a prettier UI. Now that same client won't connect to the server yet it will still connect to the old Console version.
Winforms code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
// Let the app know the server is up
}
Console code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
Client connection code:
if (!connected)
{
int i = 0;
// Try 3 times
while (i <= 2)
{
try
{
string server = Properties.Settings.Default.Server + ":" + Properties.Settings.Default.PortNumber.ToString();
connection = new HubConnection(server);
connection.StateChanged += connection_StateChanged;
hub = connection.CreateHubProxy("MyHub");
connection.Start().Wait();
hub.On<string>("addMessage", param => { UpdateAlarmStatus(param); });
return true;
}
catch (Exception)
{
i++;
}
}
return false;
}
else
{
return true;
}
The error the client is reporting is:
Exception:Thrown: "No connection could be made because the target machine actively refused it" (System.Net.Sockets.SocketException)
A System.Net.Sockets.SocketException was thrown: "No connection could be made because the target machine actively refused it"
Time: 25/01/2015 15:09:23
Thread:Worker Thread[8232]
Why would the target machine (localhost) refuse itself which the Console version doesn't? I've been looking at the code over and over and I cannot see where I'm going wrong. Can anyone point me in the right direction please?
Thank you for reading.
Paul.
I suspect this is an issue with the configuration of your machine/infrastructure rather than the code itself, which looks fine at first glance.
Have you checked the console debug output in Visual Studio? I recently encountered an issue with similar symptoms and that was what gave me the initial clue to keep investigating. In my particular case, an exception was written to the console debug output that didn't make it to the client.
SignalR will normally negotiate with the server automatically to determine the best transport method to use. In a .NET client, the available options are LongPollingTransport, ServerSentEventsTransport and WebSocketTransport. So for some reason, your console app can use at least one of those methods, whereas your WinForms client cannot.
You can perhaps enable tracing to give you more information to work with. To do this, enter the below before you create the hub proxy:
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
ASP.NET doco on SignalR tracing

Resources