LUCENE-6614: Improve partition detection in IOUtils#spins() so it works with NVMe drives

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1687883 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2015-06-27 03:35:00 +00:00
parent 878881a77a
commit ff5e36abcc
4 changed files with 90 additions and 13 deletions

View File

@ -179,6 +179,9 @@ Bug fixes
* LUCENE-6608: Fix potential resource leak in BigramDictionary.
(Rishabh Patel via Uwe Schindler)
* LUCENE-6614: Improve partition detection in IOUtils#spins() so it
works with NVMe drives. (Uwe Schindler, Mike McCandless)
Changes in Runtime Behavior
* LUCENE-6501: The subreader structure in ParallelCompositeReader

View File

@ -28,6 +28,7 @@ import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
@ -493,23 +494,28 @@ public final class IOUtils {
// /devices/XXX -> sda0
devName = path.getRoot().resolve(devName).toRealPath().getFileName().toString();
// now read:
Path sysinfo = path.getRoot().resolve("sys/block");
Path devinfo = sysinfo.resolve(devName);
// tear away partition numbers until we find it.
while (!Files.exists(devinfo)) {
if (!devName.isEmpty() && Character.isDigit(devName.charAt(devName.length()-1))) {
devName = devName.substring(0, devName.length()-1);
} else {
break; // give up
// now try to find the longest matching device folder in /sys/block
// (that starts with our dev name):
Path sysinfo = path.getRoot().resolve("sys").resolve("block");
Path devsysinfo = null;
int matchlen = 0;
try (DirectoryStream<Path> stream = Files.newDirectoryStream(sysinfo)) {
for (Path device : stream) {
String name = device.getFileName().toString();
if (name.length() > matchlen && devName.startsWith(name)) {
devsysinfo = device;
matchlen = name.length();
}
}
devinfo = sysinfo.resolve(devName);
}
if (devsysinfo == null) {
return true; // give up
}
// read first byte from rotational, it's a 1 if it spins.
Path info = devinfo.resolve("queue/rotational");
try (InputStream stream = Files.newInputStream(info)) {
Path rotational = devsysinfo.resolve("queue").resolve("rotational");
try (InputStream stream = Files.newInputStream(rotational)) {
return stream.read() == '1';
}
}

View File

@ -32,6 +32,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import org.apache.lucene.mockfile.FilterFileSystem;
import org.apache.lucene.mockfile.FilterFileSystemProvider;
@ -353,6 +355,34 @@ public class TestIOUtils extends LuceneTestCase {
assertFalse(IOUtils.spinsLinux(mockPath));
}
public void testNVME() throws Exception {
assumeFalse("windows is not supported", Constants.WINDOWS);
Path dir = createTempDir();
dir = FilterPath.unwrap(dir).toRealPath();
// fake ssd
FileStore root = new MockFileStore(dir.toString() + " (/dev/nvme0n1p1)", "btrfs", "/dev/nvme0n1p1");
// make a fake /dev/nvme0n1p1 for it
Path devdir = dir.resolve("dev");
Files.createDirectories(devdir);
Files.createFile(devdir.resolve("nvme0n1p1"));
// make a fake /sys/block/nvme0n1/queue/rotational file for it
Path sysdir = dir.resolve("sys").resolve("block").resolve("nvme0n1").resolve("queue");
Files.createDirectories(sysdir);
try (OutputStream o = Files.newOutputStream(sysdir.resolve("rotational"))) {
o.write("0\n".getBytes(StandardCharsets.US_ASCII));
}
// As test for the longest path match, add some other devices (that have no queue/rotational), too:
Files.createFile(dir.resolve("sys").resolve("block").resolve("nvme0"));
Files.createFile(dir.resolve("sys").resolve("block").resolve("dummy"));
Files.createFile(dir.resolve("sys").resolve("block").resolve("nvm"));
Map<String,FileStore> mappings = Collections.singletonMap(dir.toString(), root);
FileSystem mockLinux = new MockLinuxFileSystemProvider(dir.getFileSystem(), mappings, dir).getFileSystem(null);
Path mockPath = mockLinux.getPath(dir.toString());
assertFalse(IOUtils.spinsLinux(mockPath));
}
public void testRotatingPlatters() throws Exception {
assumeFalse("windows is not supported", Constants.WINDOWS);
Path dir = createTempDir();
@ -401,4 +431,39 @@ public class TestIOUtils extends LuceneTestCase {
assertFalse(IOUtils.spinsLinux(mockPath));
}
public void testSymlinkSSD() throws Exception {
assumeFalse("windows is not supported", Constants.WINDOWS);
Path dir = createTempDir();
dir = FilterPath.unwrap(dir).toRealPath();
// fake SSD with a symlink mount (Ubuntu-like):
Random rnd = random();
String partitionUUID = new UUID(rnd.nextLong(), rnd.nextLong()).toString();
FileStore root = new MockFileStore(dir.toString() + " (/dev/disk/by-uuid/"+partitionUUID+")", "btrfs", "/dev/disk/by-uuid/"+partitionUUID);
// make a fake /dev/sda1 for it
Path devdir = dir.resolve("dev");
Files.createDirectories(devdir);
Path deviceFile = devdir.resolve("sda1");
Files.createFile(deviceFile);
// create a symlink to the above device file
Path symlinkdir = devdir.resolve("disk").resolve("by-uuid");
Files.createDirectories(symlinkdir);
try {
Files.createSymbolicLink(symlinkdir.resolve(partitionUUID), deviceFile);
} catch (UnsupportedOperationException | IOException e) {
assumeNoException("test requires filesystem that supports symbolic links", e);
}
// make a fake /sys/block/sda/queue/rotational file for it
Path sysdir = dir.resolve("sys").resolve("block").resolve("sda").resolve("queue");
Files.createDirectories(sysdir);
try (OutputStream o = Files.newOutputStream(sysdir.resolve("rotational"))) {
o.write("0\n".getBytes(StandardCharsets.US_ASCII));
}
Map<String,FileStore> mappings = Collections.singletonMap(dir.toString(), root);
FileSystem mockLinux = new MockLinuxFileSystemProvider(dir.getFileSystem(), mappings, dir).getFileSystem(null);
Path mockPath = mockLinux.getPath(dir.toString());
assertFalse(IOUtils.spinsLinux(mockPath));
}
}

View File

@ -38,6 +38,9 @@ grant {
permission java.io.FilePermission "${junit4.childvm.cwd}${/}jacoco.db", "write";
permission java.io.FilePermission "${junit4.tempDir}${/}*", "read,write,delete";
permission java.io.FilePermission "${clover.db.dir}${/}-", "read,write,delete";
// needed by SSD detection tests in TestIOUtils (creates symlinks)
permission java.nio.file.LinkPermission "symbolic";
// needed by gson serialization of junit4 runner: TODO clean that up
permission java.lang.RuntimePermission "accessDeclaredMembers";