Skip to content

Commit 6d6cd49

Browse files
committed
Add max file size bootstrap check
This commit adds a bootstrap check for the maximum file size, and ensures the limit is set correctly when Elasticsearch is installed as a service on systemd-based systems. Relates #25974
1 parent b5e5612 commit 6d6cd49

File tree

9 files changed

+125
-3
lines changed

9 files changed

+125
-3
lines changed

core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ public boolean handle(int code) {
147147

148148
Natives.trySetMaxNumberOfThreads();
149149
Natives.trySetMaxSizeVirtualMemory();
150+
Natives.trySetMaxFileSize();
150151

151152
// init lucene random seed. it will use /dev/urandom where available:
152153
StringHelper.randomId();

core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ static List<BootstrapCheck> checks(final Settings settings) {
193193
if (Constants.LINUX || Constants.MAC_OS_X) {
194194
checks.add(new MaxSizeVirtualMemoryCheck());
195195
}
196+
if (Constants.LINUX || Constants.MAC_OS_X) {
197+
checks.add(new MaxFileSizeCheck());
198+
}
196199
if (Constants.LINUX) {
197200
checks.add(new MaxMapCountCheck());
198201
}
@@ -367,6 +370,36 @@ long getMaxSizeVirtualMemory() {
367370

368371
}
369372

373+
/**
374+
* Bootstrap check that the maximum file size is unlimited (otherwise Elasticsearch could run in to an I/O exception writing files).
375+
*/
376+
static class MaxFileSizeCheck implements BootstrapCheck {
377+
378+
@Override
379+
public boolean check() {
380+
final long maxFileSize = getMaxFileSize();
381+
return maxFileSize != Long.MIN_VALUE && maxFileSize != getRlimInfinity();
382+
}
383+
384+
@Override
385+
public String errorMessage() {
386+
return String.format(
387+
Locale.ROOT,
388+
"max file size [%d] for user [%s] is too low, increase to [unlimited]",
389+
getMaxFileSize(),
390+
BootstrapInfo.getSystemProperties().get("user.name"));
391+
}
392+
393+
long getRlimInfinity() {
394+
return JNACLibrary.RLIM_INFINITY;
395+
}
396+
397+
long getMaxFileSize() {
398+
return JNANatives.MAX_FILE_SIZE;
399+
}
400+
401+
}
402+
370403
static class MaxMapCountCheck implements BootstrapCheck {
371404

372405
private static final long LIMIT = 1 << 18;

core/src/main/java/org/elasticsearch/bootstrap/JNACLibrary.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ final class JNACLibrary {
4040
public static final int ENOMEM = 12;
4141
public static final int RLIMIT_MEMLOCK = Constants.MAC_OS_X ? 6 : 8;
4242
public static final int RLIMIT_AS = Constants.MAC_OS_X ? 5 : 9;
43+
public static final int RLIMIT_FSIZE = Constants.MAC_OS_X ? 1 : 1;
4344
public static final long RLIM_INFINITY = Constants.MAC_OS_X ? 9223372036854775807L : -1L;
4445

4546
static {

core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ private JNANatives() {}
5555

5656
static long MAX_SIZE_VIRTUAL_MEMORY = Long.MIN_VALUE;
5757

58+
static long MAX_FILE_SIZE = Long.MIN_VALUE;
59+
5860
static void tryMlockall() {
5961
int errno = Integer.MIN_VALUE;
6062
String errMsg = null;
@@ -138,6 +140,17 @@ static void trySetMaxSizeVirtualMemory() {
138140
}
139141
}
140142

143+
static void trySetMaxFileSize() {
144+
if (Constants.LINUX || Constants.MAC_OS_X) {
145+
final JNACLibrary.Rlimit rlimit = new JNACLibrary.Rlimit();
146+
if (JNACLibrary.getrlimit(JNACLibrary.RLIMIT_FSIZE, rlimit) == 0) {
147+
MAX_FILE_SIZE = rlimit.rlim_cur.longValue();
148+
} else {
149+
logger.warn("unable to retrieve max file size [" + JNACLibrary.strerror(Native.getLastError()) + "]");
150+
}
151+
}
152+
}
153+
141154
static String rlimitToString(long value) {
142155
assert Constants.LINUX || Constants.MAC_OS_X;
143156
if (value == JNACLibrary.RLIM_INFINITY) {

core/src/main/java/org/elasticsearch/bootstrap/Natives.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ static void trySetMaxSizeVirtualMemory() {
129129
JNANatives.trySetMaxSizeVirtualMemory();
130130
}
131131

132+
static void trySetMaxFileSize() {
133+
if (!JNA_AVAILABLE) {
134+
logger.warn("cannot getrlimit RLIMIT_FSIZE because JNA is not available");
135+
return;
136+
}
137+
JNANatives.trySetMaxFileSize();
138+
}
139+
132140
static boolean isSystemCallFilterInstalled() {
133141
if (!JNA_AVAILABLE) {
134142
return false;

core/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ long getRlimInfinity() {
330330
}
331331
};
332332

333-
334333
final NodeValidationException e = expectThrows(
335334
NodeValidationException.class,
336335
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory"));
@@ -340,12 +339,40 @@ long getRlimInfinity() {
340339

341340
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
342341

343-
// nothing should happen if max size virtual memory is not
344-
// available
342+
// nothing should happen if max size virtual memory is not available
345343
maxSizeVirtualMemory.set(Long.MIN_VALUE);
346344
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
347345
}
348346

347+
public void testMaxFileSizeCheck() throws NodeValidationException {
348+
final long rlimInfinity = Constants.MAC_OS_X ? 9223372036854775807L : -1L;
349+
final AtomicLong maxFileSize = new AtomicLong(randomIntBetween(0, Integer.MAX_VALUE));
350+
final BootstrapChecks.MaxFileSizeCheck check = new BootstrapChecks.MaxFileSizeCheck() {
351+
@Override
352+
long getMaxFileSize() {
353+
return maxFileSize.get();
354+
}
355+
356+
@Override
357+
long getRlimInfinity() {
358+
return rlimInfinity;
359+
}
360+
};
361+
362+
final NodeValidationException e = expectThrows(
363+
NodeValidationException.class,
364+
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize"));
365+
assertThat(e.getMessage(), containsString("max file size"));
366+
367+
maxFileSize.set(rlimInfinity);
368+
369+
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize");
370+
371+
// nothing should happen if max file size is not available
372+
maxFileSize.set(Long.MIN_VALUE);
373+
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize");
374+
}
375+
349376
public void testMaxMapCountCheck() throws NodeValidationException {
350377
final int limit = 1 << 18;
351378
final AtomicLong maxMapCount = new AtomicLong(randomIntBetween(1, limit - 1));

distribution/src/main/packaging/systemd/elasticsearch.service

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ LimitNOFILE=65536
3232
# Specifies the maximum number of processes
3333
LimitNPROC=4096
3434

35+
# Specifies the maximum file size
36+
LimitFSIZE=infinity
37+
3538
# Specifies the maximum number of bytes of memory that may be locked into RAM
3639
# Set to "infinity" if you use the 'bootstrap.memory_lock: true' option
3740
# in elasticsearch.yml and 'MAX_LOCKED_MEMORY=unlimited' in ${path.env}

docs/reference/setup/bootstrap-checks.asciidoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ address space. This can be done via `/etc/security/limits.conf` using
130130
the `as` setting to `unlimited` (note that you might have to increase
131131
the limits for the `root` user too).
132132

133+
=== Max file size check
134+
135+
The segment files that are the components of individual shards and the translog
136+
generations that are components of the translog can get large (exceeding
137+
multiple gigabytes). On systems where the max size of files that can be created
138+
by the Elasticsearch process is limited, this can lead to failed
139+
writes. Therefore, the safest option here is that the max file size is unlimited
140+
and that is what the max file size bootstrap check enforces. To pass the max
141+
file check, you must configure your system to allow the Elasticsearch process
142+
the ability to write files of unlimited size. This can be done via
143+
`/etc/security/limits.conf` using the `fsize` setting to `unlimited` (note that
144+
you might have to increase the limits for the `root` user too).
145+
133146
=== Maximum map count check
134147

135148
Continuing from the previous <<max-size-virtual-memory-check,point>>, to

qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilJNANativesTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,27 @@ public void testSetMaxSizeVirtualMemory() throws IOException {
8080
}
8181
}
8282

83+
public void testSetMaxFileSize() throws IOException {
84+
if (Constants.LINUX) {
85+
final List<String> lines = Files.readAllLines(PathUtils.get("/proc/self/limits"));
86+
for (final String line : lines) {
87+
if (line != null && line.startsWith("Max file size")) {
88+
final String[] fields = line.split("\\s+");
89+
final String limit = fields[3];
90+
assertThat(
91+
JNANatives.rlimitToString(JNANatives.MAX_FILE_SIZE),
92+
equalTo(limit));
93+
return;
94+
}
95+
}
96+
fail("should have read max file size from /proc/self/limits");
97+
} else if (Constants.MAC_OS_X) {
98+
assertThat(
99+
JNANatives.MAX_FILE_SIZE,
100+
anyOf(equalTo(Long.MIN_VALUE), greaterThanOrEqualTo(0L)));
101+
} else {
102+
assertThat(JNANatives.MAX_FILE_SIZE, equalTo(Long.MIN_VALUE));
103+
}
104+
}
105+
83106
}

0 commit comments

Comments
 (0)