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:
parent
c5b4bba30b
commit
4eb32e9d86
|
@ -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() {
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue