From 12aae4a257f8762d56d3ebe7f67ce7d27fa6f2f1 Mon Sep 17 00:00:00 2001 From: Olli Virtanen Date: Fri, 26 Apr 2019 14:16:16 +0200 Subject: Avoid dynamic ArrayList growth in HitGroup when number of hits is known --- .../yahoo/prelude/fastsearch/FS4FillInvoker.java | 6 +- .../yahoo/prelude/fastsearch/FS4SearchInvoker.java | 136 ++++++++++++++++++++- .../prelude/fastsearch/VespaBackEndSearcher.java | 126 ------------------- .../search/dispatch/rpc/ProtobufSerialization.java | 11 +- .../java/com/yahoo/search/result/HitGroup.java | 4 + 5 files changed, 148 insertions(+), 135 deletions(-) (limited to 'container-search/src') 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 { + 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 "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 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 documents, QueryPacketData queryPacketData) { + Query myQuery = result.getQuery(); + Sorting sorting = myQuery.getRanking().getSorting(); + Optional 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 partitionHits(Result result, String summaryClass) { List parts = new ArrayList<>(); TinyIdentitySet 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 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 documents, - QueryPacketData queryPacketData, - Optional 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 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 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, Cloneable, Iterable< return hits.size(); } + public void ensureCapacity(int minCapacity) { + hits.ensureCapacity(minCapacity); + } + /** *

Returns the number of concrete hits contained in this group * and all subgroups. This should equal the -- cgit v1.2.3