/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.node; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.set.Sets; import org.opensearch.core.xcontent.ToXContentFragment; import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * Class representing statistics about adaptive replica selection. This includes * EWMA of queue size, service time, and response time, as well as outgoing * searches to each node and the "rank" based on the ARS formula. * * @opensearch.internal */ public class AdaptiveSelectionStats implements Writeable, ToXContentFragment { private final Map clientOutgoingConnections; private final Map nodeComputedStats; public AdaptiveSelectionStats( Map clientConnections, Map nodeComputedStats ) { this.clientOutgoingConnections = clientConnections; this.nodeComputedStats = nodeComputedStats; } public AdaptiveSelectionStats(StreamInput in) throws IOException { this.clientOutgoingConnections = in.readMap(StreamInput::readString, StreamInput::readLong); this.nodeComputedStats = in.readMap(StreamInput::readString, ResponseCollectorService.ComputedNodeStats::new); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeMap(this.clientOutgoingConnections, StreamOutput::writeString, StreamOutput::writeLong); out.writeMap(this.nodeComputedStats, StreamOutput::writeString, (stream, stats) -> stats.writeTo(stream)); } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject("adaptive_selection"); Set allNodeIds = Sets.union(clientOutgoingConnections.keySet(), nodeComputedStats.keySet()); for (String nodeId : allNodeIds) { builder.startObject(nodeId); ResponseCollectorService.ComputedNodeStats stats = nodeComputedStats.get(nodeId); if (stats != null) { long outgoingSearches = clientOutgoingConnections.getOrDefault(nodeId, 0L); builder.field("outgoing_searches", outgoingSearches); builder.field("avg_queue_size", stats.queueSize); if (builder.humanReadable()) { builder.field("avg_service_time", new TimeValue((long) stats.serviceTime, TimeUnit.NANOSECONDS).toString()); } builder.field("avg_service_time_ns", (long) stats.serviceTime); if (builder.humanReadable()) { builder.field("avg_response_time", new TimeValue((long) stats.responseTime, TimeUnit.NANOSECONDS).toString()); } builder.field("avg_response_time_ns", (long) stats.responseTime); builder.field("rank", String.format(Locale.ROOT, "%.1f", stats.rank(outgoingSearches))); } builder.endObject(); } builder.endObject(); return builder; } /** * Returns a map of node id to the outgoing search requests to that node */ public Map getOutgoingConnections() { return clientOutgoingConnections; } /** * Returns a map of node id to the computed stats */ public Map getComputedStats() { return nodeComputedStats; } /** * Returns a map of node id to the ranking of the nodes based on the adaptive replica formula */ public Map getRanks() { return nodeComputedStats.entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().rank(clientOutgoingConnections.getOrDefault(e.getKey(), 0L)))); } }