diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 6bf899f6fa172..ec066192fbd69 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -206,6 +206,10 @@ public static void main(String[] args) { Loggers.disableConsoleLogging(); System.out.close(); } + + // fail if using broken version + JVMCheck.check(); + bootstrap.setup(true, tuple); stage = "Startup"; diff --git a/src/main/java/org/elasticsearch/bootstrap/JVMCheck.java b/src/main/java/org/elasticsearch/bootstrap/JVMCheck.java new file mode 100644 index 0000000000000..94024750fa692 --- /dev/null +++ b/src/main/java/org/elasticsearch/bootstrap/JVMCheck.java @@ -0,0 +1,139 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you 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.elasticsearch.bootstrap; + +import org.apache.lucene.util.Constants; +import org.elasticsearch.common.logging.Loggers; + +import java.lang.management.ManagementFactory; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** Checks that the JVM is ok and won't cause index corruption */ +public class JVMCheck { + + /** + * URL with latest JVM recommendations + */ + static final String JVM_RECOMMENDATIONS = "http://www.elastic.co/guide/en/elasticsearch/reference/current/_installation.html"; + + /** + * System property which if set causes us to bypass the check completely (but issues a warning in doing so) + */ + static final String JVM_BYPASS = "es.bypass.vm.check"; + + /** + * Metadata and messaging for hotspot bugs. + */ + static class HotspotBug { + + /** OpenJDK bug URL */ + final String bugUrl; + + /** Compiler workaround flag (null if there is no workaround) */ + final String workAround; + + HotspotBug(String bugUrl, String workAround) { + this.bugUrl = bugUrl; + this.workAround = workAround; + } + + /** Returns an error message to the user for a broken version */ + String getErrorMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("Java version: ").append(Constants.JAVA_VERSION); + sb.append(" suffers from critical bug ").append(bugUrl); + sb.append(" which can cause data corruption."); + sb.append(System.lineSeparator()); + sb.append("Please upgrade the JVM, see ").append(JVM_RECOMMENDATIONS); + sb.append(" for current recommendations."); + if (workAround != null) { + sb.append(System.lineSeparator()); + sb.append("If you absolutely cannot upgrade, please add ").append(workAround); + sb.append(" to the JVM_OPTS environment variable."); + sb.append(System.lineSeparator()); + sb.append("Upgrading is preferred, this workaround will result in degraded performance."); + } + return sb.toString(); + } + + /** Warns the user when a workaround is being used to dodge the bug */ + String getWarningMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("Workaround flag ").append(workAround); + sb.append(" for bug ").append(bugUrl); + sb.append(" found. "); + sb.append(System.lineSeparator()); + sb.append("This will result in degraded performance!"); + sb.append(System.lineSeparator()); + sb.append("Upgrading is preferred, see ").append(JVM_RECOMMENDATIONS); + sb.append(" for current recommendations."); + return sb.toString(); + } + } + + /** mapping of hotspot version to hotspot bug information for the most serious bugs */ + static final Map JVM_BROKEN_HOTSPOT_VERSIONS; + + static { + Map bugs = new HashMap<>(); + + // 1.7.0: loop optimizer bug + bugs.put("21.0-b17", new HotspotBug("https://bugs.openjdk.java.net/browse/JDK-7070134", "-XX:-UseLoopPredicate")); + // register allocation issues (technically only x86/amd64). This impacted update 40, 45, and 51 + bugs.put("24.0-b56", new HotspotBug("https://bugs.openjdk.java.net/browse/JDK-8024830", "-XX:-UseSuperWord")); + bugs.put("24.45-b08", new HotspotBug("https://bugs.openjdk.java.net/browse/JDK-8024830", "-XX:-UseSuperWord")); + bugs.put("24.51-b03", new HotspotBug("https://bugs.openjdk.java.net/browse/JDK-8024830", "-XX:-UseSuperWord")); + + JVM_BROKEN_HOTSPOT_VERSIONS = Collections.unmodifiableMap(bugs); + } + + /** + * Checks that the current JVM is "ok". This means it doesn't have severe bugs that cause data corruption. + */ + static void check() { + if (Boolean.parseBoolean(System.getProperty(JVM_BYPASS))) { + Loggers.getLogger(JVMCheck.class).warn("bypassing jvm version check for version [{}], this can result in data corruption!", Constants.JAVA_VERSION); + } else if ("Oracle Corporation".equals(Constants.JVM_VENDOR)) { + HotspotBug bug = JVM_BROKEN_HOTSPOT_VERSIONS.get(Constants.JVM_VERSION); + if (bug != null) { + if (bug.workAround != null && ManagementFactory.getRuntimeMXBean().getInputArguments().contains(bug.workAround)) { + Loggers.getLogger(JVMCheck.class).warn(bug.getWarningMessage()); + } else { + throw new RuntimeException(bug.getErrorMessage()); + } + } + } else if ("IBM Corporation".equals(Constants.JVM_VENDOR)) { + // currently any JVM from IBM will easily result in index corruption. + StringBuilder sb = new StringBuilder(); + sb.append("IBM runtimes suffer from several bugs which can cause data corruption."); + sb.append(System.lineSeparator()); + sb.append("Please upgrade the JVM, see ").append(JVM_RECOMMENDATIONS); + sb.append(" for current recommendations."); + throw new RuntimeException(sb.toString()); + } + } + + /** Command line driver for convenience */ + public static void main(String args[]) { + check(); + } +}