===============
SDAM Monitoring
===============

The Node.js driver features SDAM Monitoring events,
allowing an application or tool to monitor changes in the drivers
view of a single server, replica set or ``mongos``. This allows an
application to react to changes of topology, such as a secondary
joining or leaving a replica set.

Overview of SDAM events
-----------------------

.. list-table::
   :header-rows: 1

   * - Event
     - Applies To
     - Description
   * - serverOpening
     - Server, Replicaset, Mongos
     - Emitted when server connection is established.
   * - serverClosed
     - Server, Replicaset, Mongos
     - Emitted when server connection gets closed.
   * - serverDescriptionChanged
     - Server, Replicaset, Mongos
     - Emitted when server state changes (such as from secondary to primary).
   * - topologyOpening
     - Server, Replicaset, Mongos
     - Emitted before any server connections are performed.
   * - topologyClosed
     - Server, Replicaset, Mongos
     - Emitted when topology connections have all closed.
   * - topologyDescriptionChanged
     - Replicaset, Mongos
     - Emitted when the topology shape changes, such as a new primary being elected or a mongos proxy disconnecting.
   * - serverHeartbeatStarted
     - Replicaset, Mongos
     - Emitted before the ismaster command is issued to a MongoDB server.
   * - serverHeartbeatSucceeded
     - Replicaset, Mongos
     - Emitted after a successful ismaster command was issued to a MongoDB server.
   * - serverHeartbeatFailed
     - Replicaset, Mongos
     - Emitted if a ismaster command failed against a specific MongoDB server.


Simple Code Example
-------------------

The following example demonstrates how to connect to a replica set and monitor all the events that are emitted by the replica set topology.

