diff options
author | Jon Bratseth <bratseth@oath.com> | 2018-04-24 15:26:18 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@oath.com> | 2018-04-24 15:26:18 +0200 |
commit | 06371345a7b7f7d27406bd8d72ca6769b7edb651 (patch) | |
tree | 4257b1f4b9f9891ce44bac60998029ebcf6b0e03 | |
parent | 29d894be652512bf2e44ce57ac126a35fb1985e1 (diff) |
Clear renderer hit groups
This allows us to stream more data than can fit in the container
(across all concurrent queries), as rendered hits in completed groups
can now be garbage collected.
We can not deference the hit groups themselves as that entails modifying
the parent list.
9 files changed, 62 insertions, 24 deletions
diff --git a/container-core/src/main/java/com/yahoo/processing/rendering/AsynchronousSectionedRenderer.java b/container-core/src/main/java/com/yahoo/processing/rendering/AsynchronousSectionedRenderer.java index eeb02f768f4..6e1d2627c22 100644 --- a/container-core/src/main/java/com/yahoo/processing/rendering/AsynchronousSectionedRenderer.java +++ b/container-core/src/main/java/com/yahoo/processing/rendering/AsynchronousSectionedRenderer.java @@ -363,6 +363,7 @@ public abstract class AsynchronousSectionedRenderer<RESPONSE extends Response> e endRenderLevel(list); stream.flush(); dataListListenerStack.removeFirst(); + list.close(); if (parent != null) parent.childCompleted(); } diff --git a/container-search/src/main/java/com/yahoo/fs4/mplex/ConnectionPool.java b/container-search/src/main/java/com/yahoo/fs4/mplex/ConnectionPool.java index 342dc120113..e84adfbef2c 100644 --- a/container-search/src/main/java/com/yahoo/fs4/mplex/ConnectionPool.java +++ b/container-search/src/main/java/com/yahoo/fs4/mplex/ConnectionPool.java @@ -13,10 +13,11 @@ import com.yahoo.log.LogLevel; /** * Pool of FS4 connections. * - * @author tonytv + * @author Tony Vaagenes */ public class ConnectionPool { - final static int CLEANINGPERIOD = 1000; // Execute every second + + private final static int CLEANINGPERIOD = 1000; // Execute every second private final Queue<FS4Connection> connections = new ConcurrentLinkedQueue<>(); private final AtomicInteger activeConnections = new AtomicInteger(0); private final AtomicInteger passiveConnections = new AtomicInteger(0); diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java b/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java index 030a3c08cd6..e4c351149f6 100644 --- a/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java +++ b/container-search/src/main/java/com/yahoo/search/grouping/result/AbstractList.java @@ -45,4 +45,10 @@ public abstract class AbstractList extends HitGroup { return continuations; } + @Override + public void close() { + super.close(); + continuations.clear(); + } + } diff --git a/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java b/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java index 9d6e7ebd233..c0c6c67e463 100644 --- a/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java +++ b/container-search/src/main/java/com/yahoo/search/grouping/result/Group.java @@ -10,7 +10,7 @@ import com.yahoo.search.result.Relevance; * as fields, use {@link #getField(String)} to access), {@link GroupList} and {@link HitList}. Use the {@link * com.yahoo.search.grouping.GroupingRequest#getResultGroup(com.yahoo.search.Result)} to retrieve an instance of this. * - * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + * @author Simon Thoresen */ public class Group extends HitGroup { diff --git a/container-search/src/main/java/com/yahoo/search/pagetemplates/result/SectionHitGroup.java b/container-search/src/main/java/com/yahoo/search/pagetemplates/result/SectionHitGroup.java index 5897628ace1..cd3578acd11 100644 --- a/container-search/src/main/java/com/yahoo/search/pagetemplates/result/SectionHitGroup.java +++ b/container-search/src/main/java/com/yahoo/search/pagetemplates/result/SectionHitGroup.java @@ -17,8 +17,8 @@ import java.util.List; public class SectionHitGroup extends HitGroup { private static final long serialVersionUID = -9048845836777953538L; - private List<Source> sources=new ArrayList<>(0); - private List<Renderer> renderers=new ArrayList<>(0); + private List<Source> sources = new ArrayList<>(0); + private List<Renderer> renderers = new ArrayList<>(0); private final String displayId; private boolean leaf=false; @@ -49,4 +49,10 @@ public class SectionHitGroup extends HitGroup { public void setLeaf(boolean leaf) { this.leaf=leaf; } + @Override + public void close() { + sources = null; + renderers = null; + } + } diff --git a/container-search/src/main/java/com/yahoo/search/result/Hit.java b/container-search/src/main/java/com/yahoo/search/result/Hit.java index 076e6659d2b..0d8f575b1f6 100644 --- a/container-search/src/main/java/com/yahoo/search/result/Hit.java +++ b/container-search/src/main/java/com/yahoo/search/result/Hit.java @@ -797,4 +797,10 @@ public class Hit extends ListenableFreezableClass implements Data, Comparable<Hi } } + protected void close() { + query = null; + fields = null; + unmodifiableFieldMap = null; + } + } 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 cb3a9abffc4..8ad8e6a732f 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 @@ -68,13 +68,13 @@ public class HitGroup extends Hit implements DataList<Hit>, Cloneable, Iterable< transient private HitOrderer hitOrderer = null; /** Accounting the number of subgroups to allow some early returns when the number is 0 */ - private int subgroupCount=0; + private int subgroupCount = 0; /** * The number of hits not cached at this level, not counting hits in subgroups or * any nested hitgroups themselves */ - private int notCachedCount=0; + private int notCachedCount = 0; /** * A direct reference to the errors of this result, or null if there are no errors. @@ -921,4 +921,13 @@ public class HitGroup extends Hit implements DataList<Hit>, Cloneable, Iterable< hits.addListener(runnable); } + @Override + public void close() { + super.close(); + hits = null; + unmodifiableHits = null; + hitOrderer = null; + incomingHits.drain(); // Just to gc as much as possible + } + } diff --git a/processing/src/main/java/com/yahoo/processing/response/DataList.java b/processing/src/main/java/com/yahoo/processing/response/DataList.java index 911cb6f70e6..1f762ab8d58 100644 --- a/processing/src/main/java/com/yahoo/processing/response/DataList.java +++ b/processing/src/main/java/com/yahoo/processing/response/DataList.java @@ -28,9 +28,9 @@ public interface DataList<DATATYPE extends Data> extends Data { * @param data the data to add to this * @return the input data instance, for chaining */ - public DATATYPE add(DATATYPE data); + DATATYPE add(DATATYPE data); - public DATATYPE get(int index); + DATATYPE get(int index); /** * Returns the content of this as a List. @@ -38,7 +38,7 @@ public interface DataList<DATATYPE extends Data> extends Data { * If the returned list is editable and this is frozen, the only allowed operation is to add new items * to the end of the list. */ - public List<DATATYPE> asList(); + List<DATATYPE> asList(); /** * Returns the buffer of incoming/future data to this list. @@ -49,7 +49,7 @@ public interface DataList<DATATYPE extends Data> extends Data { * such lists responds to <i>read</i> calls to IncomingData as expected and without * incurring any synchronization, and throws an exception on <i>write</i> calls. */ - public IncomingData<DATATYPE> incoming(); + IncomingData<DATATYPE> incoming(); /** * Returns a future in which all incoming data in this has become available. @@ -73,13 +73,22 @@ public interface DataList<DATATYPE extends Data> extends Data { * Making this call on a list which does not support future data always returns immediately and * causes no memory synchronization cost. */ - public ListenableFuture<DataList<DATATYPE>> complete(); + ListenableFuture<DataList<DATATYPE>> complete(); /** * Adds a listener which is invoked every time data is added to this list. * The listener is always invoked on the same thread which is adding the data, * and hence it can modify this list freely without synchronization. */ - public void addDataListener(Runnable runnable); + void addDataListener(Runnable runnable); + + /** + * Notify this list that is will never be accessed again, neither for read nor write. + * Implementations can override this as an optimization to release any data held in the list + * for garbage collection. + * + * This default implementation does nothing. + */ + default void close() {}; } diff --git a/processing/src/main/java/com/yahoo/processing/response/IncomingData.java b/processing/src/main/java/com/yahoo/processing/response/IncomingData.java index 7034b2c2a02..b8cdf8683bc 100644 --- a/processing/src/main/java/com/yahoo/processing/response/IncomingData.java +++ b/processing/src/main/java/com/yahoo/processing/response/IncomingData.java @@ -22,7 +22,7 @@ public interface IncomingData<DATATYPE extends Data> { * Note that accessing the owner from the thread producing incoming data * is generally *not* thread safe. */ - public DataList<DATATYPE> getOwner(); + DataList<DATATYPE> getOwner(); /** * Returns a future in which all the incoming data that will be produced in this is available. @@ -35,57 +35,57 @@ public interface IncomingData<DATATYPE extends Data> { * <p> * This return the list owning this for convenience. */ - public abstract ListenableFuture<DataList<DATATYPE>> completed(); + ListenableFuture<DataList<DATATYPE>> completed(); /** * Returns whether this is complete */ - public boolean isComplete(); + boolean isComplete(); /** * Add new data and mark this as completed * * @throws IllegalStateException if this is already complete or does not allow writes */ - public void addLast(DATATYPE data); + void addLast(DATATYPE data); /** * Add new data without completing this * * @throws IllegalStateException if this is already complete or does not allow writes */ - public void add(DATATYPE data); + void add(DATATYPE data); /** * Add new data and mark this as completed * * @throws IllegalStateException if this is already complete or does not allow writes */ - public void addLast(List<DATATYPE> data); + void addLast(List<DATATYPE> data); /** * Add new data without completing this. * * @throws IllegalStateException if this is already complete or does not allow writes */ - public void add(List<DATATYPE> data); + void add(List<DATATYPE> data); /** * Mark this as completed and notify any listeners. If this is already complete this method does nothing. */ - public void markComplete(); + void markComplete(); /** * Get and remove all the data currently available in this */ - public List<DATATYPE> drain(); + List<DATATYPE> drain(); /** * Add a listener which will be invoked every time new data is added to this. * This listener may be invoked at any time in any thread, any thread synchronization is left * to the listener itself */ - public void addNewDataListener(Runnable listener, Executor executor); + void addNewDataListener(Runnable listener, Executor executor); /** * Creates a null implementation of this which is empty and complete at creation: @@ -98,7 +98,7 @@ public interface IncomingData<DATATYPE extends Data> { * This allows consumers to check for completion the same way whether or not the data list in question * supports asynchronous addition of data, and without incurring unnecessary costs. */ - public static final class NullIncomingData<DATATYPE extends Data> implements IncomingData<DATATYPE> { + final class NullIncomingData<DATATYPE extends Data> implements IncomingData<DATATYPE> { private DataList<DATATYPE> owner; private final ImmediateFuture<DATATYPE> completionFuture; |