Wednesday 23 June 2021

Using Nats Streams with .Net

 

The modern approach when you need to get some data from the server to the remote client that that server notifies the client and sends the data to the client without the need from the client to request every second "Do I have something to handle".
The process of receiving the data is called "Subscription".
There are many free and commercial services that provide this functionality out of the box. Today I am going to talk about Nats server. Or more correctly about Nats streams.
Nats server is just Publish/Subscribe server. It means that once you place a message into it, the server will send this message to every client which is subscribed to the server channel. IF some client was down at the point when the server sent the data to the client, the client doesn't get it.
The server also doesn't store the data. It means there is no persistency and no option to recover the data on the server-side.
All the problems from above are solved with Nats Streams. It does has the persistence , it can store the data in memory or file or Db. It does provide an access to historical data.
Now, while Nats Streams is also a server, it is actually a client to Nats server. So it this example I will install both server Nats and Nats Streams. 
The installation will take place on Windows machines.
I will use TLS to enable SSL comunication
To start, download both Nats and Nats Streams 


As I mentioned I will install on Windows but since Nats is written by using "Go" it can be installed also on Linux or on Docker.

I also will use the certificate I have from "GoDaddy" to set the server for TLS. (You can use also a self-signed certificate as described here).
The problem is that my certificate is pfx file but I need to have a key and certificate file separately.
 I will use openssl (for windows ) to convert the pfx to formats that I need.
Use the following commands
openssl pkcs12 -in [yourfile.pfx] -nocerts -out [yourfile.key]
openssl pkcs12 -in [yourfile.pfx] -clcerts -nokeys -out [yourcertificate.crt]
openssl rsa -in [drlive.key] -out [yourdecryptedkey.key]

Note that you need only "yourcertificate.crt" and "yourdecryptedkey.key"

To start the Nats with TLS support run the following command.
I created to directories "Nats" and "NatsStream" and places each server to the relevant directory. 
Also I placed the "crt" and "key" file to the NatsStream directory

D:\Nats\nats-server.exe -DV --tlsverify --tlscert=D:\NatsStream\yourcertificate.crt --tlskey=D:\NatsStream\yourdecryptedkey.key

Assuming that the server URL is myhost.mydomain.com the server will be started at myhost.mydomain.com:4222 (which is the default port)

To start the NatsStream run the following command. As I mentioned Nats stream is the client for Nats so you need to specify the certificates for the server and for the client

D:\NatsStream\nats-streaming-server.exe -m 8222 -a myhost.mydomain.com --tls_client_cert D:\NatsStream\yourcertificate.crt --tls_client_key D:\NatsStream\yourdecryptedkey.key  --tlscert D:\NatsStream\yourcertificate.crt --tlskey D:\NatsStream\yourdecryptedkey.key -nats_server nats://myhost.mydomain.com:4222

The command is a little bit long and I will explain it.
All the certificate flags are related to the TLS configuration. 
-m 8222 will start some basic monitoring service that is able to provide very basic information about channels and messages which are passed through the server. After the server is started you can access it on HTTP://myhost.mydomain.com:8222.
-nats_server nats://myhost.mydomain.com:4222. As I mentioned Nats Streams is only a client to Nats server, so you need to specify the location of the Nats server.
-a myhost.mydomain.com bind to host address

You can learn more about command-line options here

Now we are ready to start the development.
You will need to packages
Nats client from here and Nats Streams client from here. Those are also available as Nuget packages.

Next, we will write 2 simple programs for the publisher and subscriber.

The programs are very straightforward and based on the examples from the github links above. I am not going to describe every line of code.
I did struggle a bit with SSL so I will focus on the configuration

You do need to load the certificate on the client side. Not the key or crt file, but the original pfx file





Also if you are less concerned with validating the certificate security, you can just always return "true" from the following function. In my case I was interested to see that there is no TLS errors.


After execution of the program you should see the following output (of course you need to modify the server URL and certificate location and password in the code)


Attached the project files here



Using isolation level with MSSQL and ADO.Net

 We had a task to start the transaction in Entity Framework Core Middleware and after await _next(context); commit the transaction. In the middle of the process there is an insert into the table and a call to "SaveChanges"


It looks like a very easy task. Unfortunately, it doesn't work. Or should I say it doesn't work out of the box as I expect it. As I mentioned I do an insert into some table. Let's call it "SF". If after "SaveChanges" there is some long-running process and it takes about a minute before we commit the transaction, the "SF" table is blocked also for select!!!

I worked a lot with Oracle DB and this sounds like nonsense for me that select statements can be blocked, but what can I say about the MSSQL database: "Ooops ... we did it again".

The core of the problem is the default isolation level of the database

To demonstrate the problem I create a small program that I will attach to the post.

Here is the snapshot of the code



Note that I put "wait" of 1 minute to get a chance to show you the problem. 

When "ExecuteNonQuery" executed meaning that 1 record is inserted into the DB, do a simple select from the table




You will see that "Executing Query" never ends. Not something you expect , because you do expect that select statement will always bring you only committed data.

This is related to the DB isolation level. You are most invited to read about it, but I will focus on how to solve it.

The solution is very easy.

Run on your DB

ALTER DATABASE YOUR_DB SET READ_COMMITTED_SNAPSHOT ON 

WITH ROLLBACK IMMEDIATE;

It will force the DB to return you only the committed snapshot of the data.

Project data is here