-
Notifications
You must be signed in to change notification settings - Fork 18
ASP.NET Core RethinkDB
RethinkDB is an open source distributed document-oriented database. As most NoSQL databases, it stores JSON documents with no required schema or table structure.
What makes RethinkDB great:
- horizontal scaling
- table replication and sharing
- automatic fail-over
- query language with join support, map-reduce and geospatial queries
- pub/sub for data changes
If you want a scalable, fault tolerant database system for your ASP.NET Core applications then you should consider using RethinkDB.
This guide shows you how to use RethinkDB from an ASP.NET Core and how to deploy your app along with a RethinkDB cluster using Docker for Windows and Docker Swarm mode. We will be using the TokenGen app from the Scale ASP.NET Core apps with Docker Swarm Mode guide and store the generated tokens along with the issuers in a RethinkDB database.
First we need to enable Swarm Mode and create a dedicated network for our RethinkDB cluster:
# initialize swarm
docker swarm init
# create RethinkDB overlay network
docker network create --driver overlay rdb-net
We start building our RethinkDB cluster by running a single RethinkDB server, we will remove this instance later on:
# create and start rethinkdb primary
docker service create --name rdb-primary --network rdb-net --replicas 1 rethinkdb:latest rethinkdb --bind all --no-http-admin
Now we can create a secondary RethinkDB node that will join the rdb-primary
node and form a cluster:
# create and start rethinkdb secondary
docker service create --name rdb-secondary --network rdb-net --replicas 1 rethinkdb:latest rethinkdb --bind all --no-http-admin --join rdb-primary
Scale the secondary node so we can have a minimum of 3 nodes needed for RethinkDB automatic fail-over mechanism:
# up 3 nodes (primary + two secondary) to enable automatic failover
docker service scale rdb-secondary=2
We now have a functional RethinkDB cluster, but we are not done yet.
Because we started the primary node without a join command, our cluster has a single point of failure.
If for some reason rdb-primary
container crashes, the Docker Swarm engine will recreate and start this container, but he can't join the existing cluster. If we start new rdb-secondary
instances, they will join the new rdb-primary
container and form another cluster.
To resolve this issue we have to remove the rdb-primary
service and recreate it with the join
command like so:
# remove primary
docker service rm rdb-primary
# recreate primary with --join flag
docker service create --name rdb-primary --network rdb-net --replicas 1 rethinkdb:latest rethinkdb --bind all --no-http-admin --join rdb-secondary
Now we can also scale the primary node:
# start two rdb-primary instances
docker service scale rdb-primary=2
At this point we have 4 nodes in our cluster, two rdb-primary
and two rdb-secondary
. We can further scale any of these two services and they will all join our cluster. If a rdb-primary
or rdb-secondary
instance crashes, the Docker Swarm will automatically start another container that will join our current cluster.
Last step is to create a RethinkDB proxy node, we expose port 8080 for the web admin and port 28015 so we can connect to the cluster from our app:
# create and start rethinkdb proxy
docker service create --name rdb-proxy --network rdb-net --publish 8080:8080 --publish 28015:28015 rethinkdb:latest rethinkdb proxy --bind all --join rdb-primary
Open a browser and navigate to http://localhost:8080
to check the cluster state. In the servers page you should see 4 servers connected to the cluster.
Open the TokenGen project from the previous guide and install the RethinkDB driver NuGet package:
Install-Package RethinkDb.Driver
We are going to create an object that holds the RethinkDB cluster address. Add a class named RethinkDbOptions
inside TokenGen project:
public class RethinkDbOptions
{
public string Host { get; set; }
public int Port { get; set; }
public string Database { get; set; }
public int Timeout { get; set; }
}
We will be storing the connection data in appconfig.json
and use a RethinkDbOptions
object to inject the data into RethinkDbConnectionFactory
.
The RethinkDbConnectionFactory
will provide a persistent connection to the RethinkDB cluster for our app:
public class RethinkDbConnectionFactory : IRethinkDbConnectionFactory
{
private static RethinkDB R = RethinkDB.R;
private Connection conn;
private RethinkDbOptions _options;
public RethinkDbConnectionFactory(IOptions<RethinkDbOptions> options)
{
_options = options.Value;
}
public Connection CreateConnection()
{
if (conn == null)
{
conn = R.Connection()
.Hostname(_options.Host)
.Port(_options.Port)
.Timeout(_options.Timeout)
.Connect();
}
if(!conn.Open)
{
conn.Reconnect();
}
return conn;
}
public void CloseConnection()
{
if (conn != null && conn.Open)
{
conn.Close(false);
}
}
public RethinkDbOptions GetOptions()
{
return _options;
}
}
Open appsettings.json
and add the RethinkDB cluster connection data:
"RethinkDbDev": {
"Host": "localhost",
"Port": 28015,
"Timeout": 10,
"Database": "TokenStore"
}
Now we can create a singleton instance of RethinkDbConnectionFactory
in Startup.cs
. You should reuse the same connection across whole application since the RethinkDb
connection is thread safe.
public void ConfigureServices(IServiceCollection services)
{
//....
services.Configure<RethinkDbOptions>(Configuration.GetSection("RethinkDbDev"));
services.AddSingleton<IRethinkDbConnectionFactory, RethinkDbConnectionFactory>();
}
Test the connection inside Startup.Configure
method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
IRethinkDbConnectionFactory connectionFactory)
{
//....
var con = connectionFactory.CreateConnection();
con.CheckOpen();
}
Copyright Stefan Prodan