summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlli Virtanen <olli.virtanen@oath.com>2019-04-26 14:16:16 +0200
committerOlli Virtanen <olli.virtanen@oath.com>2019-04-26 14:16:16 +0200
commit12aae4a257f8762d56d3ebe7f67ce7d27fa6f2f1 (patch)
treed810bb639e2ea8379a7d6390d0c63300a507c279
parent5f6a5a5d626808459bc65c95eb226e746ed3d18f (diff)
Avoid dynamic ArrayList growth in HitGroup when number of hits is known
-rw-r--r--container-search/abi-spec.json1
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4FillInvoker.java6
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4SearchInvoker.java136
-rw-r--r--container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java126
-rw-r--r--container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java11
-rw-r--r--container-search/src/main/java/com/yahoo/search/result/HitGroup.java4
6 files changed, 149 insertions, 135 deletions
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index b968d1c391f..fb50da7bff1 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -7252,6 +7252,7 @@
"public void setId(com.yahoo.net.URI)",
"public void setOrdered(boolean)",
"public int size()",
+ "public void ensureCapacity(int)",
"public int getConcreteSize()",
"public int getConcreteSizeShallow()",
"public int getSubgroupCount()",
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4FillInvoker.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4FillInvoker.java
index 190a7905476..5dc213a6120 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4FillInvoker.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4FillInvoker.java
@@ -9,6 +9,7 @@ import com.yahoo.fs4.mplex.Backend;
import com.yahoo.fs4.mplex.FS4Channel;
import com.yahoo.fs4.mplex.InvalidChannelException;
import com.yahoo.prelude.fastsearch.VespaBackEndSearcher.FillHitsResult;
+import com.yahoo.processing.request.CompoundName;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.dispatch.FillInvoker;
@@ -26,7 +27,6 @@ import static com.yahoo.prelude.fastsearch.VespaBackEndSearcher.hitIterator;
* @author ollivir
*/
public class FS4FillInvoker extends FillInvoker {
-
private final VespaBackEndSearcher searcher;
private FS4Channel channel;
@@ -156,10 +156,10 @@ public class FS4FillInvoker extends FillInvoker {
result.getQuery().trace((summaryNeedsQuery ? "Resending " : "Not resending ") + "query during document summary fetching", 3);
GetDocSumsPacket docsumsPacket = GetDocSumsPacket.create(result, summaryClass, summaryNeedsQuery);
- int compressionLimit = result.getQuery().properties().getInteger(FastSearcher.PACKET_COMPRESSION_LIMIT, 0);
+ int compressionLimit = result.getQuery().properties().getInteger(FS4SearchInvoker.PACKET_COMPRESSION_LIMIT, 0);
docsumsPacket.setCompressionLimit(compressionLimit);
if (compressionLimit != 0) {
- docsumsPacket.setCompressionType(result.getQuery().properties().getString(FastSearcher.PACKET_COMPRESSION_TYPE, "lz4"));
+ docsumsPacket.setCompressionType(result.getQuery().properties().getString(FS4SearchInvoker.PACKET_COMPRESSION_TYPE, "lz4"));
}
boolean couldSend = channel.sendPacket(docsumsPacket);
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4SearchInvoker.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4SearchInvoker.java
index 2353054103d..27c4c73dd65 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4SearchInvoker.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/FS4SearchInvoker.java
@@ -1,21 +1,39 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.prelude.fastsearch;
+import com.yahoo.data.access.simple.Value;
+import com.yahoo.data.access.slime.SlimeAdapter;
import com.yahoo.fs4.BasicPacket;
import com.yahoo.fs4.ChannelTimeoutException;
+import com.yahoo.fs4.DocumentInfo;
+import com.yahoo.fs4.FS4Properties;
import com.yahoo.fs4.QueryPacket;
+import com.yahoo.fs4.QueryPacketData;
import com.yahoo.fs4.QueryResultPacket;
import com.yahoo.fs4.mplex.FS4Channel;
import com.yahoo.fs4.mplex.InvalidChannelException;
+import com.yahoo.io.GrowableByteBuffer;
+import com.yahoo.log.LogLevel;
+import com.yahoo.prelude.ConfigurationException;
+import com.yahoo.processing.request.CompoundName;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.dispatch.ResponseMonitor;
import com.yahoo.search.dispatch.SearchInvoker;
import com.yahoo.search.dispatch.searchcluster.Node;
+import com.yahoo.search.query.Sorting;
+import com.yahoo.search.result.Coverage;
import com.yahoo.search.result.ErrorMessage;
+import com.yahoo.search.result.Relevance;
import com.yahoo.search.searchchain.Execution;
+import com.yahoo.searchlib.aggregation.Grouping;
+import com.yahoo.slime.BinaryFormat;
+import com.yahoo.vespa.objects.BufferSerializer;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
@@ -25,6 +43,9 @@ import java.util.logging.Logger;
* @author ollivir
*/
public class FS4SearchInvoker extends SearchInvoker implements ResponseMonitor<FS4Channel> {
+ static final CompoundName PACKET_COMPRESSION_LIMIT = new CompoundName("packetcompressionlimit");
+ static final CompoundName PACKET_COMPRESSION_TYPE = new CompoundName("packetcompressiontype");
+
private static final Logger log = Logger.getLogger(FS4SearchInvoker.class.getName());
private final VespaBackEndSearcher searcher;
@@ -48,7 +69,7 @@ public class FS4SearchInvoker extends SearchInvoker implements ResponseMonitor<F
log.finest("sending query packet");
this.query = query;
- this.queryPacket = searcher.createQueryPacket(searcher.getServerId(), query);
+ createQueryPacket(searcher.getServerId(), query);
try {
boolean couldSend = channel.sendPacket(queryPacket);
@@ -98,13 +119,120 @@ public class FS4SearchInvoker extends SearchInvoker implements ResponseMonitor<F
Result result = new Result(query);
- searcher.addMetaInfo(query, queryPacket.getQueryPacketData(), resultPacket, result);
-
- searcher.addUnfilledHits(result, resultPacket.getDocuments(), queryPacket.getQueryPacketData(), distributionKey());
+ ensureResultHitCapacity(result, resultPacket);
+ addMetaInfo(query, queryPacket.getQueryPacketData(), resultPacket, result);
+ addUnfilledHits(result, resultPacket.getDocuments(), queryPacket.getQueryPacketData());
return result;
}
+ private QueryPacket createQueryPacket(String serverId, Query query) {
+ this.queryPacket = QueryPacket.create(serverId, query);
+ int compressionLimit = query.properties().getInteger(PACKET_COMPRESSION_LIMIT, 0);
+ queryPacket.setCompressionLimit(compressionLimit);
+ if (compressionLimit != 0) {
+ queryPacket.setCompressionType(query.properties().getString(PACKET_COMPRESSION_TYPE, "lz4"));
+ }
+ log.fine(() -> "made QueryPacket: " + queryPacket);
+
+ return queryPacket;
+ }
+
+ private void ensureResultHitCapacity(Result result, QueryResultPacket resultPacket) {
+ int hitCount = resultPacket.getDocumentCount();
+ if (resultPacket.getGroupData() != null) {
+ hitCount++;
+ }
+ result.hits().ensureCapacity(hitCount);
+ }
+
+ private void addMetaInfo(Query query, QueryPacketData queryPacketData, QueryResultPacket resultPacket, Result result) {
+ result.setTotalHitCount(resultPacket.getTotalDocumentCount());
+
+ addBackendTrace(query, resultPacket);
+
+ // Grouping
+ if (resultPacket.getGroupData() != null) {
+ byte[] data = resultPacket.getGroupData();
+ ArrayList<Grouping> list = new ArrayList<>();
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer(ByteBuffer.wrap(data)));
+ int cnt = buf.getInt(null);
+ for (int i = 0; i < cnt; i++) {
+ Grouping g = new Grouping();
+ g.deserialize(buf);
+ list.add(g);
+ }
+ GroupingListHit hit = new GroupingListHit(list, searcher.getDocsumDefinitionSet(query));
+ hit.setQuery(result.getQuery());
+ hit.setSource(getName());
+ hit.setQueryPacketData(queryPacketData);
+ result.hits().add(hit);
+ }
+
+ if (resultPacket.getCoverageFeature()) {
+ result.setCoverage(new Coverage(resultPacket.getCoverageDocs(), resultPacket.getActiveDocs(), resultPacket.getNodesReplied())
+ .setSoonActive(resultPacket.getSoonActiveDocs())
+ .setDegradedReason(resultPacket.getDegradedReason())
+ .setNodesTried(resultPacket.getNodesQueried()));
+ }
+ }
+
+ private void addBackendTrace(Query query, QueryResultPacket resultPacket) {
+ if (resultPacket.propsArray == null) return;
+ Value.ArrayValue traces = new Value.ArrayValue();
+ for (FS4Properties properties : resultPacket.propsArray) {
+ if ( ! properties.getName().equals("trace")) continue;
+ for (FS4Properties.Entry entry : properties.getEntries()) {
+ traces.add(new SlimeAdapter(BinaryFormat.decode(entry.getValue()).get()));
+ }
+ }
+ query.trace(traces, query.getTraceLevel());
+ }
+
+ /**
+ * Creates unfilled hits from a List of DocumentInfo instances.
+ */
+ private void addUnfilledHits(Result result, List<DocumentInfo> documents, QueryPacketData queryPacketData) {
+ Query myQuery = result.getQuery();
+ Sorting sorting = myQuery.getRanking().getSorting();
+ Optional<Integer> channelDistributionKey = distributionKey();
+
+ for (DocumentInfo document : documents) {
+
+ try {
+ FastHit hit = new FastHit();
+ hit.setQuery(myQuery);
+ if (queryPacketData != null)
+ hit.setQueryPacketData(queryPacketData);
+
+ hit.setFillable();
+ hit.setCached(false);
+
+ extractDocumentInfo(hit, document, sorting);
+ channelDistributionKey.ifPresent(hit::setDistributionKey);
+
+ result.hits().add(hit);
+ } catch (ConfigurationException e) {
+ log.log(LogLevel.WARNING, "Skipping hit", e);
+ } catch (Exception e) {
+ log.log(LogLevel.ERROR, "Skipping malformed hit", e);
+ }
+ }
+ }
+
+ private void extractDocumentInfo(FastHit hit, DocumentInfo document, Sorting sorting) {
+ hit.setSource(getName());
+
+ Number rank = document.getMetric();
+
+ hit.setRelevance(new Relevance(rank.doubleValue()));
+
+ hit.setDistributionKey(document.getDistributionKey());
+ hit.setGlobalId(document.getGlobalId());
+ hit.setPartId(document.getPartId());
+ hit.setSortData(document.getSortData(), sorting);
+ }
+
@Override
public void release() {
if (channel != null) {
diff --git a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
index f5e437c2410..055b41fd89d 100644
--- a/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
+++ b/container-search/src/main/java/com/yahoo/prelude/fastsearch/VespaBackEndSearcher.java
@@ -2,18 +2,9 @@
package com.yahoo.prelude.fastsearch;
import com.yahoo.collections.TinyIdentitySet;
-import com.yahoo.data.access.simple.Value;
-import com.yahoo.data.access.slime.SlimeAdapter;
import com.yahoo.fs4.DocsumPacket;
-import com.yahoo.fs4.DocumentInfo;
-import com.yahoo.fs4.FS4Properties;
import com.yahoo.fs4.Packet;
import com.yahoo.fs4.QueryPacket;
-import com.yahoo.fs4.QueryPacketData;
-import com.yahoo.fs4.QueryResultPacket;
-import com.yahoo.io.GrowableByteBuffer;
-import com.yahoo.log.LogLevel;
-import com.yahoo.prelude.ConfigurationException;
import com.yahoo.prelude.query.Item;
import com.yahoo.prelude.query.NullItem;
import com.yahoo.prelude.query.textualrepresentation.TextualQueryRepresentation;
@@ -24,20 +15,14 @@ import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.cluster.PingableSearcher;
import com.yahoo.search.grouping.vespa.GroupingExecutor;
-import com.yahoo.search.query.Sorting;
-import com.yahoo.search.result.Coverage;
import com.yahoo.search.result.ErrorHit;
import com.yahoo.search.result.ErrorMessage;
import com.yahoo.search.result.Hit;
-import com.yahoo.search.result.Relevance;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.searchlib.aggregation.Grouping;
-import com.yahoo.slime.BinaryFormat;
-import com.yahoo.vespa.objects.BufferSerializer;
import java.io.IOException;
import java.lang.reflect.Constructor;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -55,8 +40,6 @@ import java.util.logging.Logger;
*/
public abstract class VespaBackEndSearcher extends PingableSearcher {
- static final CompoundName PACKET_COMPRESSION_LIMIT = new CompoundName("packetcompressionlimit");
- static final CompoundName PACKET_COMPRESSION_TYPE = new CompoundName("packetcompressiontype");
private static final CompoundName TRACE_DISABLE = new CompoundName("trace.disable");
private String serverId;
@@ -198,19 +181,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
return result;
}
- protected QueryPacket createQueryPacket(String serverId, Query query) {
- QueryPacket queryPacket = QueryPacket.create(serverId, query);
- int compressionLimit = query.properties().getInteger(PACKET_COMPRESSION_LIMIT, 0);
- queryPacket.setCompressionLimit(compressionLimit);
- if (compressionLimit != 0)
- queryPacket.setCompressionType(query.properties().getString(PACKET_COMPRESSION_TYPE, "lz4"));
-
- if (isLoggingFine())
- getLogger().fine("made QueryPacket: " + queryPacket);
-
- return queryPacket;
- }
-
private List<Result> partitionHits(Result result, String summaryClass) {
List<Result> parts = new ArrayList<>();
TinyIdentitySet<Query> queryMap = new TinyIdentitySet<>(4);
@@ -343,49 +313,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
}
}
- private void addBackendTrace(Query query, QueryResultPacket resultPacket) {
- if (resultPacket.propsArray == null) return;
- Value.ArrayValue traces = new Value.ArrayValue();
- for (FS4Properties properties : resultPacket.propsArray) {
- if ( ! properties.getName().equals("trace")) continue;
- for (FS4Properties.Entry entry : properties.getEntries()) {
- traces.add(new SlimeAdapter(BinaryFormat.decode(entry.getValue()).get()));
- }
- }
- query.trace(traces, query.getTraceLevel());
- }
-
- void addMetaInfo(Query query, QueryPacketData queryPacketData, QueryResultPacket resultPacket, Result result) {
- result.setTotalHitCount(resultPacket.getTotalDocumentCount());
-
- addBackendTrace(query, resultPacket);
-
- // Grouping
- if (resultPacket.getGroupData() != null) {
- byte[] data = resultPacket.getGroupData();
- ArrayList<Grouping> list = new ArrayList<>();
- BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer(ByteBuffer.wrap(data)));
- int cnt = buf.getInt(null);
- for (int i = 0; i < cnt; i++) {
- Grouping g = new Grouping();
- g.deserialize(buf);
- list.add(g);
- }
- GroupingListHit hit = new GroupingListHit(list, getDocsumDefinitionSet(query));
- hit.setQuery(result.getQuery());
- hit.setSource(getName());
- hit.setQueryPacketData(queryPacketData);
- result.hits().add(hit);
- }
-
- if (resultPacket.getCoverageFeature()) {
- result.setCoverage(new Coverage(resultPacket.getCoverageDocs(), resultPacket.getActiveDocs(), resultPacket.getNodesReplied())
- .setSoonActive(resultPacket.getSoonActiveDocs())
- .setDegradedReason(resultPacket.getDegradedReason())
- .setNodesTried(resultPacket.getNodesQueried()));
- }
- }
-
static class FillHitResult {
final boolean ok;
final String error;
@@ -451,19 +378,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
return new FillHitsResult(skippedHits, lastError);
}
- private void extractDocumentInfo(FastHit hit, DocumentInfo document, Sorting sorting) {
- hit.setSource(getName());
-
- Number rank = document.getMetric();
-
- hit.setRelevance(new Relevance(rank.doubleValue()));
-
- hit.setDistributionKey(document.getDistributionKey());
- hit.setGlobalId(document.getGlobalId());
- hit.setPartId(document.getPartId());
- hit.setSortData(document.getSortData(), sorting);
- }
-
protected DocsumDefinitionSet getDocsumDefinitionSet(Query query) {
DocumentDatabase db = getDocumentDatabase(query);
return db.getDocsumDefinitionSet();
@@ -483,46 +397,6 @@ public abstract class VespaBackEndSearcher extends PingableSearcher {
return error;
}
- /**
- * Creates unfilled hits from a List of DocumentInfo instances. Do note
- * cacheKey should be available if a cache is active, even if the hit is not
- * created from a cache in the current call path.
- *
- * @param queryPacketData binary data from first phase of search, or null
- * @param channelDistributionKey distribution key of the node producing these hits.
- * Only set if produced directly by a search node, not dispatch
- * (in which case it is not set in the received packets.)
- */
- void addUnfilledHits(Result result,
- List<DocumentInfo> documents,
- QueryPacketData queryPacketData,
- Optional<Integer> channelDistributionKey) {
- Query myQuery = result.getQuery();
- Sorting sorting = myQuery.getRanking().getSorting();
-
- for (DocumentInfo document : documents) {
-
- try {
- FastHit hit = new FastHit();
- hit.setQuery(myQuery);
- if (queryPacketData != null)
- hit.setQueryPacketData(queryPacketData);
-
- hit.setFillable();
- hit.setCached(false);
-
- extractDocumentInfo(hit, document, sorting);
- channelDistributionKey.ifPresent(hit::setDistributionKey);
-
- result.hits().add(hit);
- } catch (ConfigurationException e) {
- getLogger().log(LogLevel.WARNING, "Skipping hit", e);
- } catch (Exception e) {
- getLogger().log(LogLevel.ERROR, "Skipping malformed hit", e);
- }
- }
- }
-
@SuppressWarnings("rawtypes")
public static VespaBackEndSearcher getSearcher(String s) {
try {
diff --git a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
index 033b507b4a4..76da9fbc1f7 100644
--- a/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
+++ b/container-search/src/main/java/com/yahoo/search/dispatch/rpc/ProtobufSerialization.java
@@ -168,10 +168,17 @@ public class ProtobufSerialization {
result.setTotalHitCount(protobuf.getTotalHitCount());
result.setCoverage(convertToCoverage(protobuf));
- if (protobuf.getGroupingBlob() != null && !protobuf.getGroupingBlob().isEmpty()) {
- ArrayList<Grouping> list = new ArrayList<>();
+ int hitItems = protobuf.getHitsCount();
+ var haveGrouping = protobuf.getGroupingBlob() != null && !protobuf.getGroupingBlob().isEmpty();
+ if(haveGrouping) {
+ hitItems++;
+ }
+ result.hits().ensureCapacity(hitItems);
+
+ if (haveGrouping) {
BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer(protobuf.getGroupingBlob().asReadOnlyByteBuffer()));
int cnt = buf.getInt(null);
+ ArrayList<Grouping> list = new ArrayList<>(cnt);
for (int i = 0; i < cnt; i++) {
Grouping g = new Grouping();
g.deserialize(buf);
diff --git a/container-search/src/main/java/com/yahoo/search/result/HitGroup.java b/container-search/src/main/java/com/yahoo/search/result/HitGroup.java
index 8755d0b0897..9ecd6513a94 100644
--- a/container-search/src/main/java/com/yahoo/search/result/HitGroup.java
+++ b/container-search/src/main/java/com/yahoo/search/result/HitGroup.java
@@ -191,6 +191,10 @@ public class HitGroup extends Hit implements DataList<Hit>, Cloneable, Iterable<
return hits.size();
}
+ public void ensureCapacity(int minCapacity) {
+ hits.ensureCapacity(minCapacity);
+ }
+
/**
* <p>Returns the number of concrete hits contained in this group
* and all subgroups. This should equal the