Browse Source

[Bootstrap] Throw exception if the JVM will corrupt data.

Detect the worst-offenders, all IBM versions and several known hotspot
versions that can cause index corruption, and fail on startup.

Provide/detect compiler workarounds when they exist, but warn about
performance degradation.

In all cases the check can be bypassed completely with a safety
switch via undocumented system property (es.bypass.vm.check=true)

Closes #7580
Robert Muir 10 years ago
parent
commit
6da99b3ef0

+ 4 - 0
src/main/java/org/elasticsearch/bootstrap/Bootstrap.java

@@ -193,6 +193,10 @@ public class Bootstrap {
                 Loggers.disableConsoleLogging();
                 System.out.close();
             }
+
+            // fail if using broken version
+            JVMCheck.check();
+
             bootstrap.setup(true, tuple);
 
             stage = "Startup";

+ 139 - 0
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<String,HotspotBug> JVM_BROKEN_HOTSPOT_VERSIONS;
+    
+    static {
+        Map<String,HotspotBug> 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();
+    }
+}