Skip to content

Commit

Permalink
Add rescorer stats to the search usage stats.
Browse files Browse the repository at this point in the history
  • Loading branch information
afoucret committed Dec 1, 2023
1 parent 5bd61f8 commit f3579e7
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.monitor.os.OsStats;
import org.elasticsearch.node.NodeRoleSettings;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.rescore.QueryRescorerBuilder;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.ESIntegTestCase.Scope;
Expand Down Expand Up @@ -352,16 +354,26 @@ public void testSearchUsageStats() throws IOException {
);
getRestClient().performRequest(request);
}
{
Request request = new Request("GET", "/_search");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(QueryBuilders.termQuery("field", "value"))
.addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f)));
request.setJsonEntity(Strings.toString(searchSourceBuilder));
getRestClient().performRequest(request);
}

SearchUsageStats stats = clusterAdmin().prepareClusterStats().get().getIndicesStats().getSearchUsageStats();
assertEquals(5, stats.getTotalSearchCount());
assertEquals(6, stats.getTotalSearchCount());
assertEquals(4, stats.getQueryUsage().size());
assertEquals(1, stats.getQueryUsage().get("match").longValue());
assertEquals(2, stats.getQueryUsage().get("term").longValue());
assertEquals(3, stats.getQueryUsage().get("term").longValue());
assertEquals(1, stats.getQueryUsage().get("range").longValue());
assertEquals(1, stats.getQueryUsage().get("bool").longValue());
assertEquals(2, stats.getSectionsUsage().size());
assertEquals(4, stats.getSectionsUsage().get("query").longValue());
assertEquals(3, stats.getSectionsUsage().size());
assertEquals(5, stats.getSectionsUsage().get("query").longValue());
assertEquals(1, stats.getSectionsUsage().get("aggs").longValue());
assertEquals(1, stats.getSectionsUsage().get("rescore").longValue());
assertEquals(1, stats.getRescorerUsage().size());
assertEquals(1, stats.getRescorerUsage().get("query").longValue());
}
}
2 changes: 2 additions & 0 deletions server/src/main/java/org/elasticsearch/TransportVersions.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ static TransportVersion def(int id) {
public static final TransportVersion GET_API_KEY_INVALIDATION_TIME_ADDED = def(8_548_00_0);
public static final TransportVersion ML_INFERENCE_GET_MULTIPLE_MODELS = def(8_549_00_0);
public static final TransportVersion INFERENCE_SERVICE_RESULTS_ADDED = def(8_550_00_0);
public static final TransportVersion CLUSTER_STATS_RESCORER_USAGE_ADDED = def(8_551_00_0);

/*
* STOP! READ THIS FIRST! No, really,
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.TransportVersions.CLUSTER_STATS_RESCORER_USAGE_ADDED;

/**
* Holds a snapshot of the search usage statistics.
* Used to hold the stats for a single node that's part of a {@link ClusterStatsNodeResponse}, as well as to
Expand All @@ -29,6 +31,7 @@
public final class SearchUsageStats implements Writeable, ToXContentFragment {
private long totalSearchCount;
private final Map<String, Long> queries;
private final Map<String, Long> rescorers;
private final Map<String, Long> sections;

/**
Expand All @@ -38,36 +41,46 @@ public SearchUsageStats() {
this.totalSearchCount = 0L;
this.queries = new HashMap<>();
this.sections = new HashMap<>();
this.rescorers = new HashMap<>();
}

/**
* Creates a new stats instance with the provided info. The expectation is that when a new instance is created using
* this constructor, the provided stats are final and won't be modified further.
*/
public SearchUsageStats(Map<String, Long> queries, Map<String, Long> sections, long totalSearchCount) {
public SearchUsageStats(Map<String, Long> queries, Map<String, Long> rescorers, Map<String, Long> sections, long totalSearchCount) {
this.totalSearchCount = totalSearchCount;
this.queries = queries;
this.sections = sections;
this.rescorers = rescorers;
}

public SearchUsageStats(StreamInput in) throws IOException {
this.queries = in.readMap(StreamInput::readLong);
this.sections = in.readMap(StreamInput::readLong);
this.totalSearchCount = in.readVLong();
this.rescorers = in.getTransportVersion().onOrAfter(CLUSTER_STATS_RESCORER_USAGE_ADDED)
? in.readMap(StreamInput::readLong)
: Map.of();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeMap(queries, StreamOutput::writeLong);
out.writeMap(sections, StreamOutput::writeLong);
out.writeVLong(totalSearchCount);

if (out.getTransportVersion().onOrAfter(CLUSTER_STATS_RESCORER_USAGE_ADDED)) {
out.writeMap(rescorers, StreamOutput::writeLong);
}
}

/**
* Add the provided stats to the ones held by the current instance, effectively merging the two
*/
public void add(SearchUsageStats stats) {
stats.queries.forEach((query, count) -> queries.merge(query, count, Long::sum));
stats.rescorers.forEach((rescorer, count) -> rescorers.merge(rescorer, count, Long::sum));
stats.sections.forEach((query, count) -> sections.merge(query, count, Long::sum));
this.totalSearchCount += stats.totalSearchCount;
}
Expand All @@ -79,6 +92,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
{
builder.field("queries");
builder.map(queries);
builder.field("rescorers");
builder.map(rescorers);
builder.field("sections");
builder.map(sections);
}
Expand All @@ -90,6 +105,10 @@ public Map<String, Long> getQueryUsage() {
return Collections.unmodifiableMap(queries);
}

public Map<String, Long> getRescorerUsage() {
return Collections.unmodifiableMap(rescorers);
}

public Map<String, Long> getSectionsUsage() {
return Collections.unmodifiableMap(sections);
}
Expand All @@ -107,12 +126,15 @@ public boolean equals(Object o) {
return false;
}
SearchUsageStats that = (SearchUsageStats) o;
return totalSearchCount == that.totalSearchCount && queries.equals(that.queries) && sections.equals(that.sections);
return totalSearchCount == that.totalSearchCount
&& queries.equals(that.queries)
&& rescorers.equals(that.rescorers)
&& sections.equals(that.sections);
}

@Override
public int hashCode() {
return Objects.hash(totalSearchCount, queries, sections);
return Objects.hash(totalSearchCount, queries, rescorers, sections);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr
sorts = new ArrayList<>(SortBuilder.fromXContent(parser));
} else if (RESCORE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
rescoreBuilders = new ArrayList<>();
rescoreBuilders.add(RescorerBuilder.parseFromXContent(parser, searchUsage::trackQueryUsage));
rescoreBuilders.add(RescorerBuilder.parseFromXContent(parser, searchUsage::trackRescorerUsage));
searchUsage.trackSectionUsage(RESCORE_FIELD.getPreferredName());
} else if (EXT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
extBuilders = new ArrayList<>();
Expand Down Expand Up @@ -1498,7 +1498,7 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr
} else if (RESCORE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
rescoreBuilders = new ArrayList<>();
while ((parser.nextToken()) != XContentParser.Token.END_ARRAY) {
rescoreBuilders.add(RescorerBuilder.parseFromXContent(parser, searchUsage::trackQueryUsage));
rescoreBuilders.add(RescorerBuilder.parseFromXContent(parser, searchUsage::trackRescorerUsage));
}
searchUsage.trackSectionUsage(RESCORE_FIELD.getPreferredName());
} else if (STATS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import java.util.Objects;
import java.util.function.Consumer;

import static org.elasticsearch.search.builder.SearchSourceBuilder.RESCORE_FIELD;

/**
* The abstract base builder for instances of {@link RescorerBuilder}.
*/
Expand Down Expand Up @@ -86,7 +84,7 @@ public static RescorerBuilder<?> parseFromXContent(XContentParser parser, Consum
}
} else if (token == XContentParser.Token.START_OBJECT) {
rescorer = parser.namedObject(RescorerBuilder.class, fieldName, null);
rescorerNameConsumer.accept(RESCORE_FIELD.getPreferredName() + "_" + rescorer.getWriteableName());
rescorerNameConsumer.accept(fieldName);
} else {
throw new ParsingException(parser.getTokenLocation(), "unexpected token [" + token + "] after [" + fieldName + "]");
}
Expand Down
15 changes: 15 additions & 0 deletions server/src/main/java/org/elasticsearch/usage/SearchUsage.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
public final class SearchUsage {
private final Set<String> queries = new HashSet<>();
private final Set<String> rescorers = new HashSet<>();
private final Set<String> sections = new HashSet<>();

/**
Expand All @@ -33,13 +34,27 @@ public void trackSectionUsage(String section) {
sections.add(section);
}

/**
* Track the usage of the provided rescorer
*/
public void trackRescorerUsage(String section) {
rescorers.add(section);
}

/**
* Returns the query types that have been used at least once in the tracked search request
*/
public Set<String> getQueryUsage() {
return Collections.unmodifiableSet(queries);
}

/**
* Returns the rescorer types that have been used at least once in the tracked search request
*/
public Set<String> getRescorerUsage() {
return Collections.unmodifiableSet(rescorers);
}

/**
* Returns the search section names that have been used at least once in the tracked search request
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
public final class SearchUsageHolder {
private final LongAdder totalSearchCount = new LongAdder();
private final Map<String, LongAdder> queriesUsage = new ConcurrentHashMap<>();
private final Map<String, LongAdder> rescorersUsage = new ConcurrentHashMap<>();
private final Map<String, LongAdder> sectionsUsage = new ConcurrentHashMap<>();

SearchUsageHolder() {}
Expand All @@ -39,6 +40,9 @@ public void updateUsage(SearchUsage searchUsage) {
for (String query : searchUsage.getQueryUsage()) {
queriesUsage.computeIfAbsent(query, q -> new LongAdder()).increment();
}
for (String rescorer : searchUsage.getRescorerUsage()) {
rescorersUsage.computeIfAbsent(rescorer, q -> new LongAdder()).increment();
}
}

/**
Expand All @@ -49,8 +53,11 @@ public SearchUsageStats getSearchUsageStats() {
queriesUsage.forEach((query, adder) -> queriesUsageMap.put(query, adder.longValue()));
Map<String, Long> sectionsUsageMap = Maps.newMapWithExpectedSize(sectionsUsage.size());
sectionsUsage.forEach((query, adder) -> sectionsUsageMap.put(query, adder.longValue()));
Map<String, Long> rescorersUsageMap = Maps.newMapWithExpectedSize(rescorersUsage.size());
rescorersUsage.forEach((query, adder) -> rescorersUsageMap.put(query, adder.longValue()));
return new SearchUsageStats(
Collections.unmodifiableMap(queriesUsageMap),
Collections.unmodifiableMap(rescorersUsageMap),
Collections.unmodifiableMap(sectionsUsageMap),
totalSearchCount.longValue()
);
Expand Down
Loading

0 comments on commit f3579e7

Please sign in to comment.