Skip to content

Commit

Permalink
Merge pull request #10755 from rmuir/filestores
Browse files Browse the repository at this point in the history
refactor SSD/FileStore logic out of NodeEnvironment
  • Loading branch information
rmuir committed Apr 23, 2015
2 parents adc0807 + a66cf85 commit 0513815
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 77 deletions.
172 changes: 172 additions & 0 deletions src/main/java/org/elasticsearch/env/ESFileStore.java
@@ -0,0 +1,172 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.env;

import org.apache.lucene.util.Constants;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.common.io.PathUtils;

import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileStoreAttributeView;

/**
* Implementation of FileStore that supports
* additional features, such as SSD detection and better
* filesystem information for the root filesystem.
* @see Environment#getFileStore(Path)
*/
class ESFileStore extends FileStore {
/** Underlying filestore */
final FileStore in;
/** Cached result of Lucene's {@code IOUtils.spins} on path. */
final Boolean spins;

ESFileStore(FileStore in) {
this.in = in;
Boolean spins;
// Lucene's IOUtils.spins only works on Linux today:
if (Constants.LINUX) {
try {
spins = IOUtils.spins(PathUtils.get(getMountPointLinux(in)));
} catch (Exception e) {
spins = null;
}
} else {
spins = null;
}
this.spins = spins;
}

// these are hacks that are not guaranteed
private static String getMountPointLinux(FileStore store) {
String desc = store.toString();
int index = desc.lastIndexOf(" (");
if (index != -1) {
return desc.substring(0, index);
} else {
return desc;
}
}

/** Files.getFileStore(Path) useless here! Don't complain, just try it yourself. */
static FileStore getMatchingFileStore(Path path, FileStore fileStores[]) throws IOException {
FileStore store = Files.getFileStore(path);

if (Constants.WINDOWS) {
return store; // be defensive, don't even try to do anything fancy.
}

try {
String mount = getMountPointLinux(store);
FileStore sameMountPoint = null;
for (FileStore fs : fileStores) {
if (mount.equals(getMountPointLinux(fs))) {
if (sameMountPoint == null) {
sameMountPoint = fs;
} else {
// more than one filesystem has the same mount point; something is wrong!
// fall back to crappy one we got from Files.getFileStore
return store;
}
}
}

if (sameMountPoint != null) {
// ok, we found only one, use it:
return sameMountPoint;
} else {
// fall back to crappy one we got from Files.getFileStore
return store;
}
} catch (Exception e) {
// ignore
}

// fall back to crappy one we got from Files.getFileStore
return store;
}

@Override
public String name() {
return in.name();
}

@Override
public String type() {
return in.type();
}

@Override
public boolean isReadOnly() {
return in.isReadOnly();
}

@Override
public long getTotalSpace() throws IOException {
return in.getTotalSpace();
}

@Override
public long getUsableSpace() throws IOException {
return in.getUsableSpace();
}

@Override
public long getUnallocatedSpace() throws IOException {
return in.getUnallocatedSpace();
}

@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return in.supportsFileAttributeView(type);
}

@Override
public boolean supportsFileAttributeView(String name) {
if ("lucene".equals(name)) {
return true;
} else {
return in.supportsFileAttributeView(name);
}
}

@Override
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
return in.getFileStoreAttributeView(type);
}

@Override
public Object getAttribute(String attribute) throws IOException {
if ("lucene:spins".equals(attribute)) {
return spins;
} else {
return in.getAttribute(attribute);
}
}

@Override
public String toString() {
return in.toString();
}
}
42 changes: 32 additions & 10 deletions src/main/java/org/elasticsearch/env/Environment.java
Expand Up @@ -20,22 +20,16 @@
package org.elasticsearch.env;

import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;

import com.google.common.base.Charsets;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
import java.util.Collections;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;

import static org.elasticsearch.common.Strings.cleanPath;
import static org.elasticsearch.common.settings.ImmutableSettings.Builder.EMPTY_SETTINGS;
Expand Down Expand Up @@ -63,6 +57,9 @@ public class Environment {

private final Path logsFile;

/** List of filestores on the system */
private final FileStore[] fileStores;

public Environment() {
this(EMPTY_SETTINGS);
}
Expand Down Expand Up @@ -112,6 +109,13 @@ public Environment(Settings settings) {
} else {
logsFile = homeFile.resolve("logs");
}

// gather information about filesystems
ArrayList<FileStore> allStores = new ArrayList<>();
for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) {
allStores.add(new ESFileStore(store));
}
fileStores = allStores.toArray(new ESFileStore[allStores.size()]);
}

/**
Expand Down Expand Up @@ -177,6 +181,24 @@ public Path logsFile() {
return logsFile;
}

/**
* Looks up the filestore associated with a Path.
* <p>
* This is an enhanced version of {@link Files#getFileStore(Path)}:
* <ul>
* <li>On *nix systems, the store returned for the root filesystem will contain
* the actual filesystem type (e.g. {@code ext4}) instead of {@code rootfs}.
* <li>On some systems, the custom attribute {@code lucene:spins} is supported
* via the {@link FileStore#getAttribute(String)} method.
* <li>Only requires the security permissions of {@link Files#getFileStore(Path)},
* no permissions to the actual mount point are required.
* <li>Exception handling has the same semantics as {@link Files#getFileStore(Path)}.
* </ul>
*/
public FileStore getFileStore(Path path) throws IOException {
return ESFileStore.getMatchingFileStore(path, fileStores);
}

public URL resolveConfig(String path) throws FailedToResolveConfigException {
String origPath = path;
// first, try it as a path on the file system
Expand Down
73 changes: 6 additions & 67 deletions src/main/java/org/elasticsearch/env/NodeEnvironment.java
Expand Up @@ -22,7 +22,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.apache.lucene.store.*;
import org.apache.lucene.util.Constants;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ElasticsearchIllegalStateException;
Expand Down Expand Up @@ -64,23 +63,15 @@ public static class NodePath {
* not running on Linux, or we hit an exception trying), True means the device possibly spins and False means it does not. */
public final Boolean spins;

public NodePath(Path path) throws IOException {
public NodePath(Path path, Environment environment) throws IOException {
this.path = path;
this.indicesPath = path.resolve(INDICES_FOLDER);
this.fileStore = getFileStore(path);
Boolean spins;

// Lucene's IOUtils.spins only works on Linux today:
if (Constants.LINUX) {
try {
spins = IOUtils.spins(path);
} catch (Exception e) {
spins = null;
}
this.fileStore = environment.getFileStore(path);
if (fileStore.supportsFileAttributeView("lucene")) {
this.spins = (Boolean) fileStore.getAttribute("lucene:spins");
} else {
spins = null;
this.spins = null;
}
this.spins = spins;
}

/**
Expand Down Expand Up @@ -157,7 +148,7 @@ public NodeEnvironment(Settings settings, Environment environment) throws IOExce
Lock tmpLock = luceneDir.makeLock(NODE_LOCK_FILENAME);
boolean obtained = tmpLock.obtain();
if (obtained) {
nodePaths[dirIndex] = new NodePath(dir);
nodePaths[dirIndex] = new NodePath(dir, environment);
locks[dirIndex] = tmpLock;
localNodeId = possibleLockId;
} else {
Expand Down Expand Up @@ -289,58 +280,6 @@ private static String toString(Collection<String> items) {
return b.toString();
}


// TODO: move somewhere more "util"? But, this is somewhat hacky code ... not great to publicize it any more:

// NOTE: poached from Lucene's IOUtils:

/** Files.getFileStore(Path) useless here! Don't complain, just try it yourself. */
private static FileStore getFileStore(Path path) throws IOException {
FileStore store = Files.getFileStore(path);

try {
String mount = getMountPoint(store);
FileStore sameMountPoint = null;
for (FileStore fs : path.getFileSystem().getFileStores()) {
if (mount.equals(getMountPoint(fs))) {
if (sameMountPoint == null) {
sameMountPoint = fs;
} else {
// more than one filesystem has the same mount point; something is wrong!
// fall back to crappy one we got from Files.getFileStore
return store;
}
}
}

if (sameMountPoint != null) {
// ok, we found only one, use it:
return sameMountPoint;
} else {
// fall back to crappy one we got from Files.getFileStore
return store;
}
} catch (Exception e) {
// ignore
}

// fall back to crappy one we got from Files.getFileStore
return store;
}

// NOTE: poached from Lucene's IOUtils:

// these are hacks that are not guaranteed
private static String getMountPoint(FileStore store) {
String desc = store.toString();
int index = desc.lastIndexOf(" (");
if (index != -1) {
return desc.substring(0, index);
} else {
return desc;
}
}

/**
* Deletes a shard data directory iff the shards locks were successfully acquired.
*
Expand Down

0 comments on commit 0513815

Please sign in to comment.