org.apache.cassandra.service.ElassandraDaemon Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* 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.apache.cassandra.service;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.Schema;
import org.apache.cassandra.config.SchemaConstants;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NativeLibrary;
import org.apache.cassandra.utils.WindowsTimer;
import org.apache.logging.log4j.Logger;
import org.elassandra.NoPersistedMetaDataException;
import org.elassandra.discovery.CassandraDiscovery;
import org.elassandra.env.EnvironmentLoader;
import org.elassandra.index.ElasticSecondaryIndex;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.Version;
import org.elasticsearch.bootstrap.Bootstrap;
import org.elasticsearch.cli.Terminal;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.NamedDiff;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.CreationException;
import org.elasticsearch.common.inject.Injector;
import org.elasticsearch.common.inject.spi.Message;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.monitor.jvm.JvmInfo;
import org.elasticsearch.monitor.process.ProcessProbe;
import org.elasticsearch.node.InternalSettingsPreparer;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.plugins.ClusterPlugin;
import org.elasticsearch.plugins.Plugin;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import static com.google.common.collect.Sets.newHashSet;
/**
* Bootstrap sequence Cassandra setup() joinRing() beforBoostrap() wait for
* local shard = STARTED ElasticSearch activate() GatewayService recover
* metadata from cassandra schema DiscoveryService discover ring topology and
* build routing table await that Cassandra start() (Complet cassandra
* bootstrap) ElasticSearch start() (Open Elastic http service)
*
*
* @author vroyer
*
*/
public class ElassandraDaemon extends CassandraDaemon {
private static final Logger logger = Loggers.getLogger(ElassandraDaemon.class);
private static volatile Thread keepAliveThread;
private static volatile CountDownLatch keepAliveLatch;
public static ElassandraDaemon instance = null;
public static boolean hasWorkloadColumn = false;
protected volatile Node node = null;
protected Environment env;
private boolean activated = false;
private boolean hasMetadata = false;
private List setupListeners = new CopyOnWriteArrayList();
public ElassandraDaemon(Environment env) {
super(true);
this.env = env;
instance = this;
}
public Environment getEnvironment() { return this.env; }
public Node node() {
return this.node;
}
public Node node(Node node) {
this.node = node;
return node;
}
public void activate(boolean addShutdownHook, boolean createNode, Settings settings, Environment env, Collection> pluginList) {
try
{
DatabaseDescriptor.daemonInitialization();
DatabaseDescriptor.createAllDirectories();
}
catch (ExceptionInInitializerError e)
{
System.out.println("Exception (" + e.getClass().getName() + ") encountered during startup: " + e.getMessage());
String errorMessage = buildErrorMessage("Initialization", e);
System.err.println(errorMessage);
System.err.flush();
System.exit(3);
}
String pidFile = System.getProperty("cassandra-pidfile");
if (pidFile != null)
{
new File(pidFile).deleteOnExit();
}
// #202 Initialize NativeLibrary.jnaLockable for correct Node initialization.
NativeLibrary.tryMlockall();
//setup(addShutdownHook, settings, env, pluginList);
org.elasticsearch.bootstrap.Bootstrap.initializeNatives(
env.tmpFile(),
settings.getAsBoolean("bootstrap.memory_lock", true),
settings.getAsBoolean("bootstrap.system_call_filter", false),
settings.getAsBoolean("bootstrap.ctrlhandler", true));
// initialize probes before the security manager is installed
org.elasticsearch.bootstrap.Bootstrap.initializeProbes();
if (addShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (node != null) {
try {
node.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
});
}
if (createNode) {
// add ElassandraDaemon as a ClusterPlugin
List> pluginList2 = Lists.newArrayList(pluginList);
pluginList2.add(ElassandraPlugin.class);
this.node = new Node(getSettings(), pluginList2);
}
//enable indexing in cassandra.
ElasticSecondaryIndex.runsElassandra = true;
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mbs.registerMBean(new StandardMBean(new NativeAccess(), NativeAccessMBean.class), new ObjectName(MBEAN_NAME));
} catch (Exception e) {
logger.error("error registering MBean {}", MBEAN_NAME, e);
// Allow the server to start even if the bean can't be registered
}
if (FBUtilities.isWindows)
{
// We need to adjust the system timer on windows from the default 15ms down to the minimum of 1ms as this
// impacts timer intervals, thread scheduling, driver interrupts, etc.
WindowsTimer.startTimerPeriod(DatabaseDescriptor.getWindowsTimerInterval());
}
super.setup(); // start bootstrap CassandraDaemon
super.start(); // start Thrift+RPC service
if (this.node != null) {
try {
this.node.clusterService().submitNumberOfShardsAndReplicasUpdate("user-keyspaces-bootstraped");
this.node.start();
} catch (NodeValidationException e) {
throw new RuntimeException(e);
}
}
}
/**
* Start Node if metadata available.
*/
@Override
public void systemKeyspaceInitialized() {
logger.debug("System keyspaces initialized");
}
@Override
public void userKeyspaceInitialized() {
logger.debug("User keyspaces initialized");
if (node != null && SystemKeyspace.bootstrapComplete()) {
try {
this.hasMetadata = this.node.clusterService().hasMetaDataTable();
if (this.hasMetadata) {
activateAndWaitShards("before opening user keyspaces");
}
} catch(Throwable e) {
logger.warn("Unexpected error",e);
}
}
}
/**
* Called just before boostraping, and after CQL schema is complete,
* so elasticsearch mapping may be available from the CQL schema.
*/
@Override
public void beforeBootstrap() {
if (node != null) {
try {
// load mapping from schema jsut before bootstrapping C*
this.hasMetadata = this.node.clusterService().hasMetaDataTable();
activateAndWaitShards("before cassandra boostraping");
} catch(Throwable e) {
logger.error("Failed to load Elasticsearch mapping from CQL schema before bootstraping:", e);
}
}
}
public static final String ELASSANDRA_SETUP_CLASS="elassandra.setup.class";
public interface SetupListener {
public void onComplete();
}
public void register(SetupListener listener) {
setupListeners.add(listener);
}
public void onNodeStarted() {
for(SetupListener listener : setupListeners) {
try {
listener.onComplete();
logger.info("Setup class ["+ listener.getClass().getName()+"] onComplete done");
} catch(Exception e) {
logger.error("Setup class ["+ listener.getClass().getName()+"] failed:", e);
}
}
}
public static class ElassandraPlugin extends Plugin implements ClusterPlugin {
public ElassandraPlugin() {
super();
}
@Override
public void onNodeStarted() {
ElassandraDaemon.instance.onNodeStarted();
}
}
/**
* Called when C* does not bootstrap, and before CQL schema is completed.
* If we don't have yet found the elasticsearch mapping from the CQL schema, wait for the CQL schema to complete before trying to start elasticsearch.
* Obviously, the first seed node will wait (30s by default) for nothing (or use cassandra.ring_delay_ms=0).
*/
@Override
public void ringReady()
{
if (node != null)
{
// wait for schema before starting elasticsearch node
if (!activated)
{
try
{
logger.info("waiting "+StorageService.RING_DELAY+"ms for CQL schema to get the elasticsearch mapping");
// first sleep the delay to make sure we see all our peers
for (int i = 0; i < StorageService.RING_DELAY; i += 1000)
{
// if we see schema, we can proceed to the next check directly
if (!Schema.instance.getVersion().equals(SchemaConstants.emptyVersion))
{
logger.debug("got schema: {}", Schema.instance.getVersion());
break;
}
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
}
// if our schema hasn't matched yet, wait until it has
if (!MigrationManager.isReadyForBootstrap())
{
logger.info("waiting for schema information to complete");
MigrationManager.waitUntilReadyForBootstrap();
}
this.hasMetadata = this.node.clusterService().hasMetaDataTable();
} catch(Throwable e)
{
logger.warn("Failed to load elasticsearch mapping from CQL schema after after joining without boostraping:", e);
}
}
activateAndWaitShards((hasMetadata) ? "after getting the elasticsearch mapping from CQL schema" : "with empty elasticsearch mapping");
}
}
public void activateAndWaitShards(String source)
{
if (!activated) {
activated = true;
logger.info("Activating Elasticsearch, shards starting "+source);
node.activate();
node.clusterService().blockUntilShardsStarted();
logger.info("Elasticsearch shards started, ready to go on.");
}
}
/**
* hook for JSVC
*/
@Override
public void start() {
super.start();
}
/**
* hook for JSVC
*/
@Override
public void stop() {
super.stop();
if (node != null)
node.stop();
}
/**
* hook for JSVC
*/
@Override
public void init(String[] args) {
}
/**
* hook for JSVC
*/
@Override
public void activate() {
if (node != null)
node.activate();
}
/**
* hook for JSVC
*/
@Override
public void destroy() {
super.destroy();
if (node != null) {
try {
node.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (keepAliveLatch != null)
keepAliveLatch.countDown();
}
// for tests only
public Node newNode(Settings settings, Collection> classpathPlugins) {
Settings nodeSettings = nodeSettings(settings);
System.out.println("node settings="+nodeSettings.getAsGroups());
System.out.println("node plugins="+classpathPlugins);
// add ElassandraDaemon as a ClusterPlugin
List> classpathPlugins2 = Lists.newArrayList(classpathPlugins);
classpathPlugins2.add(ElassandraPlugin.class);
this.node = new Node(nodeSettings, classpathPlugins2);
return this.node;
}
public Settings getSettings() {
return nodeSettings(env.settings());
}
public Settings nodeSettings(Settings settings) {
return Settings.builder()
// overloadable settings from elasticsearch.yml
// by default, HTTP is bound to C* rpc address, Transport is bound to C* internal listen address.
.put("network.bind_host", DatabaseDescriptor.getRpcAddress().getHostAddress())
.put("network.publish_host", FBUtilities.getBroadcastRpcAddress().getHostAddress())
.put("transport.bind_host", FBUtilities.getLocalAddress().getHostAddress())
.put("transport.publish_host", Boolean.getBoolean("es.use_internal_address") ? FBUtilities.getLocalAddress().getHostAddress() : FBUtilities.getBroadcastAddress().getHostAddress())
.put("path.data", getElasticsearchDataDir())
.put(settings)
// not overloadable settings.
.put("discovery.type", "cassandra")
.put("node.data", true)
.put("node.master", true)
.put("node.name", CassandraDiscovery.buildNodeName(FBUtilities.getBroadcastAddress()))
.put("node.attr.dc", DatabaseDescriptor.getLocalDataCenter())
.put("node.attr.rack", DatabaseDescriptor.getEndpointSnitch().getRack(FBUtilities.getBroadcastAddress()))
.put("cluster.name", ClusterService.getElasticsearchClusterName(env.settings()))
.build();
}
public static Client client() {
if ((instance.node != null) && (!instance.node.isClosed()))
return instance.node.client();
return null;
}
public static Injector injector() {
if ((instance.node != null) && (!instance.node.isClosed()))
return instance.node.injector();
return null;
}
public static String getHomeDir() {
String cassandra_home = System.getenv("CASSANDRA_HOME");
if (cassandra_home == null) {
cassandra_home = System.getProperty("cassandra.home", System.getProperty("path.home"));
if (cassandra_home == null)
throw new IllegalStateException("Cannot start, environnement variable CASSANDRA_HOME and system properties cassandra.home or path.home are null. Please set one of these to start properly.");
}
return cassandra_home;
}
public static String getConfigDir() {
String cassandra_conf = System.getenv("CASSANDRA_CONF");
if (cassandra_conf == null) {
cassandra_conf = System.getProperty("cassandra.conf", System.getProperty("path.conf",getHomeDir()+"/conf"));
}
return cassandra_conf;
}
public static String getElasticsearchDataDir() {
String cassandra_storage = System.getProperty("cassandra.storagedir", getHomeDir() + File.separator + "data");
return cassandra_storage + File.separator + "elasticsearch.data";
}
public static void main(String[] args) {
try
{
DatabaseDescriptor.daemonInitialization();
DatabaseDescriptor.createAllDirectories();
}
catch (ExceptionInInitializerError e)
{
System.out.println("Exception (" + e.getClass().getName() + ") encountered during startup: " + e.getMessage());
String errorMessage = buildErrorMessage("Initialization", e);
System.err.println(errorMessage);
System.err.flush();
System.exit(3);
}
boolean foreground = System.getProperty("cassandra-foreground") != null;
// handle the wrapper system property, if its a service, don't run as a
// service
if (System.getProperty("wrapper.service", "XXX").equalsIgnoreCase("true")) {
foreground = false;
}
if (System.getProperty("es.max-open-files", "false").equals("true")) {
Logger logger = Loggers.getLogger(Bootstrap.class);
logger.info("max_open_files [{}]", ProcessProbe.getInstance().getMaxFileDescriptorCount());
}
// warn if running using the client VM
if (JvmInfo.jvmInfo().getVmName().toLowerCase(Locale.ROOT).contains("client")) {
Logger logger = Loggers.getLogger(Bootstrap.class);
logger.warn("jvm uses the client vm, make sure to run `java` with the server vm for best performance by adding `-server` to the command line");
}
// disable netty setAvailableProcessors by Elasticsearch (set by Cassandra).
AccessController.doPrivileged(new PrivilegedAction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy