
org.elassandra.discovery.CassandraDiscovery Maven / Gradle / Ivy
/*
* Copyright (c) 2017 Strapdata (http://www.strapdata.com)
* Contains some code from Elasticsearch (http://www.elastic.co)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.elassandra.discovery;
import com.google.common.net.InetAddresses;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.UnavailableException;
import org.apache.cassandra.gms.ApplicationState;
import org.apache.cassandra.gms.EndpointState;
import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.gms.IEndpointStateChangeSubscriber;
import org.apache.cassandra.gms.VersionedValue;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.transport.Event;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.elassandra.ConcurrentMetaDataUpdateException;
import org.elassandra.PaxosMetaDataUpdateException;
import org.elassandra.gateway.CassandraGatewayService;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.ClusterStateTaskConfig;
import org.elasticsearch.cluster.ClusterStateTaskConfig.SchemaUpdate;
import org.elasticsearch.cluster.ClusterStateTaskListener;
import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNode.DiscoveryNodeStatus;
import org.elasticsearch.cluster.node.DiscoveryNode.Role;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.routing.ShardRoutingState;
import org.elasticsearch.cluster.service.ClusterApplier;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsException;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.discovery.AckClusterStatePublishResponseHandler;
import org.elasticsearch.discovery.Discovery;
import org.elasticsearch.discovery.DiscoverySettings;
import org.elasticsearch.discovery.DiscoveryStats;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.transport.TransportService;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import static org.apache.cassandra.cql3.QueryProcessor.executeInternal;
import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK;
/**
* https://www.elastic.co/guide/en/elasticsearch/reference/6.3/modules-discovery-zen.html
*
* Discover the cluster topology from cassandra snitch and settings, mappings, blocks from the elastic_admin keyspace.
* Publishing is just a notification to refresh in memory configuration from the cassandra table.
* @author vroyer
*
*/
public class CassandraDiscovery extends AbstractLifecycleComponent implements Discovery, IEndpointStateChangeSubscriber, AppliedClusterStateAction.AppliedClusterStateListener {
private static final EnumSet CASSANDRA_ROLES = EnumSet.of(Role.MASTER,Role.DATA);
private final TransportService transportService;
private final ClusterService clusterService;
private final ClusterApplier clusterApplier;
private final AtomicReference committedState; // last committed cluster state
private final ClusterName clusterName;
private final DiscoverySettings discoverySettings;
private final NamedWriteableRegistry namedWriteableRegistry;
private final PendingClusterStatesQueue pendingStatesQueue;
private final AppliedClusterStateAction appliedClusterStateAction;
private final AtomicReference handlerRef = new AtomicReference<>();
private final Object stateMutex = new Object();
private final ClusterGroup clusterGroup;
private final InetAddress localAddress;
private final String localDc;
private final ConcurrentMap localShardStateMap = new ConcurrentHashMap();
private final ConcurrentMap> remoteShardRoutingStateMap = new ConcurrentHashMap>();
/**
* When searchEnabled=true, local shards are visible for routing, otherwise, local shards are seen as UNASSIGNED.
* This allows to gracefully shutdown or start the node for maintenance like an offline repair or rebuild_index.
*/
private final AtomicBoolean searchEnabled = new AtomicBoolean(false);
/**
* If autoEnableSearch=true, search is automatically enabled when the node becomes ready to operate, otherwise, searchEnabled should be manually set to true.
*/
private final AtomicBoolean autoEnableSearch = new AtomicBoolean(System.getProperty("es.auto_enable_search") == null || Boolean.getBoolean("es.auto_enable_search"));
public static final Setting MAX_PENDING_CLUSTER_STATES_SETTING =
Setting.intSetting("discovery.cassandra.publish.max_pending_cluster_states", 25, 1, Property.NodeScope);
public CassandraDiscovery(Settings settings,
TransportService transportService,
final ClusterService clusterService,
final ClusterApplier clusterApplier,
NamedWriteableRegistry namedWriteableRegistry) {
super(settings);
this.clusterApplier = clusterApplier;
this.clusterService = clusterService;
this.discoverySettings = new DiscoverySettings(settings, clusterService.getClusterSettings());
this.namedWriteableRegistry = namedWriteableRegistry;
this.transportService = transportService;
this.clusterName = clusterService.getClusterName();
this.committedState = new AtomicReference<>();
this.clusterService.setDiscovery(this);
this.clusterService.getMasterService().setClusterStateSupplier(() -> committedState.get());
this.clusterService.getMasterService().setClusterStatePublisher(this::publish);
this.localAddress = FBUtilities.getBroadcastAddress();
this.localDc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(FBUtilities.getBroadcastAddress());
this.clusterGroup = new ClusterGroup();
this.pendingStatesQueue = new PendingClusterStatesQueue(logger, MAX_PENDING_CLUSTER_STATES_SETTING.get(settings));
this.appliedClusterStateAction = new AppliedClusterStateAction(settings, transportService, this, discoverySettings);
}
public PendingClusterStatesQueue pendingStatesQueue() {
return this.pendingStatesQueue;
}
public static String buildNodeName(InetAddress addr) {
String hostname = NetworkAddress.format(addr);
if (hostname != null)
return hostname;
return String.format(Locale.getDefault(), "node%03d%03d%03d%03d",
(int) (addr.getAddress()[0] & 0xFF), (int) (addr.getAddress()[1] & 0xFF),
(int) (addr.getAddress()[2] & 0xFF), (int) (addr.getAddress()[3] & 0xFF));
}
@Override
protected void doStart() {
Gossiper.instance.register(this);
synchronized (clusterGroup) {
logger.debug("Connected to cluster [{}]", clusterName.value());
clusterGroup.put(localNode().getId(), localNode());
logger.info("localNode name={} id={} localAddress={} publish_host={}", localNode().getName(), localNode().getId(), localAddress, localNode().getAddress());
// initialize cluster from cassandra local token map
for(InetAddress endpoint : StorageService.instance.getTokenMetadata().getAllEndpoints()) {
if (!this.localAddress.equals(endpoint) && this.localDc.equals(DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint))) {
String hostId = StorageService.instance.getHostId(endpoint).toString();
UntypedResultSet rs = executeInternal("SELECT preferred_ip, rpc_address from system." + SystemKeyspace.PEERS+" WHERE peer = ?", endpoint);
if (!rs.isEmpty()) {
UntypedResultSet.Row row = rs.one();
EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
clusterGroup.update(epState, hostId, endpoint,
row.has("preferred_ip") ? row.getInetAddress("preferred_ip") : endpoint,
row.has("rpc_address") ? row.getInetAddress("rpc_address") : null);
}
}
}
}
updateClusterGroupsFromGossiper();
// Cassandra is usually in the NORMAL state when discovery start.
if (isNormal(Gossiper.instance.getEndpointStateForEndpoint(this.localAddress)) && isAutoEnableSearch()) {
try {
this.setSearchEnabled(true);
} catch (IOException e) {
logger.error("Failed to set searchEnabled",e);
}
}
updateRoutingTable("starting-cassandra-discovery", true);
}
public ClusterState initClusterState(DiscoveryNode localNode) {
ClusterState.Builder builder = clusterApplier.newClusterStateBuilder();
ClusterState clusterState = builder.nodes(DiscoveryNodes.builder().add(localNode)
.localNodeId(localNode.getId())
.masterNodeId(localNode.getId())
.build())
.blocks(ClusterBlocks.builder()
.addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)
.addGlobalBlock(CassandraGatewayService.NO_CASSANDRA_RING_BLOCK))
.build();
setCommittedState(clusterState);
this.clusterApplier.setInitialState(clusterState);
return clusterState;
}
/**
* Update the shardState map and trigger a cluster state routing table update if changed.
* @param remoteNode
* @param shardsStateMap
* @param source
*/
private void updateShardRouting(final UUID remoteNode, final Map shardsStateMap, String source) {
this.remoteShardRoutingStateMap.compute(remoteNode, (k, v) -> {
if (v == null) return shardsStateMap;
if (!v.equals(shardsStateMap))
updateRoutingTable(source, false);
return shardsStateMap;
});
}
private void updateRoutingTable(String source, boolean nodesUpdate) {
clusterService.submitStateUpdateTask(source, new ClusterStateUpdateTask() {
@Override
public ClusterState execute(ClusterState currentState) {
ClusterState.Builder clusterStateBuilder = ClusterState.builder(currentState);
DiscoveryNodes discoverNodes = nodes();
if (nodesUpdate)
clusterStateBuilder.nodes(discoverNodes);
if (currentState.nodes().getSize() != discoverNodes.getSize()) {
// update numberOfShards for all indices.
MetaData.Builder metaDataBuilder = MetaData.builder(currentState.metaData());
for(Iterator it = currentState.metaData().iterator(); it.hasNext(); ) {
IndexMetaData indexMetaData = it.next();
IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(indexMetaData);
indexMetaDataBuilder.numberOfShards(discoverNodes.getSize());
metaDataBuilder.put(indexMetaDataBuilder.build(), false);
}
clusterStateBuilder.metaData(metaDataBuilder.build());
}
return clusterStateBuilder.build();
}
@Override
public void onFailure(String source, Exception t) {
logger.error("unexpected failure during [{}]", t, source);
}
});
}
/**
* Update cluster group members from cassandra topology (should only be triggered by IEndpointStateChangeSubscriber events).
* This should trigger re-sharding of index for new nodes (when token distribution change).
*/
private void updateClusterGroupsFromGossiper() {
for (Entry entry : Gossiper.instance.getEndpointStates()) {
EndpointState epState = entry.getValue();
InetAddress endpoint = entry.getKey();
if (!epState.getStatus().equals(VersionedValue.STATUS_NORMAL) && !epState.getStatus().equals(VersionedValue.SHUTDOWN)) {
logger.info("Ignoring node state={}", epState);
continue;
}
if (isLocal(endpoint)) {
VersionedValue vv = epState.getApplicationState(ApplicationState.HOST_ID);
if (vv != null) {
String hostId = vv.value;
if (!this.localNode().getId().equals(hostId)) {
clusterGroup.update(epState, hostId, endpoint, getInternalIp(epState), getRpcAddress(epState));
}
// initialize the remoteShardRoutingStateMap from gossip states
if (epState.getApplicationState(ApplicationState.X1) != null) {
VersionedValue x1 = epState.getApplicationState(ApplicationState.X1);
if (!this.localNode().getId().equals(hostId)) {
try {
Map shardsStateMap = jsonMapper.readValue(x1.value, indexShardStateTypeReference);
updateShardRouting(Gossiper.instance.getHostId(endpoint), shardsStateMap, "X1-"+endpoint);
} catch (IOException e) {
logger.error("Failed to parse X1 for node [{}]", hostId);
}
}
}
}
}
}
updateRoutingTable("discovery-refresh", true);
}
private long getMetadataVersion(VersionedValue versionValue) {
int i = versionValue.value.indexOf('/');
if (i > 0) {
try {
return Long.valueOf(versionValue.value.substring(i+1));
} catch (NumberFormatException e) {
logger.error("Unexpected gossip.X2 value "+versionValue.value, e);
}
}
return -1;
}
private int publishPort() {
try {
return settings.getAsInt("transport.netty.publish_port", settings.getAsInt("transport.publish_port",settings.getAsInt("transport.tcp.port", 9300)));
} catch (SettingsException | NumberFormatException e) {
String publishPort = settings.get("transport.netty.publish_port", settings.get("transport.publish_port",settings.get("transport.tcp.port", "9300")));
if (publishPort.indexOf("-") >0 ) {
return Integer.parseInt(publishPort.split("-")[0]);
} else {
throw e;
}
}
}
public boolean updateNode(InetAddress endpoint, EndpointState epState) {
if (isLocal(endpoint)) {
UUID hostUuid = (epState.getApplicationState(ApplicationState.HOST_ID) == null) ?
StorageService.instance.getHostId(endpoint) :
UUID.fromString(epState.getApplicationState(ApplicationState.HOST_ID) .value);
boolean updatedNode = clusterGroup.update(epState, hostUuid.toString(), endpoint, getInternalIp(epState), getRpcAddress(epState));
// update remote shard routing view.
DiscoveryNodeStatus newStatus = discoveryNodeStatus(epState);
switch(newStatus) {
case ALIVE:
VersionedValue x1 = epState.getApplicationState(ApplicationState.X1);
if (x1 != null) {
try {
Map shardsStateMap = jsonMapper.readValue(x1.value, indexShardStateTypeReference);
this.remoteShardRoutingStateMap.put(hostUuid, shardsStateMap);
} catch (IOException e) {
logger.error("Failed to parse X1 for node=[{}]", hostUuid);
}
}
break;
default:
this.remoteShardRoutingStateMap.remove(hostUuid);
}
if (updatedNode) {
updateRoutingTable("update-node-" + NetworkAddress.format(endpoint)+"-"+newStatus.toString(), true);
return true;
}
}
return false;
}
private boolean isLocal(InetAddress endpoint) {
return DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint).equals(localDc);
}
private boolean isMember(InetAddress endpoint) {
return !this.localAddress.equals(endpoint) && DatabaseDescriptor.getEndpointSnitch().getDatacenter(endpoint).equals(localDc);
}
/**
* #183 lookup EndpointState with the node name = cassandra broadcast address.
* ES RPC adress can be different from the cassandra broadcast address.
*/
public boolean isNormal(DiscoveryNode node) {
// endpoint address = C* broadcast address = Elasticsearch node name (transport may be bound to C* internal or C* RPC broadcast)
EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(InetAddresses.forString(node.getName()));
if (state == null) {
logger.warn("Node endpoint address=[{}] name=[{}] state not found", node.getInetAddress(), node.getName());
return false;
}
return state.isAlive() && state.getStatus().equals(VersionedValue.STATUS_NORMAL);
}
private boolean isNormal(EndpointState state) {
return state != null && state.isAlive() && state.getStatus().equals(VersionedValue.STATUS_NORMAL);
}
public static InetAddress getInternalIp(EndpointState epState) {
return epState.getApplicationState(ApplicationState.INTERNAL_IP) == null ? null :
InetAddresses.forString(epState.getApplicationState(ApplicationState.INTERNAL_IP).value);
}
public static InetAddress getRpcAddress(EndpointState epState) {
return epState.getApplicationState(ApplicationState.RPC_ADDRESS) == null ? null :
InetAddresses.forString(epState.getApplicationState(ApplicationState.RPC_ADDRESS).value);
}
@Override
public void beforeChange(InetAddress endpoint, EndpointState state, ApplicationState appState, VersionedValue value) {
//logger.debug("beforeChange Endpoint={} EndpointState={} ApplicationState={} value={}", endpoint, state, appState, value);
}
@Override
public void onChange(InetAddress endpoint, ApplicationState state, VersionedValue versionValue) {
EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
if (isMember(endpoint)) {
if (logger.isTraceEnabled())
logger.trace("Endpoint={} ApplicationState={} value={}", endpoint, state, versionValue);
switch (state) {
case STATUS:
if (isNormal(epState)) {
updateNode(endpoint, epState);
} else {
// node probably down, notify metaDataVersionAckListener..
notifyHandler(Gossiper.instance.getEndpointStateForEndpoint(endpoint));
}
break;
case X1:
try {
// update the remoteShardRoutingStateMap to build ES routing table for joined-normal nodes only.
if (clusterGroup.contains(epState.getApplicationState(ApplicationState.HOST_ID).value)) {
if (logger.isTraceEnabled())
logger.trace("Endpoint={} X1={} => updating routing table", endpoint, versionValue);
final Map shardsStateMap = jsonMapper.readValue(versionValue.value, indexShardStateTypeReference);
final UUID remoteNode = Gossiper.instance.getHostId(endpoint);
updateShardRouting(remoteNode, shardsStateMap, "X1-" + endpoint);
}
} catch (Exception e) {
logger.warn("Failed to parse gossip index shard state", e);
}
break;
case X2:
case INTERNAL_IP: // manage address replacement from a remote node
case RPC_ADDRESS:
updateNode(endpoint, epState);
break;
}
}
// self status update.
if (this.localAddress.equals(endpoint)) {
switch (state) {
case STATUS:
if (logger.isTraceEnabled())
logger.trace("Endpoint={} STATUS={} => may update searchEnabled", endpoint, versionValue);
// update searchEnabled according to the node status and autoEnableSearch.
if (isNormal(Gossiper.instance.getEndpointStateForEndpoint(endpoint))) {
if (!this.searchEnabled.get() && this.autoEnableSearch.get()) {
try {
setSearchEnabled(true, true);
} catch (IOException e) {
logger.error("Failed to enable search",e);
}
}
publishX2(this.committedState.get(), true);
} else {
// node is leaving or whatever, disabling search.
if (this.searchEnabled.get()) {
try {
setSearchEnabled(false, true);
} catch (IOException e) {
logger.error("Failed to disable search",e);
}
}
}
break;
}
}
}
/**
* Warning: IEndpointStateChangeSubscriber.onXXXX should not block (on connection timeout or clusterState update) to avoid gossip issues.
*/
private void traceEpState(InetAddress endpoint, EndpointState epState) {
if (logger.isTraceEnabled())
logger.trace("Endpoint={} isAlive={} STATUS={} HOST_ID={} INTERNAL_IP={} RPC_ADDRESS={} SCHEMA={} X1={} X2={}", endpoint,
epState.isAlive(),
epState.getStatus(),
epState.getApplicationState(ApplicationState.HOST_ID),
epState.getApplicationState(ApplicationState.INTERNAL_IP),
epState.getApplicationState(ApplicationState.RPC_ADDRESS),
epState.getApplicationState(ApplicationState.SCHEMA),
epState.getApplicationState(ApplicationState.X1),
epState.getApplicationState(ApplicationState.X2));
}
@Override
public void onAlive(InetAddress endpoint, EndpointState epState) {
if (isMember(endpoint)) {
traceEpState(endpoint, epState);
logger.debug("Endpoint={} isAlive={} => update node + connecting", endpoint, epState.isAlive());
if (isNormal(epState))
updateNode(endpoint, epState);
}
}
@Override
public void onDead(InetAddress endpoint, EndpointState epState) {
if (isMember(endpoint)) {
traceEpState(endpoint, epState);
logger.debug("Endpoint={} isAlive={} => update node + disconnecting", endpoint, epState.isAlive());
notifyHandler(Gossiper.instance.getEndpointStateForEndpoint(endpoint));
updateNode(endpoint, epState);
}
}
@Override
public void onRestart(InetAddress endpoint, EndpointState epState) {
if (isMember(endpoint)) {
traceEpState(endpoint, epState);
if (isNormal(epState))
updateNode(endpoint, epState);
}
}
@Override
public void onJoin(InetAddress endpoint, EndpointState epState) {
if (isLocal(endpoint)) {
traceEpState(endpoint, epState);
if (isNormal(epState))
updateNode(endpoint, epState);
}
}
@Override
public void onRemove(InetAddress endpoint) {
if (this.localAddress.equals(endpoint)) {
try {
setSearchEnabled(false);
} catch (IOException e) {
}
} else if (isMember(endpoint)) {
DiscoveryNode removedNode = this.nodes().findByInetAddress(endpoint);
if (removedNode != null && !this.localNode().getId().equals(removedNode.getId())) {
logger.warn("Removing node ip={} node={} => disconnecting", endpoint, removedNode);
notifyHandler(Gossiper.instance.getEndpointStateForEndpoint(endpoint));
this.clusterGroup.remove(removedNode.getId());
updateRoutingTable("node-removed-"+endpoint, true);
}
}
}
/**
* Release the listener when all attendees have reached the expected version or become down.
* Called by the cassandra gossiper thread from onChange() or onDead() or onRemove().
*/
public void notifyHandler(EndpointState endPointState) {
VersionedValue hostIdValue = endPointState.getApplicationState(ApplicationState.HOST_ID);
if (hostIdValue == null)
return; // happen when we are removing a node while updating the mapping
String hostId = hostIdValue.value;
if (hostId == null || localNode().getId().equals(hostId))
return;
if (!endPointState.isAlive() || !endPointState.getStatus().equals("NORMAL")) {
// node was removed from the gossiper, down or leaving, acknowledge to avoid locking.
AckClusterStatePublishResponseHandler handler = handlerRef.get();
if (handler != null) {
DiscoveryNode node = nodes().get(hostId);
if (node != null) {
logger.debug("nack node={}", node.getId());
handler.onFailure(node, new NoNodeAvailableException("Node "+hostId+" unavailable"));
}
}
}
}
@Override
protected void doStop() throws ElasticsearchException {
Gossiper.instance.unregister(this);
synchronized (clusterGroup) {
clusterGroup.members.clear();
}
}
private static final ApplicationState ELASTIC_SHARDS_STATES = ApplicationState.X1;
private static final ApplicationState ELASTIC_META_DATA = ApplicationState.X2;
private static final ObjectMapper jsonMapper = new ObjectMapper();
private static final TypeReference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy