Expose disk usage estimates in nodes stats

This exposes the least and most used disk usage estimates within the "fs" nodes
stats output:

```json
GET /_nodes/stats/fs?pretty&human
{
  "nodes" : {
    "34fPVU0uQ_-wWitDzDXX_g" : {
      "fs" : {
        "timestamp" : 1481238723550,
        "total" : {
          "total" : "396.1gb",
          "total_in_bytes" : 425343254528,
          "free" : "140.6gb",
          "free_in_bytes" : 151068725248,
          "available" : "120.5gb",
          "available_in_bytes" : 129438912512
        },
        "least_usage_estimate" : {
          "path" : "/home/hinmanm/es/elasticsearch/distribution/build/cluster/run node0/elasticsearch-6.0.0-alpha1-SNAPSHOT/data/nodes/0",
          "total" : "396.1gb",
          "total_in_bytes" : 425343254528,
          "available" : "120.5gb",
          "available_in_bytes" : 129438633984,
          "used_disk_percent" : 69.56842912023208
        },
        "most_usage_estimate" : {
          "path" : "/home/hinmanm/es/elasticsearch/distribution/build/cluster/run node0/elasticsearch-6.0.0-alpha1-SNAPSHOT/data/nodes/0",
          "total" : "396.1gb",
          "total_in_bytes" : 425343254528,
          "available" : "120.5gb",
          "available_in_bytes" : 129438633984,
          "used_disk_percent" : 69.56842912023208
        },
        "data" : [{...}],
        "io_stats" : {...}
      }
    }
  }
}
```

Resolves #8686
Resolves #22081
This commit is contained in:
Lee Hinman 2017-01-19 13:26:33 -07:00
parent c5b4bba30b
commit 4eb32e9d86
7 changed files with 78 additions and 12 deletions

View File

