Date:

Share:

Health Checks in .NET: 2 ways to check communication with MongoDB

Related Articles

In any complex system, you have to deal with external dependencies.

More often than not, if one of the external systems (database, other API, or authentication provider) is down, the entire system may be affected.

In this article, we will learn what health checks They are, how to create custom and how to check if the MongoDB instance is reachable or not.

What are health checks?

A health check is a special type of HTTP endpoint that allows you to understand the state of the system – well, it’s a check of the health of the entire system, including external dependencies.

You can use it to understand if the application itself and all its dependencies are healthy and responsive in a reasonable amount of time.

These endpoints are also useful for humans, but yes Even more useful for tools that monitor the application and can fix automatically Some problems if occur – for example, they can restart the application if it is in a damaged state.

How to add health checks in dotNET

Fortunately, .NET already comes with Health Check capabilities, so we can simply follow the existing standard without reinventing the wheel.

For the purpose of this article, I created a simple .NET API implementation.

turned to the Program class – or, in general, wherever you define the application – and add this line:

builder.Services.AddHealthChecks();

And then, after var app = builder.Build();You need to add the following line for the health checks to show up under /healtz path.

app.MapHealthChecks("/healthz");

In summary, the minimum structure should be:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/healthz");

app.MapControllers();

app.Run();

So if you run the application and navigate to /healthYou’ll just see an almost empty page with two properties:

  • The status code is 200;
  • The only printed result is healthy

Obviously that’s not enough for us.

How to create a custom health check class in .NET

Each project has its own dependencies and requirements. We should be able to build custom health checks and add them to our endpoint.

It’s just a matter of creating a new class that implements IHealthCheckAn interface that lives below the Microsoft.Extensions.Diagnostics.HealthChecks namespace.

Next, you need to implement the method that tells us if the system under test is healthy or degraded:

Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default);

The method returns an HealthCheckResultwhich is a structure that can include these values:

  • healthy: Everything is fine;
  • humiliated: The application is running, but taking too long to respond;
  • sick: The application is offline, or an error occurred while performing the test.

So, for example, we build a customized department of health check such as:

public class MyCustomHealthCheck : IHealthCheck
{
    private readonly IExternalDependency _dependency;

    public MyCustomHealthCheck(IExternalDependency dependency)
    {
        _dependency = dependency;
    }

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var isHealthy = _dependency.IsHealthy();

        if (isHealthy)
        {
            return Task.FromResult(HealthCheckResult.Healthy());
        }
        return Task.FromResult(HealthCheckResult.Unhealthy());
    }
}

And finally, add it to the Startup class:

builder.Services.AddHealthChecks()
    .AddCheck<MyCustomHealthCheck>("A custom name");

Now, you can create an implicit class that implements IExternalDependency Have fun with the different types of results. In fact, if we create and inject a dummy class like this:

public class StubExternalDependency : IExternalDependency
{
    public bool IsHealthy() => false;
}

And we run the application, we can see that the final result of the application is sick.

A question for you: Why should we specify a name for sanity checks, such as “custom name”? Leave a comment below 📩

Added a custom health check provider for MongoDB

Now we can create a custom health check for MongoDB.

Of course, we will need to use the library to access Mongo: so just install via NuGet the package MongoDB.Driver – We have already used this library in a previous article.

Then, you can create a class like this:

public class MongoCustomHealthCheck : IHealthCheck
{
    private readonly IConfiguration _configurations;
    private readonly ILogger<MongoCustomHealthCheck> _logger;

    public MongoCustomHealthCheck(IConfiguration configurations, ILogger<MongoCustomHealthCheck> logger)
    {
        _configurations = configurations;
        _logger = logger;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        try
        {
            await MongoCheck();
            return HealthCheckResult.Healthy();
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy();
        }
    }

