diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 2a8984e59d4..2cb4fb6450a 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -135,6 +135,8 @@ final class Bootstrap { JNANatives.trySetMaxNumberOfThreads(); + JNANatives.trySetMaxSizeVirtualMemory(); + // init lucene random seed. it will use /dev/urandom where available: StringHelper.randomId(); } diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java index 433dd4498a4..0a31da34c60 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java @@ -123,6 +123,7 @@ final class BootstrapCheck { if (Constants.LINUX) { checks.add(new MaxNumberOfThreadsCheck()); } + checks.add(new MaxSizeVirtualMemoryCheck()); return Collections.unmodifiableList(checks); } @@ -249,4 +250,27 @@ final class BootstrapCheck { } + static class MaxSizeVirtualMemoryCheck implements Check { + + @Override + public boolean check() { + return getMaxSizeVirtualMemory() != Long.MIN_VALUE && getMaxSizeVirtualMemory() != JNACLibrary.RLIM_INFINITY; + } + + @Override + public String errorMessage() { + return String.format( + Locale.ROOT, + "max size virtual memory [%d] for user [%s] likely too low, increase to [unlimited]", + getMaxSizeVirtualMemory(), + BootstrapInfo.getSystemProperties().get("user.name")); + } + + // visible for testing + long getMaxSizeVirtualMemory() { + return JNANatives.MAX_SIZE_VIRTUAL_MEMORY; + } + + } + } diff --git a/core/src/main/java/org/elasticsearch/bootstrap/JNACLibrary.java b/core/src/main/java/org/elasticsearch/bootstrap/JNACLibrary.java index 573f3d5be3e..5d1369b21f7 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/JNACLibrary.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/JNACLibrary.java @@ -39,6 +39,7 @@ final class JNACLibrary { public static final int MCL_CURRENT = 1; public static final int ENOMEM = 12; public static final int RLIMIT_MEMLOCK = Constants.MAC_OS_X ? 6 : 8; + public static final int RLIMIT_AS = Constants.MAC_OS_X ? 5 : 9; public static final long RLIM_INFINITY = Constants.MAC_OS_X ? 9223372036854775807L : -1L; static { diff --git a/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java b/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java index 0ea8da6a9be..e55d38a0f72 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java @@ -52,6 +52,8 @@ class JNANatives { // the user ID that owns the running Elasticsearch process static long MAX_NUMBER_OF_THREADS = -1; + static long MAX_SIZE_VIRTUAL_MEMORY = Long.MIN_VALUE; + static void tryMlockall() { int errno = Integer.MIN_VALUE; String errMsg = null; @@ -124,6 +126,17 @@ class JNANatives { } } + static void trySetMaxSizeVirtualMemory() { + if (Constants.LINUX || Constants.MAC_OS_X) { + final JNACLibrary.Rlimit rlimit = new JNACLibrary.Rlimit(); + if (JNACLibrary.getrlimit(JNACLibrary.RLIMIT_AS, rlimit) == 0) { + MAX_SIZE_VIRTUAL_MEMORY = rlimit.rlim_cur.longValue(); + } else { + logger.warn("unable to retrieve max size virtual memory [" + JNACLibrary.strerror(Native.getLastError()) + "]"); + } + } + } + static String rlimitToString(long value) { assert Constants.LINUX || Constants.MAC_OS_X; if (value == JNACLibrary.RLIM_INFINITY) { diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java index 3c269c39004..3e5dc892889 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapCheckTests.java @@ -157,6 +157,33 @@ public class BootstrapCheckTests extends ESTestCase { BootstrapCheck.check(true, Collections.singletonList(check)); } + public void testMaxSizeVirtualMemory() { + final long limit = JNACLibrary.RLIM_INFINITY; + final AtomicLong maxSizeVirtualMemory = new AtomicLong(randomInt()); + final BootstrapCheck.MaxSizeVirtualMemoryCheck check = new BootstrapCheck.MaxSizeVirtualMemoryCheck() { + @Override + long getMaxSizeVirtualMemory() { + return maxSizeVirtualMemory.get(); + } + }; + + try { + BootstrapCheck.check(true, Collections.singletonList(check)); + fail("should have failed due to max size virtual memory too low"); + } catch (final RuntimeException e) { + assertThat(e.getMessage(), containsString("max size virtual memory")); + } + + maxSizeVirtualMemory.set(limit); + + BootstrapCheck.check(true, Collections.singletonList(check)); + + // nothing should happen if max size virtual memory is not + // available + maxSizeVirtualMemory.set(Long.MIN_VALUE); + BootstrapCheck.check(true, Collections.singletonList(check)); + } + public void testEnforceLimits() { final Set enforceSettings = BootstrapCheck.enforceSettings(); final Setting setting = randomFrom(Arrays.asList(enforceSettings.toArray(new Setting[enforceSettings.size()]))); diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilJNANativesTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilJNANativesTests.java index 080eee2501b..1ef5bdfcfae 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilJNANativesTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilJNANativesTests.java @@ -27,7 +27,9 @@ import java.io.IOException; import java.nio.file.Files; import java.util.List; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; public class EvilJNANativesTests extends ESTestCase { @@ -49,4 +51,26 @@ public class EvilJNANativesTests extends ESTestCase { assertThat(JNANatives.MAX_NUMBER_OF_THREADS, equalTo(-1L)); } } + + public void testSetMaxSizeVirtualMemory() throws IOException { + if (Constants.LINUX) { + final List lines = Files.readAllLines(PathUtils.get("/proc/self/limits")); + if (!lines.isEmpty()) { + for (String line : lines) { + if (line != null && line.startsWith("Max address space")) { + final String[] fields = line.split("\\s+"); + final String limit = fields[3]; + assertEquals(JNANatives.rlimitToString(JNANatives.MAX_SIZE_VIRTUAL_MEMORY), limit); + return; + } + } + } + fail("should have read max size virtual memory from /proc/self/limits"); + } else if (Constants.MAC_OS_X) { + assertThat(JNANatives.MAX_SIZE_VIRTUAL_MEMORY, anyOf(equalTo(Long.MIN_VALUE), greaterThanOrEqualTo(0L))); + } else { + assertThat(JNANatives.MAX_SIZE_VIRTUAL_MEMORY, equalTo(Long.MIN_VALUE)); + } + } + }