Eventing

The .NET Driver core provides a robust model for event publication and subscription. Each event is represented by a class or struct which contains all the information related to the particular event.

ClusterBuilder

The ClusterBuilder class contains two methods for subscribing to events.

IEventSubscriber

The first version of Subscribe takes an IEventSubscriber. IEventSubscriber contains a single method to implement, TryGetEventHandler. It takes a generic parameter indicating the type of event and sets an out parameter with the handler.

Note
This method will be invoked once on each subscriber per event type. Therefore, performance of this method is not critical.

For instance, to handle the ConnectionPoolAddedConnectionEvent, the following could be done:

public class MyEventSubscriber : IEventSubscriber
{
    public bool TryGetEventHandler<TEvent>(out Action<TEvent> handler)
    {
        if(typeof(TEvent)) == typeof(ConnectionPoolAddedConnectionEvent))
        {
            handler = (Action<TEvent>)HandleConnectionPoolAddedConnectionEvent;
            return true;
        }

        handler = null;
        return false;
    }

    private void HandleConnectionPoolAddedConnectionEvent(ConnectionPoolAddedConnectionEvent @event)
    {
        Console.WriteLine("Added a connection to the pool.");
    }
}

This could quickly become unmaintainable with multiple events. To make this easier, we have implemented the ReflectionEventSubscriber. It uses reflection to find all the event handler methods inside a class based on certain constructor parameters for the method name and the binding flags. For instance, we could change the above MyEventSubscriber class as follows:

public class MyEventSubscriber : IEventSubscriber
{
    private readonly IEventSubscriber _subscriber;

    public MyEventSubscriber()
    {
        _subscriber = new ReflectionEventSubscriber(this);
    }

    public bool TryGetEventHandler<TEvent>(out Action<TEvent> handler)
    {
        return _subscriber.TryGetEventHandler(out handler);
    }

    private void Handle(ConnectionPoolAddedConnectionEvent @event)
    {
        Console.WriteLine("Added a connection to the pool.");
    }

    private void Handle(ConnectionPoolRemovedConnectionEvent @event)
    {
        Console.WriteLine("Removed a connection from the pool.");
    }
}

Note
The default method name is “Handle” and the default binding flags are for public instance methods.

The PerformanceCounterEventSubscriber is a good example of utilizing the ReflectionEventSubscriber.

Method

The second version of Subscribe takes an Action<TEvent>. For example, to use a static method:

static void Main() 
{
    var builder = new ClusterBuilder();

    builder.Subscribe<ConnectionPoolAddedConnectionEvent>(Handle);

    // ... snip
}

private static void Handle(ConnectionPoolAddedConnectionEvent @event)
{
    Console.WriteLine("Added a connection to the pool.");
}

Alternatively, a lambda expression could be used:

static void Main() 
{
    var builder = new ClusterBuilder();

    builder.Subscribe<ConnectionPoolAddedConnectionEvent>(x => Console.WriteLine("Added a connection to the pool."));

    // ... snip
}

Operation Ids

Any commands that could occur based on user initiation will contain an operation identifier. This identifer can be used to link together all events that occurred due to the user initiated action.

ClusterId, ServerId, and ConnectionId

All events will contain at least one of these identifiers. They can be used to uniquely attribute a particular event to a cluster, a server, or a connection. In addition, the ConnectionId also contains a local value and a server value where the server value contains the same value that will show up in the server logs for its connection logging.

Command Events

There are three events related to monitoring data sent on the wire. These are the CommandStartedEvent, the CommandSucceededEvent, and the CommandFailedEvent. For every started event, there will always be a succeeded or failed event.

In addition, any messages sent to the server that are not already commands will be upconverted for the sake of consumption. For instance, an OP_DELETE wire protocol message on server 2.4 will appear as though it were a delete command.

warning
These are heavy events to generate. Do not subscribe to these unless they provide necessary information.

Note

Certain information has been removed for security reasons. For instance, the authenticate command will not contain the actual command or its reply. However, it will still contain the command name itself.

CommandStartedEvent

The CommandStartedEvent contains, amongst other information, the command name as well as the command itself. While the command also contains the command name, the command is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the command should be pulled out and used immediately or stored.

CommandSucceededEvent

The CommandSucceededEvent contains, amongst other information, the command name, the duration of the command, and the reply. The reply is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the reply should be pulled out and used immediately or stored.

CommandFailedEvent

The CommandFailedEvent contains, amongst other information, the command name, the duration of the command, and the exception. The exception is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the exception should be pulled out and used immediately or stored.

SDAM Events

There are a number of events that are raised as part of Server Discovery and Monitoring (SDAM). See SDAM Events for more information.