    private async Task IsMongoHealthy()
    {
        string connectionString = _configurations.GetConnectionString("MongoDB");
        MongoUrl url = new MongoUrl(connectionString);

        IMongoDatabase dbInstance = new MongoClient(url)
            .GetDatabase(url.DatabaseName)
            .WithReadPreference(new ReadPreference(ReadPreferenceMode.Secondary));

        _ = await dbInstance.RunCommandAsync<BsonDocument>(new BsonDocument { { "ping", 1 } });
    }
}

As you can see, this is nothing more than a generic class with some services injected into the constructor.

The central part is the IsMongoHealthy Method: Here we access the DB instance. Let’s take a closer look at it.

How to ping a MongoDB instance

Here it is again IsMongoHealthy method.

string connectionString = _configurations.GetConnectionString("MongoDB");
MongoUrl url = new MongoUrl(connectionString);

IMongoDatabase dbInstance = new MongoClient(url)
    .GetDatabase(url.DatabaseName)
    .WithReadPreference(new ReadPreference(ReadPreferenceMode.Secondary));

_ = await dbInstance.RunCommandAsync<BsonDocument>(new BsonDocument { { "ping", 1 } });

Obviously we create a reference to a specific DB instance: new MongoClient(url).GetDatabase(url.DatabaseName). Note that we require access to the secondary node, to avoid performing operations on the primary node.

After that, we send the Ping command: dbInstance.RunCommandAsync<BsonDocument>(new BsonDocument { { "ping", 1 } }).

now what? God Ping The command returns an object in the following form:

Or, if the command cannot be executed, it throws a System.TimeoutException.

MongoDB health checks with AspNetCore.Diagnostics.HealthChecks

If we don’t want to write such things ourselves, we can rely on existing libraries.

AspNetCore.Diagnostics.HealthChecks is a library you can find on GitHub that automatically handles several types of health checks for .NET applications.

It noted This library is not maintained or supported by Microsoft – but it appears in the official .NET documentation.

This library exposes several NuGet packages for dozens of different dependencies you might want to consider in your health checks. For example, we have Azure.IoTHub, CosmosDb, Elasticsearch, Gremlin, SendGridand many more.

Obviously, we’re going to use this for MongoDB. It’s pretty easy.

First, you need to install the AspNetCore.HealthChecks.MongoDb NuGet package.


Then, you just need to add a line of code to the initial setup:

builder.Services.AddHealthChecks()
    .AddMongoDb(mongodbConnectionString: builder.Configuration.GetConnectionString("MongoDB"))

that’s it! Neat and easy! 😎

Why do we even want a custom provider?

Ok, if we can just add a line of code instead of creating a whole new class, why should we bother creating the whole custom class?

There are several reasons to create a custom supplier:

  1. You want more control over DB access: for example, you want to ping only secondary nodes, as we did before;
  2. You don’t just want to check if the DB is running, but also the performance of performing specific operations, such as retrieving all documents from a particular collection.

But, yes, in general, you can just use the NuGet package that we used in the previous section, and you’re good to go.

Additional readings

As usual, the best way to learn more about a topic is by reading the official documentation:

🔗 Validity tests in ASP.NET Core | Microsoft Docs

How can you use MongoDB locally? Well, easy: *with Docker!

🔗 Getting started with Docker: Download and run MongoDB locally | Code4IT

As we have seen, we can perform a PING operation on a MongoDB instance.

🔗 ping command | MongoDB

This article first appeared on Code4IT 🐧

Finally, here’s the link to the GitHub repo with the health check list:

🔗 AspNetCore.Diagnostics.HealthChecks | GitHub

And if you want to take a look at the MongoDB implementation, you can read the code here:

🔗 MongoDbHealthCheck.cs | GitHub

finishing

In this article, we learned two ways to implement health checks for a MongoDB connection.

You can use a pre-existing NuGet package, or you can write a custom package yourself. It all depends on your use cases.

I hope you enjoyed this article! Let’s keep in touch Twitter or LinkedIn, If you want! 🤜🤛

Happy coding!

🐧

.

Source

Popular Articles