.. code-block:: js

   const { MongoClient } = require('mongodb');

   const url = 'mongodb://localhost:31000,localhost:31001/?replicaSet=rs';
   const client = new MongoClient(url);

   client.on('serverDescriptionChanged', event => {
     console.log('received serverDescriptionChanged');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('serverHeartbeatStarted', event => {
     console.log('received serverHeartbeatStarted');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('serverHeartbeatSucceeded', event => {
     console.log('received serverHeartbeatSucceeded');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('serverHeartbeatFailed', event => {
     console.log('received serverHeartbeatFailed');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('serverOpening', event => {
     console.log('received serverOpening');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('serverClosed', event => {
     console.log('received serverClosed');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('topologyOpening', event => {
     console.log('received topologyOpening');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('topologyClosed', event => {
     console.log('received topologyClosed');
     console.log(JSON.stringify(event, null, 2));
   });

   client.on('topologyDescriptionChanged', event => {
     console.log('received topologyDescriptionChanged');
     console.log(JSON.stringify(event, null, 2));
   });

   async function run() {
     try {
       await client.connect();
       console.log('successfully connected');
     } finally {
       await client.close();
     }
   }

   run();

Example Documents Returned For Each Event Type
----------------------------------------------

The following examples serve as a guide to the format of the returned documents.

serverDescriptionChanged
^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: js

  ServerDescriptionChangedEvent {
     topologyId: 0,
     address: 'localhost:27017',
     previousDescription: ServerDescription {
       address: 'localhost:27017',
       error: null,
       roundTripTime: 0,
       lastUpdateTime: 1571251089030,
       lastWriteDate: null,
       opTime: null,
       type: 'Unknown',
       minWireVersion: 0,
       maxWireVersion: 0,
       hosts: [],
       passives: [],
       arbiters: [],
       tags: []
     },
     newDescription: ServerDescription {
       address: 'localhost:27017',
       error: null,
       roundTripTime: 0,
       lastUpdateTime: 1571251089051,
       lastWriteDate: 2019-10-16T18:38:07.000Z,
       opTime: { ts: Timestamp, t: 18 },
       type: 'RSPrimary',
       minWireVersion: 0,
       maxWireVersion: 7,
       maxBsonObjectSize: 16777216,
       maxMessageSizeBytes: 48000000,
       maxWriteBatchSize: 100000,
       me: 'localhost:27017',
       hosts: [ 'localhost:27017' ],
       passives: [],
       arbiters: [],
       tags: [],
       setName: 'rs',
       setVersion: 1,
       electionId: ObjectID,
       primary: 'localhost:27017',
       logicalSessionTimeoutMinutes: 30,
       '$clusterTime': ClusterTime
     }
   }

The ``type`` of the ``ServerDescription`` can be one of the following values:

.. list-table::
   :header-rows: 1

   * - Type
     - Description
   * - Unknown
     - Unknown server
   * - Standalone
     - Standalone server
   * - Mongos
     - Mongos proxy
   * - PossiblePrimary
     - Not checked yet, but another server thinks this is a primary
   * - RSPrimary
     - Primary server
   * - RSSecondary
     - Secondary server
   * - RSArbiter
     - Arbiter
   * - RSOther
     - See `the spec <https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#rsghost-and-rsother>`_ for more details
   * - RSGhost
     - See `the spec <https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#rsghost-and-rsother>`_ for more details

serverHeartbeatStarted
^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: js

   ServerHeartbeatStartedEvent {
     connectionId: 'localhost:27017'
   }

serverHeartbeatSucceeded
^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: js

   ServerHeartbeatSucceededEvent {
     duration: 1.939997,
     reply:{
       hosts: [ 'localhost:27017' ],
       setName: 'rs',
       setVersion: 1,
       ismaster: true,
       secondary: false,
       primary: 'localhost:27017',
       me: 'localhost:27017',
       electionId: ObjectID,
       lastWrite: {
         opTime: { ts: [Timestamp], t: 18 },
         lastWriteDate: 2019-10-16T18:38:17.000Z,
         majorityOpTime: { ts: [Timestamp], t: 18 },
         majorityWriteDate: 2019-10-16T18:38:17.000Z
       },
       maxBsonObjectSize: 16777216,
       maxMessageSizeBytes: 48000000,
       maxWriteBatchSize: 100000,
       localTime: 2019-10-16T18:38:19.589Z,
       logicalSessionTimeoutMinutes: 30,
       minWireVersion: 0,
       maxWireVersion: 7,
       readOnly: false,
       ok: 1,
       operationTime: Timestamp,
       '$clusterTime': ClusterTime
     },
     connectionId: 'localhost:27017'
   }

serverHeartbeatFailed
^^^^^^^^^^^^^^^^^^^^^

.. code-block:: js

   ServerHeartbeatFailed {
     duration: 20,
     failure: MongoError('some error'),
     connectionId: 'localhost:27017'
   }

serverOpening
^^^^^^^^^^^^^

.. code-block:: js

   ServerOpeningEvent {
     topologyId: 0,
     address: 'localhost:27017'
   }

serverClosed
^^^^^^^^^^^^

.. code-block:: js

   ServerClosedEvent {
     topologyId: 0,
     address: 'localhost:27017' 
   }

topologyOpening
^^^^^^^^^^^^^^^

.. code-block:: js

   TopologyOpeningEvent {
     topologyId: 0
   }

topologyClosed
^^^^^^^^^^^^^^

.. code-block:: js

   TopologyClosedEvent {
     topologyId: 0
   }

topologyDescriptionChanged
^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: js

   TopologyDescriptionChangedEvent {
     topologyId: 0,
     previousDescription: TopologyDescription {
       type: 'ReplicaSetNoPrimary',
       setName: null,
       maxSetVersion: null,
       maxElectionId: null,
       servers: Map {
         'localhost:27017' => ServerDescription
       },
       stale: false,
       compatible: true,
       compatibilityError: null,
       logicalSessionTimeoutMinutes: null,
       heartbeatFrequencyMS: 10000,
       localThresholdMS: 15,
       options: Object,
       error: undefined,
       commonWireVersion: null
     },
     newDescription: TopologyDescription {
       type: 'ReplicaSetWithPrimary',
       setName: 'rs',
       maxSetVersion: 1,
       maxElectionId: null,
       servers: Map {
         'localhost:27017' => ServerDescription
       },
       stale: false,
       compatible: true,
       compatibilityError: null,
       logicalSessionTimeoutMinutes: 30,
       heartbeatFrequencyMS: 10000,
       localThresholdMS: 15,
       options: Object,
       error: undefined,
       commonWireVersion: 7
     }
   }
     

The ``type`` field can be one of the following values:

.. list-table::
   :header-rows: 1

   * - Type
     - Description
   * - Single
     - A single standalone server
   * - ReplicaSetWithPrimary
     - Replica set with a primary
   * - ReplicaSetNoPrimary
     - Replica set with no primary
   * - Sharded
     - A sharded cluster
   * - Unknown
     - Unknown topology
