blob: b24fc4cefbeff284faed7c56f3a3bdef1708b96b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.lucene.misc.store;
import com.carrotsearch.randomizedtesting.RandomizedTest;
import java.io.EOFException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.OptionalLong;
import org.apache.lucene.document.*;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.store.*;
import org.junit.BeforeClass;
public class TestDirectIODirectory extends BaseDirectoryTestCase {
@BeforeClass
public static void checkSupported() throws IOException {
assumeTrue(
"This test required a JDK version that has support for ExtendedOpenOption.DIRECT",
DirectIODirectory.ExtendedOpenOption_DIRECT != null);
// jdk supports it, let's check that the filesystem does too
Path path = createTempDir("directIOProbe");
try (Directory dir = open(path);
IndexOutput out = dir.createOutput("out", IOContext.DEFAULT)) {
out.writeString("test");
} catch (IOException e) {
assumeNoException("test requires filesystem that supports Direct IO", e);
}
}
private static DirectIODirectory open(Path path) throws IOException {
return new DirectIODirectory(FSDirectory.open(path)) {
@Override
protected boolean useDirectIO(String name, IOContext context, OptionalLong fileLength) {
return true;
}
};
}
@Override
protected DirectIODirectory getDirectory(Path path) throws IOException {
return open(path);
}
public void testIndexWriteRead() throws IOException {
try (Directory dir = getDirectory(createTempDir("testDirectIODirectory"))) {
try (RandomIndexWriter iw = new RandomIndexWriter(random(), dir)) {
Document doc = new Document();
Field field = newField("field", "foo bar", TextField.TYPE_STORED);
doc.add(field);
iw.addDocument(doc);
iw.commit();
}
try (IndexReader ir = DirectoryReader.open(dir)) {
IndexSearcher s = newSearcher(ir);
assertEquals(1, s.count(new PhraseQuery("field", "foo", "bar")));
}
}
}
public void testIllegalEOFWithFileSizeMultipleOfBlockSize() throws Exception {
Path path = createTempDir("testIllegalEOF");
final int fileSize = Math.toIntExact(Files.getFileStore(path).getBlockSize()) * 2;
try (Directory dir = getDirectory(path)) {
IndexOutput o = dir.createOutput("out", newIOContext(random()));
byte[] b = new byte[fileSize];
o.writeBytes(b, 0, fileSize);
o.close();
IndexInput i = dir.openInput("out", newIOContext(random()));
i.seek(fileSize);
// Seeking past EOF should always throw EOFException
expectThrows(
EOFException.class, () -> i.seek(fileSize + RandomizedTest.randomIntBetween(1, 2048)));
// Reading immediately after seeking past EOF should throw EOFException
expectThrows(EOFException.class, () -> i.readByte());
i.close();
}
}
public void testReadPastEOFShouldThrowEOFExceptionWithEmptyFile() throws Exception {
// fileSize needs to be 0 to test this condition. Do not randomized.
final int fileSize = 0;
try (Directory dir = getDirectory(createTempDir("testReadPastEOF"))) {
try (IndexOutput o = dir.createOutput("out", newIOContext(random()))) {
o.writeBytes(new byte[fileSize], 0, fileSize);
}
try (IndexInput i = dir.openInput("out", newIOContext(random()))) {
i.seek(fileSize);
expectThrows(EOFException.class, () -> i.readByte());
expectThrows(EOFException.class, () -> i.readBytes(new byte[1], 0, 1));
}
try (IndexInput i = dir.openInput("out", newIOContext(random()))) {
expectThrows(
EOFException.class, () -> i.seek(fileSize + RandomizedTest.randomIntBetween(1, 2048)));
expectThrows(EOFException.class, () -> i.readByte());
expectThrows(EOFException.class, () -> i.readBytes(new byte[1], 0, 1));
}
try (IndexInput i = dir.openInput("out", newIOContext(random()))) {
expectThrows(EOFException.class, () -> i.readByte());
}
try (IndexInput i = dir.openInput("out", newIOContext(random()))) {
expectThrows(EOFException.class, () -> i.readBytes(new byte[1], 0, 1));
}
}
}
public void testSeekPastEOFAndRead() throws Exception {
try (Directory dir = getDirectory(createTempDir("testSeekPastEOF"))) {
final int len = random().nextInt(2048);
try (IndexOutput o = dir.createOutput("out", newIOContext(random()))) {
byte[] b = new byte[len];
o.writeBytes(b, 0, len);
}
try (IndexInput i = dir.openInput("out", newIOContext(random()))) {
// Seeking past EOF should always throw EOFException
expectThrows(
EOFException.class, () -> i.seek(len + RandomizedTest.randomIntBetween(1, 2048)));
// Reading immediately after seeking past EOF should throw EOFException
expectThrows(EOFException.class, () -> i.readByte());
}
}
}
public void testUseDirectIODefaults() throws Exception {
Path path = createTempDir("testUseDirectIODefaults");
try (DirectIODirectory dir = new DirectIODirectory(FSDirectory.open(path))) {
long largeSize = DirectIODirectory.DEFAULT_MIN_BYTES_DIRECT + random().nextInt(10_000);
long smallSize =
random().nextInt(Math.toIntExact(DirectIODirectory.DEFAULT_MIN_BYTES_DIRECT));
int numDocs = random().nextInt(1000);
assertFalse(dir.useDirectIO("dummy", IOContext.DEFAULT, OptionalLong.empty()));
assertTrue(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, largeSize, true, -1)),
OptionalLong.empty()));
assertFalse(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, smallSize, true, -1)),
OptionalLong.empty()));
assertTrue(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, largeSize, true, -1)),
OptionalLong.of(largeSize)));
assertFalse(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, smallSize, true, -1)),
OptionalLong.of(smallSize)));
assertFalse(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, smallSize, true, -1)),
OptionalLong.of(largeSize)));
assertFalse(
dir.useDirectIO(
"dummy",
new IOContext(new MergeInfo(numDocs, largeSize, true, -1)),
OptionalLong.of(smallSize)));
assertFalse(
dir.useDirectIO(
"dummy", new IOContext(new FlushInfo(numDocs, largeSize)), OptionalLong.empty()));
assertFalse(
dir.useDirectIO(
"dummy", new IOContext(new FlushInfo(numDocs, smallSize)), OptionalLong.empty()));
assertFalse(
dir.useDirectIO(
"dummy",
new IOContext(new FlushInfo(numDocs, largeSize)),
OptionalLong.of(largeSize)));
}
}
}