@ -28,6 +28,7 @@ import org.elasticsearch.monitor.jvm.JvmService;
import org.elasticsearch.monitor.os.OsService; import org.elasticsearch.monitor.os.OsService;
import org.elasticsearch.monitor.process.ProcessService; import org.elasticsearch.monitor.process.ProcessService;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.cluster.ClusterInfoService;
import java.io.IOException; import java.io.IOException;
@ -39,13 +40,14 @@ public class MonitorService extends AbstractLifecycleComponent {
private final JvmService jvmService; private final JvmService jvmService;
private final FsService fsService; private final FsService fsService;
public MonitorService(Settings settings, NodeEnvironment nodeEnvironment, ThreadPool threadPool) throws IOException { public MonitorService(Settings settings, NodeEnvironment nodeEnvironment, ThreadPool threadPool,
ClusterInfoService clusterInfoService) throws IOException {
super(settings); super(settings);
this.jvmGcMonitorService = new JvmGcMonitorService(settings, threadPool); this.jvmGcMonitorService = new JvmGcMonitorService(settings, threadPool);
this.osService = new OsService(settings); this.osService = new OsService(settings);
this.processService = new ProcessService(settings); this.processService = new ProcessService(settings);
this.jvmService = new JvmService(settings); this.jvmService = new JvmService(settings);
this.fsService = new FsService(settings, nodeEnvironment); this.fsService = new FsService(settings, nodeEnvironment, clusterInfoService);
} }
public OsService osService() { public OsService osService() {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.monitor.fs; package org.elasticsearch.monitor.fs;
import org.elasticsearch.cluster.DiskUsage;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
@ -438,12 +439,20 @@ public class FsInfo implements Iterable<FsInfo.Path>, Writeable, ToXContent {
private final Path[] paths; private final Path[] paths;
private final IoStats ioStats; private final IoStats ioStats;
private final Path total; private final Path total;
private final DiskUsage leastDiskEstimate;
private final DiskUsage mostDiskEstimate;
public FsInfo(long timestamp, IoStats ioStats, Path[] paths) { public FsInfo(long timestamp, IoStats ioStats, Path[] paths) {
this(timestamp, ioStats, paths, null, null);
}
public FsInfo(long timestamp, IoStats ioStats, Path[] paths, @Nullable DiskUsage leastUsage, @Nullable DiskUsage mostUsage) {
this.timestamp = timestamp; this.timestamp = timestamp;
this.ioStats = ioStats; this.ioStats = ioStats;
this.paths = paths; this.paths = paths;
this.total = total(); this.total = total();
this.leastDiskEstimate = leastUsage;
this.mostDiskEstimate = mostUsage;
} }
/** /**
@ -457,6 +466,8 @@ public class FsInfo implements Iterable<FsInfo.Path>, Writeable, ToXContent {
paths[i] = new Path(in); paths[i] = new Path(in);
} }
this.total = total(); this.total = total();
this.leastDiskEstimate = in.readOptionalWriteable(DiskUsage::new);
this.mostDiskEstimate = in.readOptionalWriteable(DiskUsage::new);
} }
@Override @Override
@ -467,12 +478,24 @@ public class FsInfo implements Iterable<FsInfo.Path>, Writeable, ToXContent {
for (Path path : paths) { for (Path path : paths) {
path.writeTo(out); path.writeTo(out);
} }
out.writeOptionalWriteable(this.leastDiskEstimate);
out.writeOptionalWriteable(this.mostDiskEstimate);
} }
public Path getTotal() { public Path getTotal() {
return total; return total;
} }
@Nullable
public DiskUsage getLeastDiskEstimate() {
return this.leastDiskEstimate;
}
@Nullable
public DiskUsage getMostDiskEstimate() {
return this.mostDiskEstimate;
}
private Path total() { private Path total() {
Path res = new Path(); Path res = new Path();
Set<String> seenDevices = new HashSet<>(paths.length); Set<String> seenDevices = new HashSet<>(paths.length);
@ -506,6 +529,27 @@ public class FsInfo implements Iterable<FsInfo.Path>, Writeable, ToXContent {
builder.field(Fields.TIMESTAMP, timestamp); builder.field(Fields.TIMESTAMP, timestamp);
builder.field(Fields.TOTAL); builder.field(Fields.TOTAL);
total().toXContent(builder, params); total().toXContent(builder, params);
if (leastDiskEstimate != null) {
builder.startObject(Fields.LEAST_ESTIMATE);
{
builder.field(Fields.PATH, leastDiskEstimate.getPath());
builder.byteSizeField(Fields.TOTAL_IN_BYTES, Fields.TOTAL, leastDiskEstimate.getTotalBytes());
builder.byteSizeField(Fields.AVAILABLE_IN_BYTES, Fields.AVAILABLE, leastDiskEstimate.getFreeBytes());
builder.field(Fields.USAGE_PERCENTAGE, leastDiskEstimate.getUsedDiskAsPercentage());
}
builder.endObject();
}
if (mostDiskEstimate != null) {
builder.startObject(Fields.MOST_ESTIMATE);
{
builder.field(Fields.PATH, mostDiskEstimate.getPath());
builder.byteSizeField(Fields.TOTAL_IN_BYTES, Fields.TOTAL, mostDiskEstimate.getTotalBytes());
builder.byteSizeField(Fields.AVAILABLE_IN_BYTES, Fields.AVAILABLE, mostDiskEstimate.getFreeBytes());
builder.field(Fields.USAGE_PERCENTAGE, mostDiskEstimate.getUsedDiskAsPercentage());
}
builder.endObject();
}
builder.startArray(Fields.DATA); builder.startArray(Fields.DATA);
for (Path path : paths) { for (Path path : paths) {
path.toXContent(builder, params); path.toXContent(builder, params);
@ -525,6 +569,13 @@ public class FsInfo implements Iterable<FsInfo.Path>, Writeable, ToXContent {
static final String TIMESTAMP = "timestamp"; static final String TIMESTAMP = "timestamp";
static final String DATA = "data"; static final String DATA = "data";
static final String TOTAL = "total"; static final String TOTAL = "total";
static final String TOTAL_IN_BYTES = "total_in_bytes";
static final String IO_STATS = "io_stats"; static final String IO_STATS = "io_stats";
static final String LEAST_ESTIMATE = "least_usage_estimate";
static final String MOST_ESTIMATE = "most_usage_estimate";
static final String USAGE_PERCENTAGE = "used_disk_percent";
static final String AVAILABLE = "available";
static final String AVAILABLE_IN_BYTES = "available_in_bytes";
static final String PATH = "path";
} }
} }

View File

@ -22,6 +22,9 @@ package org.elasticsearch.monitor.fs;
import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier; import org.apache.logging.log4j.util.Supplier;
import org.apache.lucene.util.Constants; import org.apache.lucene.util.Constants;
import org.elasticsearch.cluster.ClusterInfo;
import org.elasticsearch.cluster.DiskUsage;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
@ -48,7 +51,7 @@ public class FsProbe extends AbstractComponent {
this.nodeEnv = nodeEnv; this.nodeEnv = nodeEnv;
} }
public FsInfo stats(FsInfo previous) throws IOException { public FsInfo stats(FsInfo previous, @Nullable ClusterInfo clusterInfo) throws IOException {
if (!nodeEnv.hasNodeFile()) { if (!nodeEnv.hasNodeFile()) {
return new FsInfo(System.currentTimeMillis(), null, new FsInfo.Path[0]); return new FsInfo(System.currentTimeMillis(), null, new FsInfo.Path[0]);
} }
@ -67,7 +70,13 @@ public class FsProbe extends AbstractComponent {
} }
ioStats = ioStats(devicesNumbers, previous); ioStats = ioStats(devicesNumbers, previous);
} }
return new FsInfo(System.currentTimeMillis(), ioStats, paths); DiskUsage leastDiskEstimate = null;
DiskUsage mostDiskEstimate = null;
if (clusterInfo != null) {
leastDiskEstimate = clusterInfo.getNodeLeastAvailableDiskUsages().get(nodeEnv.nodeId());
mostDiskEstimate = clusterInfo.getNodeMostAvailableDiskUsages().get(nodeEnv.nodeId());
}
return new FsInfo(System.currentTimeMillis(), ioStats, paths, leastDiskEstimate, mostDiskEstimate);
} }
final FsInfo.IoStats ioStats(final Set<Tuple<Integer, Integer>> devicesNumbers, final FsInfo previous) { final FsInfo.IoStats ioStats(final Set<Tuple<Integer, Integer>> devicesNumbers, final FsInfo previous) {

View File

@ -20,6 +20,8 @@
package org.elasticsearch.monitor.fs; package org.elasticsearch.monitor.fs;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.ClusterInfo;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Setting.Property;
@ -27,6 +29,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.SingleObjectCache; import org.elasticsearch.common.util.SingleObjectCache;
import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.cluster.ClusterInfoService;
import java.io.IOException; import java.io.IOException;
@ -35,6 +38,7 @@ public class FsService extends AbstractComponent {
private final FsProbe probe; private final FsProbe probe;
private final TimeValue refreshInterval; private final TimeValue refreshInterval;
private final SingleObjectCache<FsInfo> cache; private final SingleObjectCache<FsInfo> cache;
private final ClusterInfoService clusterInfoService;
public static final Setting<TimeValue> REFRESH_INTERVAL_SETTING = public static final Setting<TimeValue> REFRESH_INTERVAL_SETTING =
Setting.timeSetting( Setting.timeSetting(
@ -43,21 +47,22 @@ public class FsService extends AbstractComponent {
TimeValue.timeValueSeconds(1), TimeValue.timeValueSeconds(1),
Property.NodeScope); Property.NodeScope);
public FsService(final Settings settings, final NodeEnvironment nodeEnvironment) { public FsService(final Settings settings, final NodeEnvironment nodeEnvironment, ClusterInfoService clusterInfoService) {
super(settings); super(settings);
this.probe = new FsProbe(settings, nodeEnvironment); this.probe = new FsProbe(settings, nodeEnvironment);
this.clusterInfoService = clusterInfoService;
refreshInterval = REFRESH_INTERVAL_SETTING.get(settings); refreshInterval = REFRESH_INTERVAL_SETTING.get(settings);
logger.debug("using refresh_interval [{}]", refreshInterval); logger.debug("using refresh_interval [{}]", refreshInterval);
cache = new FsInfoCache(refreshInterval, stats(probe, null, logger)); cache = new FsInfoCache(refreshInterval, stats(probe, null, logger, null));
} }
public FsInfo stats() { public FsInfo stats() {
return cache.getOrRefresh(); return cache.getOrRefresh();
} }
private static FsInfo stats(FsProbe probe, FsInfo initialValue, Logger logger) { private static FsInfo stats(FsProbe probe, FsInfo initialValue, Logger logger, @Nullable ClusterInfo clusterInfo) {
try { try {
return probe.stats(initialValue); return probe.stats(initialValue, clusterInfo);
} catch (IOException e) { } catch (IOException e) {
logger.debug("unexpected exception reading filesystem info", e); logger.debug("unexpected exception reading filesystem info", e);
return null; return null;
@ -75,7 +80,7 @@ public class FsService extends AbstractComponent {
@Override @Override
protected FsInfo refresh() { protected FsInfo refresh() {
return stats(probe, initialValue, logger); return stats(probe, initialValue, logger, clusterInfoService.getClusterInfo());
} }
} }

View File

@ -342,7 +342,7 @@ public class Node implements Closeable {
for (Module pluginModule : pluginsService.createGuiceModules()) { for (Module pluginModule : pluginsService.createGuiceModules()) {
modules.add(pluginModule); modules.add(pluginModule);
} }
final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool); final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService);
modules.add(new NodeModule(this, monitorService)); modules.add(new NodeModule(this, monitorService));
ClusterModule clusterModule = new ClusterModule(settings, clusterService, ClusterModule clusterModule = new ClusterModule(settings, clusterService,
pluginsService.filterPlugins(ClusterPlugin.class)); pluginsService.filterPlugins(ClusterPlugin.class));

View File

@ -172,7 +172,6 @@ public class ClusterInfoServiceIT extends ESIntegTestCase {
IndexShard indexShard = indexService.getShardOrNull(shard.id()); IndexShard indexShard = indexService.getShardOrNull(shard.id());
assertEquals(indexShard.shardPath().getRootDataPath().toString(), dataPath); assertEquals(indexShard.shardPath().getRootDataPath().toString(), dataPath);
} }
} }
public void testClusterInfoServiceInformationClearOnError() throws InterruptedException, ExecutionException { public void testClusterInfoServiceInformationClearOnError() throws InterruptedException, ExecutionException {

View File

@ -45,7 +45,7 @@ public class FsProbeTests extends ESTestCase {
try (NodeEnvironment env = newNodeEnvironment()) { try (NodeEnvironment env = newNodeEnvironment()) {
FsProbe probe = new FsProbe(Settings.EMPTY, env); FsProbe probe = new FsProbe(Settings.EMPTY, env);
FsInfo stats = probe.stats(null); FsInfo stats = probe.stats(null, null);
assertNotNull(stats); assertNotNull(stats);
assertThat(stats.getTimestamp(), greaterThan(0L)); assertThat(stats.getTimestamp(), greaterThan(0L));