Add max size virtual memory check

This commit adds a bootstrap check on Linux and OS X for the max size of
virtual memory (address space) to the user running the Elasticsearch
process.

Closes #16935
This commit is contained in:
Jason Tedor 2016-03-03 12:51:00 -05:00
parent c52b1f3a7c
commit 8004c51c17
6 changed files with 91 additions and 0 deletions

View File

@ -135,6 +135,8 @@ final class Bootstrap {
JNANatives.trySetMaxNumberOfThreads();
JNANatives.trySetMaxSizeVirtualMemory();
// init lucene random seed. it will use /dev/urandom where available:
StringHelper.randomId();
}

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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<Setting> enforceSettings = BootstrapCheck.enforceSettings();
final Setting setting = randomFrom(Arrays.asList(enforceSettings.toArray(new Setting[enforceSettings.size()])));

View File

@ -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<String> 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));
}
}
}