Node metadata

Quick overview

session.getMetadata().getNodes(): all nodes known to the driver (even if not actively connected).


Metadata#getNodes returns all the nodes known to the driver when the metadata was retrieved; this includes down and ignored nodes (see below), so the fact that a node is in this list does not necessarily mean that the driver is connected to it.

Map<InetSocketAddress, Node> nodes = session.getMetadata().getNodes();
System.out.println("Nodes in the cluster:");
for (Node node : nodes.values()) {
  System.out.printf(
      "  %s is %s and %s (%d connections)%n",
      node.getConnectAddress().getAddress(),
      node.getState(),
      node.getDistance(),
      node.getOpenConnections());
}

The returned map is immutable: it does not reflect additions or removals since the metadata was retrieved. On the other hand, the Node object is mutable; you can hold onto an instance across metadata refreshes and see updates to the fields.

A few notable fields are explained below; for the full details, refer to the Javadocs.

Node#getState() indicates how the driver sees the node (see the Javadocs of NodeState for the list of possible states with detailed explanations). In general, the driver tries to be resilient to spurious DOWN notifications, and will try to use a node as long as it seems up, even if some events seem to indicate otherwise: for example, if the Cassandra gossip detects a node as down because of cross-node connectivity issues, but the driver still has active connections to that node, the node will stay up. Two related properties are Node#getOpenConnections() and Node#isReconnecting().

Node#getDatacenter() and Node#getRack() represent the location of the node. This information is used by some load balancing policies to prioritize coordinators that are physically close to the client.

Node#getDistance() is set by the load balancing policy. The driver does not connect to IGNORED nodes. The exact definition of LOCAL and REMOTE is left to the interpretation of each policy, but in general it represents the proximity to the client, and LOCAL nodes will be prioritized as coordinators. They also influence pooling options.

Node#getExtras() contains additional free-form properties. This is intended for future evolution or custom driver extensions. In particular, if the driver is connected to Datastax Enterprise, the map will contain additional information under the keys defined in DseNodeProperties:

Object rawDseVersion = node.getExtras().get(DseNodeProperties.DSE_VERSION);
Version dseVersion = (rawDseVersion == null) ? null : (Version) rawDseVersion;

Notifications

If you need to follow node state changes, you don’t need to poll the metadata manually; instead, you can register one or more listeners to get notified when changes occur:

NodeStateListener listener =
    new NodeStateListenerBase() {
      @Override
      public void onUp(@NonNull Node node) {
        System.out.printf("%s went UP%n", node);
      }
    };
CqlSession session = CqlSession.builder()
    .addNodeStateListener(listener)
    .build();

See NodeStateListener for the list of available methods. NodeStateListenerBase is a convenience implementation with empty methods, for when you only need to override a few of them.

It is also possible to register one or more listeners via the configuration:

datastax-java-driver {
  advanced {
    node-state-listener.classes = [com.example.app.MyNodeStateListener1,com.example.app.MyNodeStateListener2]
  }
}

Listeners registered via configuration will be instantiated with reflection; they must have a public constructor taking a DriverContext argument.

The two registration methods (programmatic and via the configuration) can be used simultaneously.

Advanced topics

Forcing a node down

It is possible to temporarily or permanently close all connections to a node and disable reconnection. The driver does that internally for certain unrecoverable errors (such as a protocol version mismatch), but this could also be useful for maintenance, or for a custom component (load balancing policy, etc).

import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.metadata.TopologyEvent;

InternalDriverContext context = (InternalDriverContext) session.getContext();
context.getEventBus().fire(TopologyEvent.forceDown(node1.getConnectAddress()));
context.getEventBus().fire(TopologyEvent.forceUp(node1.getConnectAddress()));

As shown by the imports above, forcing a node down requires the internal driver API, which is reserved for expert usage and subject to the disclaimers in API conventions.

Using a custom topology monitor

By default, the driver relies on Cassandra’s gossip protocol to receive notifications about the node states. It opens a control connection to one of the nodes, and registers for server-sent state events.

Some organizations have their own way of monitoring Cassandra nodes, and prefer to use it instead. It is possible to completely override the default behavior to bypass gossip. The full details are beyond the scope of this document; if you’re interested, study the TopologyMonitor interface in the source code.