Skip to content

Commit 105f93c

Browse files
authored
Catch FileSystemException when opening direct IO (#128834)
1 parent 906f207 commit 105f93c

File tree

2 files changed

+45
-18
lines changed

2 files changed

+45
-18
lines changed

server/src/internalClusterTest/java/org/elasticsearch/index/store/DirectIOIT.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@
4343
@LuceneTestCase.SuppressCodecs("*") // only use our own codecs
4444
public class DirectIOIT extends ESIntegTestCase {
4545

46+
private static boolean SUPPORTED;
47+
4648
@BeforeClass
4749
public static void checkSupported() {
4850
assumeTrue("Direct IO is not enabled", ES818BinaryQuantizedVectorsFormat.USE_DIRECT_IO);
4951

5052
Path path = createTempDir("directIOProbe");
5153
try (Directory dir = open(path); IndexOutput out = dir.createOutput("out", IOContext.DEFAULT)) {
5254
out.writeString("test");
55+
SUPPORTED = true;
5356
} catch (IOException e) {
54-
assumeNoException("test requires filesystem that supports Direct IO", e);
57+
SUPPORTED = false;
5558
}
5659
}
5760

@@ -109,15 +112,21 @@ static void assertBBQIndexType(String type) {
109112
@TestLogging(value = "org.elasticsearch.index.store.FsDirectoryFactory:DEBUG", reason = "to capture trace logging for direct IO")
110113
public void testDirectIOUsed() {
111114
try (MockLog mockLog = MockLog.capture(FsDirectoryFactory.class)) {
112-
// we're just looking for some evidence direct IO is used
113-
mockLog.addExpectation(
114-
new MockLog.PatternSeenEventExpectation(
115+
// we're just looking for some evidence direct IO is used (or not)
116+
MockLog.LoggingExpectation expectation = SUPPORTED
117+
? new MockLog.PatternSeenEventExpectation(
115118
"Direct IO used",
116119
FsDirectoryFactory.class.getCanonicalName(),
117120
Level.DEBUG,
118121
"Opening .*\\.vec with direct IO"
119122
)
120-
);
123+
: new MockLog.PatternSeenEventExpectation(
124+
"Direct IO not used",
125+
FsDirectoryFactory.class.getCanonicalName(),
126+
Level.DEBUG,
127+
"Could not open .*\\.vec with direct IO"
128+
);
129+
mockLog.addExpectation(expectation);
121130

122131
indexVectors();
123132

server/src/main/java/org/elasticsearch/index/store/FsDirectoryFactory.java

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.lucene.store.NativeFSLockFactory;
2323
import org.apache.lucene.store.ReadAdvice;
2424
import org.apache.lucene.store.SimpleFSLockFactory;
25+
import org.elasticsearch.common.Strings;
2526
import org.elasticsearch.common.settings.Setting;
2627
import org.elasticsearch.common.settings.Setting.Property;
2728
import org.elasticsearch.common.util.FeatureFlag;
@@ -36,6 +37,7 @@
3637
import org.elasticsearch.plugins.IndexStorePlugin;
3738

3839
import java.io.IOException;
40+
import java.nio.file.FileSystemException;
3941
import java.nio.file.Files;
4042
import java.nio.file.Path;
4143
import java.util.HashSet;
@@ -157,22 +159,38 @@ protected boolean useDirectIO(String name, IOContext context, OptionalLong fileL
157159

158160
@Override
159161
public IndexInput openInput(String name, IOContext context) throws IOException {
162+
Throwable directIOException = null;
160163
if (directIODelegate != null && context.hints().contains(DirectIOHint.INSTANCE)) {
161164
ensureOpen();
162165
ensureCanRead(name);
163-
Log.debug("Opening {} with direct IO", name);
164-
return directIODelegate.openInput(name, context);
165-
} else if (useDelegate(name, context)) {
166-
// we need to do these checks on the outer directory since the inner doesn't know about pending deletes
167-
ensureOpen();
168-
ensureCanRead(name);
169-
// we only use the mmap to open inputs. Everything else is managed by the NIOFSDirectory otherwise
170-
// we might run into trouble with files that are pendingDelete in one directory but still
171-
// listed in listAll() from the other. We on the other hand don't want to list files from both dirs
172-
// and intersect for perf reasons.
173-
return delegate.openInput(name, context);
174-
} else {
175-
return super.openInput(name, context);
166+
try {
167+
Log.debug("Opening {} with direct IO", name);
168+
return directIODelegate.openInput(name, context);
169+
} catch (FileSystemException e) {
170+
Log.debug(() -> Strings.format("Could not open %s with direct IO", name), e);
171+
directIOException = e;
172+
// and fallthrough to normal opening below
173+
}
174+
}
175+
176+
try {
177+
if (useDelegate(name, context)) {
178+
// we need to do these checks on the outer directory since the inner doesn't know about pending deletes
179+
ensureOpen();
180+
ensureCanRead(name);
181+
// we only use the mmap to open inputs. Everything else is managed by the NIOFSDirectory otherwise
182+
// we might run into trouble with files that are pendingDelete in one directory but still
183+
// listed in listAll() from the other. We on the other hand don't want to list files from both dirs
184+
// and intersect for perf reasons.
185+
return delegate.openInput(name, context);
186+
} else {
187+
return super.openInput(name, context);
188+
}
189+
} catch (Throwable t) {
190+
if (directIOException != null) {
191+
t.addSuppressed(directIOException);
192+
}
193+
throw t;
176194
}
177195
}
178196

0 commit comments

Comments
 (0)