/* * Licensed to ElasticSearch and Shay Banon 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. */ package org.elasticsearch.index.search.stats; import com.google.common.collect.ImmutableMap; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.metrics.CounterMetric; import org.elasticsearch.common.metrics.MeanMetric; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.search.slowlog.ShardSlowLogSearchService; import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.shard.AbstractIndexShardComponent; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.internal.SearchContext; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** */ public class ShardSearchService extends AbstractIndexShardComponent { private final ShardSlowLogSearchService slowLogSearchService; private final StatsHolder totalStats = new StatsHolder(); private volatile Map<String, StatsHolder> groupsStats = ImmutableMap.of(); @Inject public ShardSearchService(ShardId shardId, @IndexSettings Settings indexSettings, ShardSlowLogSearchService slowLogSearchService) { super(shardId, indexSettings); this.slowLogSearchService = slowLogSearchService; } /** * Returns the stats, including group specific stats. If the groups are null/0 length, then nothing * is returned for them. If they are set, then only groups provided will be returned, or * <tt>_all</tt> for all groups. */ public SearchStats stats(String... groups) { SearchStats.Stats total = totalStats.stats(); Map<String, SearchStats.Stats> groupsSt = null; if (groups != null && groups.length > 0) { if (groups.length == 1 && groups[0].equals("_all")) { groupsSt = new HashMap<String, SearchStats.Stats>(groupsStats.size()); for (Map.Entry<String, StatsHolder> entry : groupsStats.entrySet()) { groupsSt.put(entry.getKey(), entry.getValue().stats()); } } else { groupsSt = new HashMap<String, SearchStats.Stats>(groups.length); for (String group : groups) { StatsHolder statsHolder = groupsStats.get(group); if (statsHolder != null) { groupsSt.put(group, statsHolder.stats()); } } } } return new SearchStats(total, groupsSt); } public void onPreQueryPhase(SearchContext searchContext) { totalStats.queryCurrent.inc(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { groupStats(searchContext.groupStats().get(i)).queryCurrent.inc(); } } } public void onFailedQueryPhase(SearchContext searchContext) { totalStats.queryCurrent.dec(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { groupStats(searchContext.groupStats().get(i)).queryCurrent.dec(); } } } public void onQueryPhase(SearchContext searchContext, long tookInNanos) { totalStats.queryMetric.inc(tookInNanos); totalStats.queryCurrent.dec(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { StatsHolder statsHolder = groupStats(searchContext.groupStats().get(i)); statsHolder.queryMetric.inc(tookInNanos); statsHolder.queryCurrent.dec(); } } slowLogSearchService.onQueryPhase(searchContext, tookInNanos); } public void onPreFetchPhase(SearchContext searchContext) { totalStats.fetchCurrent.inc(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { groupStats(searchContext.groupStats().get(i)).fetchCurrent.inc(); } } } public void onFailedFetchPhase(SearchContext searchContext) { totalStats.fetchCurrent.dec(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { groupStats(searchContext.groupStats().get(i)).fetchCurrent.dec(); } } } public void onFetchPhase(SearchContext searchContext, long tookInNanos) { totalStats.fetchMetric.inc(tookInNanos); totalStats.fetchCurrent.dec(); if (searchContext.groupStats() != null) { for (int i = 0; i < searchContext.groupStats().size(); i++) { StatsHolder statsHolder = groupStats(searchContext.groupStats().get(i)); statsHolder.fetchMetric.inc(tookInNanos); statsHolder.fetchCurrent.dec(); } } slowLogSearchService.onFetchPhase(searchContext, tookInNanos); } public void clear() { totalStats.clear(); synchronized (this) { if (!groupsStats.isEmpty()) { MapBuilder<String, StatsHolder> typesStatsBuilder = MapBuilder.newMapBuilder(); for (Map.Entry<String, StatsHolder> typeStats : groupsStats.entrySet()) { if (typeStats.getValue().totalCurrent() > 0) { typeStats.getValue().clear(); typesStatsBuilder.put(typeStats.getKey(), typeStats.getValue()); } } groupsStats = typesStatsBuilder.immutableMap(); } } } private StatsHolder groupStats(String group) { StatsHolder stats = groupsStats.get(group); if (stats == null) { synchronized (this) { stats = groupsStats.get(group); if (stats == null) { stats = new StatsHolder(); groupsStats = MapBuilder.newMapBuilder(groupsStats).put(group, stats).immutableMap(); } } } return stats; } static class StatsHolder { public final MeanMetric queryMetric = new MeanMetric(); public final MeanMetric fetchMetric = new MeanMetric(); public final CounterMetric queryCurrent = new CounterMetric(); public final CounterMetric fetchCurrent = new CounterMetric(); public SearchStats.Stats stats() { return new SearchStats.Stats( queryMetric.count(), TimeUnit.NANOSECONDS.toMillis(queryMetric.sum()), queryCurrent.count(), fetchMetric.count(), TimeUnit.NANOSECONDS.toMillis(fetchMetric.sum()), fetchCurrent.count()); } public long totalCurrent() { return queryCurrent.count() + fetchCurrent.count(); } public void clear() { queryMetric.clear(); fetchMetric.clear(); } } }