All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.xpack.core.DataTiersFeatureSet Maven / Gradle / Ivy

/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0; you may not use this file except in compliance with the Elastic License
 * 2.0.
 */

package org.elasticsearch.xpack.core;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.CommonStatsFlags;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
import org.elasticsearch.cluster.routing.RoutingNode;
import org.elasticsearch.cluster.routing.RoutingNodes;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.ShardRoutingState;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.store.StoreStats;
import org.elasticsearch.search.aggregations.metrics.TDigestState;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

public class DataTiersFeatureSet implements XPackFeatureSet {

    private final Client client;
    private final ClusterService clusterService;

    @Inject
    public DataTiersFeatureSet(Client client, ClusterService clusterService) {
        this.client = client;
        this.clusterService = clusterService;
    }

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

    @Override
    public boolean available() {
        return true;
    }

    @Override
    public boolean enabled() {
        return true;
    }

    @Override
    public Map nativeCodeInfo() {
        return null;
    }

    @Override
    public void usage(ActionListener listener) {
        final ClusterState state = clusterService.state();
        client.admin().cluster().prepareNodesStats()
            .all()
            .setIndices(CommonStatsFlags.ALL)
            .execute(ActionListener.wrap(nodesStatsResponse -> {
                final RoutingNodes routingNodes = state.getRoutingNodes();

                // First separate the nodes into separate tiers, note that nodes *may* be duplicated
                Map> tierSpecificNodeStats = separateTiers(nodesStatsResponse);

                // Generate tier specific stats for the nodes
                Map tierSpecificStats = tierSpecificNodeStats.entrySet()
                    .stream().collect(Collectors.toMap(Map.Entry::getKey, ns -> calculateStats(ns.getValue(), routingNodes)));

                listener.onResponse(new DataTiersFeatureSetUsage(tierSpecificStats));
            }, listener::onFailure));
    }

    // Visible for testing
    static Map> separateTiers(NodesStatsResponse nodesStatsResponse) {
        Map> responses = new HashMap<>();
        DataTier.ALL_DATA_TIERS.forEach(tier ->
            responses.put(tier, nodesStatsResponse.getNodes().stream()
                .filter(stats -> stats.getNode().getRoles().stream()
                    .map(DiscoveryNodeRole::roleName)
                    .anyMatch(rn -> rn.equals(tier)))
                .collect(Collectors.toList())));
        return responses;
    }

    private DataTiersFeatureSetUsage.TierSpecificStats calculateStats(List nodesStats, RoutingNodes routingNodes) {
        int nodeCount = 0;
        int indexCount = 0;
        int totalShardCount = 0;
        long totalByteCount = 0;
        long docCount = 0;
        final AtomicInteger primaryShardCount = new AtomicInteger(0);
        final AtomicLong primaryByteCount = new AtomicLong(0);
        final TDigestState valueSketch = new TDigestState(1000);
        for (NodeStats nodeStats : nodesStats) {
            nodeCount++;
            totalByteCount += nodeStats.getIndices().getStore().getSizeInBytes();
            docCount += nodeStats.getIndices().getDocs().getCount();
            String nodeId = nodeStats.getNode().getId();
            final RoutingNode node = routingNodes.node(nodeId);
            if (node != null) {
                totalShardCount += node.shardsWithState(ShardRoutingState.STARTED).size();
                Set indicesOnNode = node.shardsWithState(ShardRoutingState.STARTED).stream()
                    .map(ShardRouting::index)
                    .collect(Collectors.toSet());
                indexCount += indicesOnNode.size();
                indicesOnNode.forEach(index -> {
                    nodeStats.getIndices().getShardStats(index).stream()
                        .filter(shardStats -> shardStats.getPrimary().getStore() != null)
                        .forEach(shardStats -> {
                            StoreStats primaryStoreStats = shardStats.getPrimary().getStore();
                            // If storeStats is null, it means this is not a replica
                            primaryShardCount.incrementAndGet();
                            long primarySize = primaryStoreStats.getSizeInBytes();
                            primaryByteCount.addAndGet(primarySize);
                            valueSketch.add(primarySize);
                        });
                });
            }
        }
        long primaryShardSizeMedian = (long) valueSketch.quantile(0.5);
        long primaryShardSizeMAD = computeMedianAbsoluteDeviation(valueSketch);
        return new DataTiersFeatureSetUsage.TierSpecificStats(nodeCount, indexCount, totalShardCount, primaryShardCount.get(), docCount,
            totalByteCount, primaryByteCount.get(), primaryShardSizeMedian, primaryShardSizeMAD);
    }

    // Visible for testing
    static long computeMedianAbsoluteDeviation(TDigestState valuesSketch) {
        if (valuesSketch.size() == 0) {
            return 0;
        } else {
            final double approximateMedian = valuesSketch.quantile(0.5);
            final TDigestState approximatedDeviationsSketch = new TDigestState(valuesSketch.compression());
            valuesSketch.centroids().forEach(centroid -> {
                final double deviation = Math.abs(approximateMedian - centroid.mean());
                approximatedDeviationsSketch.add(deviation, centroid.count());
            });

            return (long) approximatedDeviationsSketch.quantile(0.5);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy