All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.driver.core.Host Maven / Gradle / Ivy

/*
 * Copyright DataStax, Inc.
 *
 * This software can be used solely with DataStax Enterprise. Please consult the license at
 * http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */
package com.datastax.driver.core;

import com.google.common.util.concurrent.ListenableFuture;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Cassandra node.
 *
 * 

This class keeps the information the driver maintain on a given Cassandra node. */ public class Host { private static final Logger logger = LoggerFactory.getLogger(Host.class); static final Logger statesLogger = LoggerFactory.getLogger(Host.class.getName() + ".STATES"); // The address we'll use to connect to the node private final EndPoint endPoint; // The broadcast RPC address, as reported in system tables. // Note that, unlike previous versions of the driver, this address is NOT TRANSLATED. private volatile InetSocketAddress broadcastRpcAddress; // The broadcast_address as known by Cassandra. // We use that internally because // that's the 'peer' in the 'System.peers' table and avoids querying the full peers table in // ControlConnection.refreshNodeInfo. private volatile InetSocketAddress broadcastSocketAddress; // The listen_address as known by Cassandra. // This is usually the same as broadcast_address unless // specified otherwise in cassandra.yaml file. private volatile InetSocketAddress listenSocketAddress; private volatile UUID hostId; private volatile UUID schemaVersion; private volatile String serverId; private volatile int nativeTransportPort; private volatile int nativeTransportPortSsl; private volatile int storagePort; private volatile int storagePortSsl; private volatile int jmxPort; enum State { ADDED, DOWN, UP } volatile State state; /** Ensures state change notifications for that host are handled serially */ final ReentrantLock notificationsLock = new ReentrantLock(true); final ConvictionPolicy convictionPolicy; private final Cluster.Manager manager; // Tracks later reconnection attempts to that host so we avoid adding multiple tasks. final AtomicReference> reconnectionAttempt = new AtomicReference>(); final ExecutionInfo defaultExecutionInfo; private volatile String datacenter; private volatile String rack; private volatile VersionNumber cassandraVersion; private volatile Set tokens; private volatile Set dseWorkloads = Collections.emptySet(); private volatile Boolean dseGraphEnabled; private volatile VersionNumber dseVersion; Host( EndPoint endPoint, ConvictionPolicy.Factory convictionPolicyFactory, Cluster.Manager manager) { if (endPoint == null || convictionPolicyFactory == null) throw new NullPointerException(); this.endPoint = endPoint; this.convictionPolicy = convictionPolicyFactory.create(this, manager.reconnectionPolicy()); this.manager = manager; this.defaultExecutionInfo = new ExecutionInfo(this); this.state = State.ADDED; } void setLocationInfo(String datacenter, String rack) { this.datacenter = datacenter; this.rack = rack; } void setVersion(String cassandraVersion) { VersionNumber versionNumber = null; try { if (cassandraVersion != null) { versionNumber = VersionNumber.parse(cassandraVersion); } } catch (IllegalArgumentException e) { logger.warn( "Error parsing Cassandra version {}. This shouldn't have happened", cassandraVersion); } this.cassandraVersion = versionNumber; } void setBroadcastRpcAddress(InetSocketAddress broadcastRpcAddress) { this.broadcastRpcAddress = broadcastRpcAddress; } void setBroadcastSocketAddress(InetSocketAddress broadcastAddress) { this.broadcastSocketAddress = broadcastAddress; } void setListenSocketAddress(InetSocketAddress listenAddress) { this.listenSocketAddress = listenAddress; } void setDseVersion(String dseVersion) { VersionNumber versionNumber = null; try { if (dseVersion != null) { versionNumber = VersionNumber.parse(dseVersion); } } catch (IllegalArgumentException e) { logger.warn("Error parsing DSE version {}. This shouldn't have happened", dseVersion); } this.dseVersion = versionNumber; } void setDseWorkloads(Set dseWorkloads) { this.dseWorkloads = Collections.unmodifiableSet(dseWorkloads); } void setDseGraphEnabled(boolean dseGraphEnabled) { this.dseGraphEnabled = dseGraphEnabled; } void setHostId(UUID hostId) { this.hostId = hostId; } void setSchemaVersion(UUID schemaVersion) { this.schemaVersion = schemaVersion; } void setServerId(String serverId) { this.serverId = serverId; } void setNativeTransportPort(int nativeTransportPort) { this.nativeTransportPort = nativeTransportPort; } void setNativeTransportPortSsl(int nativeTransportPortSsl) { this.nativeTransportPortSsl = nativeTransportPortSsl; } void setStoragePort(int storagePort) { this.storagePort = storagePort; } void setStoragePortSsl(int storagePortSsl) { this.storagePortSsl = storagePortSsl; } void setJmxPort(int jmxPort) { this.jmxPort = jmxPort; } boolean supports(ProtocolVersion version) { return getCassandraVersion() == null || version.minCassandraVersion().compareTo(getCassandraVersion().nextStable()) <= 0; } /** Returns information to connect to the node. */ public EndPoint getEndPoint() { return endPoint; } /** * Returns the address that the driver will use to connect to the node. * * @deprecated This is exposed mainly for historical reasons. Internally, the driver uses {@link * #getEndPoint()} to establish connections. This is a shortcut for {@code * getEndPoint().resolve().getAddress()}. */ @Deprecated public InetAddress getAddress() { return endPoint.resolve().getAddress(); } /** * Returns the address and port that the driver will use to connect to the node. * * @deprecated This is exposed mainly for historical reasons. Internally, the driver uses {@link * #getEndPoint()} to establish connections. This is a shortcut for {@code * getEndPoint().resolve()}. * @see The * cassandra.yaml configuration file */ @Deprecated public InetSocketAddress getSocketAddress() { return endPoint.resolve(); } /** * Returns the broadcast RPC address, as reported by the node. * *

This is address reported in {@code system.peers.rpc_address} (Cassandra 3) or {@code * system.peers_v2.native_address/native_port} (Cassandra 4+). * *

Note that this is not necessarily the address that the driver will use to connect: if the * node is accessed through a proxy, a translation might be necessary; this is handled by {@link * #getEndPoint()}. * *

For versions of Cassandra less than 2.0.16, 2.1.6 or 2.2.0-rc1, this will be {@code null} * for the control host. It will get updated if the control connection switches to another host. * * @see CASSANDRA-9436 (where the * information was added for the control host) */ public InetSocketAddress getBroadcastRpcAddress() { return broadcastRpcAddress; } /** * Returns the node broadcast address, if known. Otherwise {@code null}. * *

This is a shortcut for {@code getBroadcastSocketAddress().getAddress()}. * * @return the node broadcast address, if known. Otherwise {@code null}. * @see #getBroadcastSocketAddress() * @see The * cassandra.yaml configuration file */ public InetAddress getBroadcastAddress() { return broadcastSocketAddress != null ? broadcastSocketAddress.getAddress() : null; } /** * Returns the node broadcast address (that is, the address by which it should be contacted by * other peers in the cluster), if known. Otherwise {@code null}. * *

Note that the port of the returned address will be 0 for versions of Cassandra older than * 4.0. * *

This corresponds to the {@code broadcast_address} cassandra.yaml file setting and is by * default the same as {@link #getListenSocketAddress()}, unless specified otherwise in * cassandra.yaml. This is NOT the address clients should use to contact this node. * *

This information is always available for peer hosts. For the control host, it's only * available if CASSANDRA-9436 is fixed on the server side (Cassandra versions >= 2.0.16, 2.1.6, * 2.2.0 rc1). For older versions, note that if the driver loses the control connection and * reconnects to a different control host, the old control host becomes a peer, and therefore its * broadcast address is updated. * * @return the node broadcast address, if known. Otherwise {@code null}. * @see The * cassandra.yaml configuration file */ public InetSocketAddress getBroadcastSocketAddress() { return broadcastSocketAddress; } /** * Returns the node listen address, if known. Otherwise {@code null}. * *

This is a shortcut for {@code getListenSocketAddress().getAddress()}. * * @return the node listen address, if known. Otherwise {@code null}. * @see #getListenSocketAddress() * @see The * cassandra.yaml configuration file */ public InetAddress getListenAddress() { return listenSocketAddress != null ? listenSocketAddress.getAddress() : null; } /** * Returns the node listen address (that is, the address the node uses to contact other peers in * the cluster), if known. Otherwise {@code null}. * *

Note that the port of the returned address will be 0 for versions of Cassandra older than * 4.0. * *

This corresponds to the {@code listen_address} cassandra.yaml file setting. This is NOT * the address clients should use to contact this node. * *

This information is available for the control host if CASSANDRA-9603 is fixed on the server * side (Cassandra versions >= 2.0.17, 2.1.8, 2.2.0 rc2). It's currently not available for peer * hosts. Note that the current driver code already tries to read a {@code listen_address} column * in {@code system.peers}; when a future Cassandra version adds it, it will be picked by the * driver without any further change needed. * * @return the node listen address, if known. Otherwise {@code null}. * @see The * cassandra.yaml configuration file */ public InetSocketAddress getListenSocketAddress() { return listenSocketAddress; } /** * Returns the name of the datacenter this host is part of. * *

The returned datacenter name is the one as known by Cassandra. It is also possible for this * information to be unavailable. In that case this method returns {@code null}, and the caller * should always be aware of this possibility. * * @return the Cassandra datacenter name or null if datacenter is unavailable. */ public String getDatacenter() { return datacenter; } /** * Returns the name of the rack this host is part of. * *

The returned rack name is the one as known by Cassandra. It is also possible for this * information to be unavailable. In that case this method returns {@code null}, and the caller * should always be aware of this possibility. * * @return the Cassandra rack name or null if the rack is unavailable */ public String getRack() { return rack; } /** * The Cassandra version the host is running. * *

It is also possible for this information to be unavailable. In that case this method returns * {@code null}, and the caller should always be aware of this possibility. * * @return the Cassandra version the host is running. */ public VersionNumber getCassandraVersion() { return cassandraVersion; } /** * The DSE version the host is running. * *

It is also possible for this information to be unavailable. In that case this method returns * {@code null}, and the caller should always be aware of this possibility. * * @return the DSE version the host is running. */ public VersionNumber getDseVersion() { return dseVersion; } /** * The main DSE Workload the host is running. * *

It is also possible for this information to be unavailable. In that case this method returns * {@code null}, and the caller should always be aware of this possibility. * * @return the DSE workload the host is running. * @deprecated This method returns only the first workload reported by the host; use {@link * #getDseWorkloads()} instead. */ @Deprecated public String getDseWorkload() { return dseWorkloads.isEmpty() ? null : dseWorkloads.iterator().next(); } /** * The DSE Workloads the host is running. * *

This is based on the "workload" or "workloads" columns in {@code system.local} and {@code * system.peers}. * *

Workload labels may vary depending on the DSE version in use; e.g. DSE 5.1 may report two * distinct workloads: {@code Search} and {@code Analytics}, while DSE 5.0 would report a single * {@code SearchAnalytics} workload instead. It is up to users to deal with such discrepancies; * the driver simply returns the workload labels as reported by DSE, without any form of * pre-processing. * *

The returned set is immutable. It is also possible for this information to be unavailable. * In that case this method returns an empty set, and the caller should always be aware of this * possibility. * * @return the DSE workloads the host is running. */ public Set getDseWorkloads() { return dseWorkloads; } /** * Returns whether the host is running DSE Graph. * *

This is based on the "graph" column in {@code system.local} and {@code system.peers}. * * @return whether the node is running DSE Graph. * @deprecated As of DSE 5.1, users should determine whether this host has the graph workload * enabled by inspecting the contents of {@link #getDseWorkloads()} instead. Note that this * method will throw {@link UnsupportedOperationException} if the system tables do not contain * a "graph" column. */ @Deprecated public boolean isDseGraphEnabled() { if (dseGraphEnabled == null) { throw new UnsupportedOperationException( "No 'graph' column in system tables. Try getDseWorkloads() instead."); } else { return dseGraphEnabled; } } /** * Return the host id value for the host. * *

The host id is the main identifier used by Cassandra on the server for internal * communication (gossip). It is referenced as the column {@code host_id} in the {@code * system.local} or {@code system.peers} table. * * @return the node's host id value. */ public UUID getHostId() { return hostId; } /** * Return the current schema version for the host. * *

Schema versions in Cassandra are used to ensure all the nodes agree on the current Cassandra * schema when it is modified. For more information see {@link * ExecutionInfo#isSchemaInAgreement()} * * @return the node's current schema version value. */ public UUID getSchemaVersion() { return schemaVersion; } /** * Returns the tokens that this host owns. * * @return the (immutable) set of tokens. */ public Set getTokens() { return tokens; } void setTokens(Set tokens) { this.tokens = tokens; } /** * Returns whether the host is considered up by the driver. * *

Please note that this is only the view of the driver and may not reflect reality. In * particular a node can be down but the driver hasn't detected it yet, or it can have been * restarted and the driver hasn't detected it yet (in particular, for hosts to which the driver * does not connect (because the {@code LoadBalancingPolicy.distance} method says so), this * information may be durably inaccurate). This information should thus only be considered as best * effort and should not be relied upon too strongly. * * @return whether the node is considered up. */ public boolean isUp() { return state == State.UP; } /** * Returns a description of the host's state, as seen by the driver. * *

This is exposed for debugging purposes only; the format of this string might change between * driver versions, so clients should not make any assumptions about it. * * @return a description of the host's state. */ public String getState() { return state.name(); } /** * Returns a {@code ListenableFuture} representing the completion of the reconnection attempts * scheduled after a host is marked {@code DOWN}. * *

If the caller cancels this future, the driver will not try to reconnect to this host * until it receives an UP event for it. Note that this could mean never, if the node was marked * down because of a driver-side error (e.g. read timeout) but no failure was detected by * Cassandra. The caller might decide to trigger an explicit reconnection attempt at a later point * with {@link #tryReconnectOnce()}. * * @return the future, or {@code null} if no reconnection attempt was in progress. */ public ListenableFuture getReconnectionAttemptFuture() { return reconnectionAttempt.get(); } /** * Triggers an asynchronous reconnection attempt to this host. * *

This method is intended for load balancing policies that mark hosts as {@link * HostDistance#IGNORED IGNORED}, but still need a way to periodically check these hosts' states * (UP / DOWN). * *

For a host that is at distance {@code IGNORED}, this method will try to reconnect exactly * once: if reconnection succeeds, the host is marked {@code UP}; otherwise, no further attempts * will be scheduled. It has no effect if the node is already {@code UP}, or if a reconnection * attempt is already in progress. * *

Note that if the host is not a distance {@code IGNORED}, this method will * trigger a periodic reconnection attempt if the reconnection fails. */ public void tryReconnectOnce() { this.manager.startSingleReconnectionAttempt(this); } /** * Returns the value of the {@code server_id} field in the {@code peers} system table for this * node. * *

The {@code server_id} is the single identifier of the machine running a DSE instance. If DSE * has been configured with DSE * Multi-Instance, the {@code server_id} helps identifying the single physical machine that * runs the multiple DSE instances. If DSE is not configured with DSE Multi-Instance, the {@code * server_id} will be automatically set and be unique for each host. * *

For more information on the {@code server_id} see the * documentation. * * @return the node's server id value. This information is only available if connecting the driver * to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public String getDseServerId() { return serverId; } /** * Returns the port for the native transport connections on the DSE node. * *

The native transport port is {@code 9042} by default but can be changed on instances * requiring specific firewall configurations. This can be configured in the {@code * cassandra.yaml} configuration file under the {@code native_transport_port} property. * *

If a specific port is configured on a node, it will be exposed via this method. * * @return the node's native transport port value. This information is only available if * connecting the driver to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public int getNativeTransportPort() { return nativeTransportPort; } /** * Returns the port for the encrypted native transport connections on the DSE node. * *

In most scenarios enabling client communications in DSE will result in using a single port * that will only accept encrypted connections (by default the port {@code 9042} is reused since * unencrypted connections are not allowed). * *

However, it is possible to configure DSE to use both encrypted and a non-encrypted * communication ports with clients. In that case the port accepting encrypted connections will * differ from the non-encrypted one (see {@link #getNativeTransportPort}) and will be exposed via * this method. * * @return the node's encrypted native transport port value. This information is only available if * connecting the driver to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public int getNativeTransportPortSsl() { return nativeTransportPortSsl; } /** * Returns the storage port used by the DSE node. * *

The storage port is used for internal communication between the DSE server nodes. This port * is never used by the driver. * * @return the node's storage port value. This information is only available if connecting the * driver to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public int getStoragePort() { return storagePort; } /** * Returns the encrypted storage port used by the DSE node. * *

If inter-node encryption is enabled on the DSE cluster, nodes will communicate securely * between each other via this port. This port is never used by the driver. * * @return the node's encrypted storage port value. This information is only available if * connecting the driver to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public int getStoragePortSsl() { return storagePortSsl; } /** * Returns the JMX port used by this node. * *

The JMX port can be configured in the {@code cassandra-env.sh} configuration file separately * on each node. * * @return the node's JMX port value. This information is only available if connecting the driver * to a DSE 6.0+ node. Otherwise this returns {@code null}. */ public int getJmxPort() { return jmxPort; } @Override public boolean equals(Object other) { if (other instanceof Host) { Host that = (Host) other; return this.endPoint.equals(that.endPoint); } return false; } @Override public int hashCode() { return endPoint.hashCode(); } boolean wasJustAdded() { return state == State.ADDED; } @Override public String toString() { return endPoint.toString(); } void setDown() { state = State.DOWN; } void setUp() { state = State.UP; } /** * Interface for listeners that are interested in hosts added, up, down and removed events. * *

It is possible for the same event to be fired multiple times, particularly for up or down * events. Therefore, a listener should ignore the same event if it has already been notified of a * node's state. */ public interface StateListener { /** * Called when a new node is added to the cluster. * *

The newly added node should be considered up. * * @param host the host that has been newly added. */ void onAdd(Host host); /** * Called when a node is determined to be up. * * @param host the host that has been detected up. */ void onUp(Host host); /** * Called when a node is determined to be down. * * @param host the host that has been detected down. */ void onDown(Host host); /** * Called when a node is removed from the cluster. * * @param host the removed host. */ void onRemove(Host host); /** * Gets invoked when the tracker is registered with a cluster, or at cluster startup if the * tracker was registered at initialization with {@link * com.datastax.driver.core.Cluster.Initializer#register(LatencyTracker)}. * * @param cluster the cluster that this tracker is registered with. */ void onRegister(Cluster cluster); /** * Gets invoked when the tracker is unregistered from a cluster, or at cluster shutdown if the * tracker was not unregistered. * * @param cluster the cluster that this tracker was registered with. */ void onUnregister(Cluster cluster); } /** * A {@code StateListener} that tracks when it gets registered or unregistered with a cluster. * *

This interface exists only for backward-compatibility reasons: starting with the 3.0 branch * of the driver, its methods are on the parent interface directly. */ public interface LifecycleAwareStateListener extends StateListener { /** * Gets invoked when the listener is registered with a cluster, or at cluster startup if the * listener was registered at initialization with {@link * com.datastax.driver.core.Cluster#register(Host.StateListener)}. * * @param cluster the cluster that this listener is registered with. */ @Override void onRegister(Cluster cluster); /** * Gets invoked when the listener is unregistered from a cluster, or at cluster shutdown if the * listener was not unregistered. * * @param cluster the cluster that this listener was registered with. */ @Override void onUnregister(Cluster cluster); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy