|  | @@ -0,0 +1,281 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * 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.searchablesnapshots.action.cache;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.ActionType;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.FailedNodeException;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.support.ActionFilters;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.support.nodes.BaseNodeResponse;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.support.nodes.BaseNodesRequest;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.support.nodes.BaseNodesResponse;
 | 
	
		
			
				|  |  | +import org.elasticsearch.action.support.nodes.TransportNodesAction;
 | 
	
		
			
				|  |  | +import org.elasticsearch.cluster.ClusterName;
 | 
	
		
			
				|  |  | +import org.elasticsearch.cluster.ClusterState;
 | 
	
		
			
				|  |  | +import org.elasticsearch.cluster.node.DiscoveryNode;
 | 
	
		
			
				|  |  | +import org.elasticsearch.cluster.service.ClusterService;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.collect.ImmutableOpenMap;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.inject.Inject;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.io.stream.StreamInput;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.io.stream.StreamOutput;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.unit.ByteSizeValue;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.xcontent.ToXContentFragment;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.xcontent.ToXContentObject;
 | 
	
		
			
				|  |  | +import org.elasticsearch.common.xcontent.XContentBuilder;
 | 
	
		
			
				|  |  | +import org.elasticsearch.tasks.Task;
 | 
	
		
			
				|  |  | +import org.elasticsearch.threadpool.ThreadPool;
 | 
	
		
			
				|  |  | +import org.elasticsearch.transport.TransportRequest;
 | 
	
		
			
				|  |  | +import org.elasticsearch.transport.TransportService;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshots;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xpack.searchablesnapshots.cache.shared.FrozenCacheService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.io.IOException;
 | 
	
		
			
				|  |  | +import java.util.Arrays;
 | 
	
		
			
				|  |  | +import java.util.List;
 | 
	
		
			
				|  |  | +import java.util.function.Supplier;
 | 
	
		
			
				|  |  | +import java.util.stream.Collectors;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Node level stats about searchable snapshots caches.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +public class TransportSearchableSnapshotsNodeCachesStatsAction extends TransportNodesAction<
 | 
	
		
			
				|  |  | +    TransportSearchableSnapshotsNodeCachesStatsAction.NodesRequest,
 | 
	
		
			
				|  |  | +    TransportSearchableSnapshotsNodeCachesStatsAction.NodesCachesStatsResponse,
 | 
	
		
			
				|  |  | +    TransportSearchableSnapshotsNodeCachesStatsAction.NodeRequest,
 | 
	
		
			
				|  |  | +    TransportSearchableSnapshotsNodeCachesStatsAction.NodeCachesStatsResponse> {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static final String ACTION_NAME = "cluster:admin/xpack/searchable_snapshots/cache/stats";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static final ActionType<NodesCachesStatsResponse> TYPE = new ActionType<>(ACTION_NAME, NodesCachesStatsResponse::new);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private final Supplier<FrozenCacheService> frozenCacheService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Inject
 | 
	
		
			
				|  |  | +    public TransportSearchableSnapshotsNodeCachesStatsAction(
 | 
	
		
			
				|  |  | +        ThreadPool threadPool,
 | 
	
		
			
				|  |  | +        ClusterService clusterService,
 | 
	
		
			
				|  |  | +        TransportService transportService,
 | 
	
		
			
				|  |  | +        ActionFilters actionFilters,
 | 
	
		
			
				|  |  | +        SearchableSnapshots.FrozenCacheServiceSupplier frozenCacheService
 | 
	
		
			
				|  |  | +    ) {
 | 
	
		
			
				|  |  | +        super(
 | 
	
		
			
				|  |  | +            ACTION_NAME,
 | 
	
		
			
				|  |  | +            threadPool,
 | 
	
		
			
				|  |  | +            clusterService,
 | 
	
		
			
				|  |  | +            transportService,
 | 
	
		
			
				|  |  | +            actionFilters,
 | 
	
		
			
				|  |  | +            NodesRequest::new,
 | 
	
		
			
				|  |  | +            NodeRequest::new,
 | 
	
		
			
				|  |  | +            ThreadPool.Names.MANAGEMENT,
 | 
	
		
			
				|  |  | +            ThreadPool.Names.SAME,
 | 
	
		
			
				|  |  | +            NodeCachesStatsResponse.class
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        this.frozenCacheService = frozenCacheService;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected NodesCachesStatsResponse newResponse(
 | 
	
		
			
				|  |  | +        NodesRequest request,
 | 
	
		
			
				|  |  | +        List<NodeCachesStatsResponse> responses,
 | 
	
		
			
				|  |  | +        List<FailedNodeException> failures
 | 
	
		
			
				|  |  | +    ) {
 | 
	
		
			
				|  |  | +        return new NodesCachesStatsResponse(clusterService.getClusterName(), responses, failures);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected NodeRequest newNodeRequest(NodesRequest request) {
 | 
	
		
			
				|  |  | +        return new NodeRequest();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected NodeCachesStatsResponse newNodeResponse(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +        return new NodeCachesStatsResponse(in);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected void resolveRequest(NodesRequest request, ClusterState clusterState) {
 | 
	
		
			
				|  |  | +        final ImmutableOpenMap<String, DiscoveryNode> dataNodes = clusterState.getNodes().getDataNodes();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        final DiscoveryNode[] resolvedNodes;
 | 
	
		
			
				|  |  | +        if (request.nodesIds() == null || request.nodesIds().length == 0) {
 | 
	
		
			
				|  |  | +            resolvedNodes = dataNodes.values().toArray(DiscoveryNode.class);
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            resolvedNodes = Arrays.stream(request.nodesIds())
 | 
	
		
			
				|  |  | +                .filter(dataNodes::containsKey)
 | 
	
		
			
				|  |  | +                .map(dataNodes::get)
 | 
	
		
			
				|  |  | +                .collect(Collectors.toList())
 | 
	
		
			
				|  |  | +                .toArray(DiscoveryNode[]::new);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        request.setConcreteNodes(resolvedNodes);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    protected NodeCachesStatsResponse nodeOperation(NodeRequest request, Task task) {
 | 
	
		
			
				|  |  | +        final FrozenCacheService.Stats frozenCacheStats;
 | 
	
		
			
				|  |  | +        if (frozenCacheService.get() != null) {
 | 
	
		
			
				|  |  | +            frozenCacheStats = frozenCacheService.get().getStats();
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            frozenCacheStats = FrozenCacheService.Stats.EMPTY;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return new NodeCachesStatsResponse(
 | 
	
		
			
				|  |  | +            clusterService.localNode(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getNumberOfRegions(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getSize(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getRegionSize(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getWriteCount(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getWriteBytes(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getReadCount(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getReadBytes(),
 | 
	
		
			
				|  |  | +            frozenCacheStats.getEvictCount()
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static final class NodeRequest extends TransportRequest {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodeRequest() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodeRequest(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +            super(in);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        public void writeTo(StreamOutput out) throws IOException {
 | 
	
		
			
				|  |  | +            super.writeTo(out);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static final class NodesRequest extends BaseNodesRequest<NodesRequest> {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodesRequest(String[] nodes) {
 | 
	
		
			
				|  |  | +            super(nodes);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodesRequest(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +            super(in);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        public void writeTo(StreamOutput out) throws IOException {
 | 
	
		
			
				|  |  | +            super.writeTo(out);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static class NodeCachesStatsResponse extends BaseNodeResponse implements ToXContentFragment {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private final int numRegions;
 | 
	
		
			
				|  |  | +        private final long size;
 | 
	
		
			
				|  |  | +        private final long regionSize;
 | 
	
		
			
				|  |  | +        private final long writes;
 | 
	
		
			
				|  |  | +        private final long bytesWritten;
 | 
	
		
			
				|  |  | +        private final long reads;
 | 
	
		
			
				|  |  | +        private final long bytesRead;
 | 
	
		
			
				|  |  | +        private final long evictions;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodeCachesStatsResponse(
 | 
	
		
			
				|  |  | +            DiscoveryNode node,
 | 
	
		
			
				|  |  | +            int numRegions,
 | 
	
		
			
				|  |  | +            long size,
 | 
	
		
			
				|  |  | +            long regionSize,
 | 
	
		
			
				|  |  | +            long writes,
 | 
	
		
			
				|  |  | +            long bytesWritten,
 | 
	
		
			
				|  |  | +            long reads,
 | 
	
		
			
				|  |  | +            long bytesRead,
 | 
	
		
			
				|  |  | +            long evictions
 | 
	
		
			
				|  |  | +        ) {
 | 
	
		
			
				|  |  | +            super(node);
 | 
	
		
			
				|  |  | +            this.numRegions = numRegions;
 | 
	
		
			
				|  |  | +            this.size = size;
 | 
	
		
			
				|  |  | +            this.regionSize = regionSize;
 | 
	
		
			
				|  |  | +            this.writes = writes;
 | 
	
		
			
				|  |  | +            this.bytesWritten = bytesWritten;
 | 
	
		
			
				|  |  | +            this.reads = reads;
 | 
	
		
			
				|  |  | +            this.bytesRead = bytesRead;
 | 
	
		
			
				|  |  | +            this.evictions = evictions;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodeCachesStatsResponse(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +            super(in);
 | 
	
		
			
				|  |  | +            this.numRegions = in.readVInt();
 | 
	
		
			
				|  |  | +            this.size = in.readVLong();
 | 
	
		
			
				|  |  | +            this.regionSize = in.readVLong();
 | 
	
		
			
				|  |  | +            this.writes = in.readVLong();
 | 
	
		
			
				|  |  | +            this.bytesWritten = in.readVLong();
 | 
	
		
			
				|  |  | +            this.reads = in.readVLong();
 | 
	
		
			
				|  |  | +            this.bytesRead = in.readVLong();
 | 
	
		
			
				|  |  | +            this.evictions = in.readVLong();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        public void writeTo(StreamOutput out) throws IOException {
 | 
	
		
			
				|  |  | +            super.writeTo(out);
 | 
	
		
			
				|  |  | +            out.writeVInt(numRegions);
 | 
	
		
			
				|  |  | +            out.writeVLong(size);
 | 
	
		
			
				|  |  | +            out.writeVLong(regionSize);
 | 
	
		
			
				|  |  | +            out.writeVLong(writes);
 | 
	
		
			
				|  |  | +            out.writeVLong(bytesWritten);
 | 
	
		
			
				|  |  | +            out.writeVLong(reads);
 | 
	
		
			
				|  |  | +            out.writeVLong(bytesRead);
 | 
	
		
			
				|  |  | +            out.writeVLong(evictions);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
 | 
	
		
			
				|  |  | +            builder.startObject(getNode().getId());
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                builder.startObject("shared_cache");
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    builder.field("reads", reads);
 | 
	
		
			
				|  |  | +                    builder.humanReadableField("bytes_read_in_bytes", "bytes_read", ByteSizeValue.ofBytes(bytesRead));
 | 
	
		
			
				|  |  | +                    builder.field("writes", writes);
 | 
	
		
			
				|  |  | +                    builder.humanReadableField("bytes_written_in_bytes", "bytes_written", ByteSizeValue.ofBytes(bytesWritten));
 | 
	
		
			
				|  |  | +                    builder.field("evictions", evictions);
 | 
	
		
			
				|  |  | +                    builder.field("num_regions", numRegions);
 | 
	
		
			
				|  |  | +                    builder.humanReadableField("size_in_bytes", "size", ByteSizeValue.ofBytes(size));
 | 
	
		
			
				|  |  | +                    builder.humanReadableField("region_size_in_bytes", "region_size", ByteSizeValue.ofBytes(regionSize));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                builder.endObject();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            builder.endObject();
 | 
	
		
			
				|  |  | +            return builder;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static class NodesCachesStatsResponse extends BaseNodesResponse<NodeCachesStatsResponse> implements ToXContentObject {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodesCachesStatsResponse(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +            super(in);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public NodesCachesStatsResponse(ClusterName clusterName, List<NodeCachesStatsResponse> nodes, List<FailedNodeException> failures) {
 | 
	
		
			
				|  |  | +            super(clusterName, nodes, failures);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        protected List<NodeCachesStatsResponse> readNodesFrom(StreamInput in) throws IOException {
 | 
	
		
			
				|  |  | +            return in.readList(NodeCachesStatsResponse::new);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        protected void writeNodesTo(StreamOutput out, List<NodeCachesStatsResponse> nodes) throws IOException {
 | 
	
		
			
				|  |  | +            out.writeList(nodes);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        @Override
 | 
	
		
			
				|  |  | +        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
 | 
	
		
			
				|  |  | +            builder.startObject();
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                builder.startObject("nodes");
 | 
	
		
			
				|  |  | +                for (NodeCachesStatsResponse node : getNodes()) {
 | 
	
		
			
				|  |  | +                    node.toXContent(builder, params);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                builder.endObject();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            builder.endObject();
 | 
	
		
			
				|  |  | +            return builder;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |