Database

The system uses a SQL database to store meta data and queue messages. At the time of writing we support SQLite, Postgres and SQL Server. Feel free to reach out to us if you need support for another database engine like MySQL or MariaDB.

Configuring which database to use

The support for the different databases exists in their own NuGet packages:

CommerceMind.Nexus.Postgres
CommerceMind.Nexus.SqlServer
CommerceMind.Nexus.Sqlite

Install the package(s) you need before you initialize it below.

After you've installed the db package(s) you open your Program.cs and go to these lines:

builder.Services
    .AddNexus()
    .AddFunctions()
    .AddScheduledJobs()
    .AddQueues()
    // Either:
    .AddPostgresConnection()
    // Or:
    .AddSqlServerConnection()
    // Or:
    .AddSqliteConnection()

    // This should be the last thing you do, after you've finished configuring Nexus
    .Build();

By default the system will call IConfiguration.GetConnectionString("postgres"), IConfiguration.GetConnectionString("sqlserver") or IConfiguration.GetConnectionString("sqlite") to get the connection string to the database. If that doesn't work for you can register a lamda to provide your connection string like this:

builder.Services.AddNexus().AddPostgresConnection(options =>
{
    options.ConnectionStringLoader = serviceProvider =>
    {
        // Find and return your connection string here
    };
});

// Or:
builder.Services.AddNexus().AddSqlServerConnection(options =>
{
    options.ConnectionStringLoader = serviceProvider =>
    {
        // Find and return your connection string here
    };
});

// Or:
builder.Services.AddNexus().AddSqliteConnection(options =>
{
    options.ConnectionStringLoader = serviceProvider =>
    {
        // Find and return your connection string here
    };
});

Using a ConnectionStringLoader

When you use ConnectionStringLoader you need to make sure that it's a very fast operation as the loader will be called every time Nexus runs a database query. You need to make sure to cache the value if you fetch it from an external store such as Azure KeyVault.

Controlling the connection

By default Nexus will open and close database connections for almost every query, and rely on the underlying connection pooling. In some cases you might want to have more control over the connection such as if you want to include the Nexus queries inside a transaction.

One option is to use a DbConnectionScope like this:

public async Task RunAsync(IEnqueuer<SomeMessage> enqueuer)
{
    var dbConnection = CreateDbConnection();

    using (var transaction = connection.BeginTransaction())
    using (var _ = new DbConnectionScope(connection, transaction))
    {
        // The enqueuer will now use dbConnection to run the database queries to enqueue the message
        await enqueuer.EnqueueAsync(new SomeMessage());

        transaction.Commit();
    }

    // This message will be enqueued using the default way with a new connection
    await enqueuer.EnqueueAsync(new SomeMessage());
}

The effect of this is that any Nexus services inside the using block will use the dbConnection object rather than creating new connections. Which means that you can start a transaction in dbConnection and automatically have all Nexus operations inside the using block be a part of that transaction.

Transient errors

Nexus has built-in retries of transient errors using Polly but if you want to you can add custom retry logic by creating your own implementation of the ITransientDatabaseErrorRetryer interface.

Database maintenance

Nexus includes two jobs that are enabled by default. One job is called RebuildDatabaseIndexes and will rebuild database indexes for Postgres and SQL Server. It's scheduled to run daily at midnight. If you're using SQLite this job won't do anything as it's not needed to rebuild SQLite indexes.

By default this job will only rebuild indexes in tables created by Nexus. If you want it to rebuild indexes for all tables in the database you can can pass an option to AddNexus().AddPostgresConnection() or AddNexus().AddSqlServerConnection():

builder.Services.AddNexus().AddPostgresConnection(options =>
{
    options.LimitDatabaseMaintenanceToNexusTables = false;
});

// Or:
builder.Services.AddNexus().AddSqlServerConnection(options =>
{
    options.LimitDatabaseMaintenanceToNexusTables = false;
});

The other job is called RunDatabaseMaintenanceJob and is also enabled by default and but not scheduled. For Postgres that will run VACUUM and then ANALYZE. If you're using SQL Server this job won't do anything as SQL Server has automatic update of query statistics. If you're using SQLite this job will not do anything either.

Remove database maintenance jobs

If you want to completely hide these jobs you can pass a filter lambda when you register the jobs system to exclude these jobs like this:

builder.Services.AddNexus().AddScheduledJobs(options =>
{
    options.ScheduledJobFilter = jobType => jobType.Name != "RebuildDatabaseIndexes" && jobType.Name != "RunDatabaseMaintenanceJob";
});

If you want to run the jobs but do something different than the defaults you can create your own implementation of the IDatabaseMaintenanceService interface and register it with the service provider like this:

builder.Services.AddSingleton<IDatabaseMaintenanceService, MyDatabaseMaintenanceService>();

Using another database engine

If you want to use another database engine such as MySQL or MariaDB you can always implement it and open a PR to the Github repository. If you don't have access yet to the repository just ping us! Another option is of course to hire us to implement it for you.