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

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

Go to download

A driver for DataStax Enterprise (DSE) and Apache Cassandra 1.2+ clusters that works exclusively with the Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol, supporting DSE-specific features such as geospatial types, DSE Graph and DSE authentication.

There is a newer version: 2.4.0
Show newest version
/*
 * 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.datastax.driver.core.policies.AddressTranslator;
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 InetSocketAddress address; // 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 InetAddress broadcastAddress; // The listen_address as known by Cassandra. // This is usually the same as broadcast_address unless // specified otherwise in cassandra.yaml file. private volatile InetAddress listenAddress; 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; // ClusterMetadata keeps one Host object per inet address and we rely on this (more precisely, // we rely on the fact that we can use Object equality as a valid equality), so don't use // that constructor but ClusterMetadata.getHost instead. Host(InetSocketAddress address, ConvictionPolicy.Factory convictionPolicyFactory, Cluster.Manager manager) { if (address == null || convictionPolicyFactory == null) throw new NullPointerException(); this.address = address; 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 setBroadcastAddress(InetAddress broadcastAddress) { this.broadcastAddress = broadcastAddress; } void setListenAddress(InetAddress listenAddress) { this.listenAddress = 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 the address that the driver will use to connect to the node. *

* This is a shortcut for {@code getSocketAddress().getAddress()}. * * @return the address. * @see #getSocketAddress() */ public InetAddress getAddress() { return address.getAddress(); } /** * Returns the address and port that the driver will use to connect to the node. *

* This is the node's broadcast RPC address, possibly translated if an {@link AddressTranslator} has been configured * for this cluster. *

* The broadcast RPC address is inferred from the following cassandra.yaml file settings: *

    *
  1. {@code rpc_address}, {@code rpc_interface} or {@code broadcast_rpc_address}
  2. *
  3. {@code native_transport_port}
  4. *
* * @return the address and port. * @see The cassandra.yaml configuration file */ public InetSocketAddress getSocketAddress() { return address; } /** * Returns the node broadcast address (that is, the IP by which it should be contacted by other peers in the * cluster), if known. *

* This corresponds to the {@code broadcast_address} cassandra.yaml file setting and * is by default the same as {@link #getListenAddress()}, 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 InetAddress getBroadcastAddress() { return broadcastAddress; } /** * Returns the node listen address (that is, the IP the node uses to contact other peers in the cluster), if known. *

* 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 InetAddress getListenAddress() { return listenAddress; } /** * 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.address.equals(that.address); } return false; } @Override public int hashCode() { return address.hashCode(); } boolean wasJustAdded() { return state == State.ADDED; } @Override public String toString() { return address.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