Hi Mark,

On Fri, Sep 25, 2020 at 4:35 PM <ma...@apache.org> wrote:

> This is an automated email from the ASF dual-hosted git repository.
>
> markt pushed a commit to branch master
> in repository https://gitbox.apache.org/repos/asf/tomcat.git
>
> commit 954cbffa73efe6e546a07c84ade97a3b9b38a893
> Author: Mark Thomas <ma...@apache.org>
> AuthorDate: Fri Sep 25 14:14:06 2020 +0100
>
>     Refactor the pruning so more stream info is retained for longer
>
>     The memory impact of this is mitigated by the previous changes to
>     replace closed Stream instances with RecycledStream instances.
> ---
>  .../apache/coyote/http2/Http2UpgradeHandler.java   | 89
> ++++++++++++----------
>  1 file changed, 49 insertions(+), 40 deletions(-)
>
> diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
> b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
> index a023fe0..49115d3 100644
> --- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
> +++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
> @@ -30,6 +30,7 @@ import java.util.TreeSet;
>  import java.util.concurrent.ConcurrentHashMap;
>  import java.util.concurrent.ConcurrentLinkedQueue;
>  import java.util.concurrent.ConcurrentMap;
> +import java.util.concurrent.ConcurrentSkipListMap;
>  import java.util.concurrent.atomic.AtomicInteger;
>  import java.util.concurrent.atomic.AtomicLong;
>  import java.util.concurrent.atomic.AtomicReference;
> @@ -123,7 +124,7 @@ class Http2UpgradeHandler extends AbstractStream
> implements InternalHttpUpgradeH
>      private HpackDecoder hpackDecoder;
>      private HpackEncoder hpackEncoder;
>
> -    private final ConcurrentMap<Integer,AbstractNonZeroStream> streams =
> new ConcurrentHashMap<>();
> +    private final ConcurrentMap<Integer,AbstractNonZeroStream> streams =
> new ConcurrentSkipListMap<>();
>

In my branch I also moved to ConcurrentSkipListMap:
https://github.com/martin-g/tomcat/commit/08cca44fbb9bd05e35c5655ff7d6ea9781bc28fa
I did it because #closeIdleStreams() was appearing in the profiler. With
this change it is no more in the profiler results!
Now the only drawback is that streams.size() (called
in org.apache.coyote.http2.Http2UpgradeHandler#pruneClosedStreams()) is no
longer a constant-time operation.


>      protected final AtomicInteger activeRemoteStreamCount = new
> AtomicInteger(0);
>      // Start at -1 so the 'add 2' logic in closeIdleStreams() works
>      private volatile int maxActiveRemoteStreamId = -1;
> @@ -1207,78 +1208,86 @@ class Http2UpgradeHandler extends AbstractStream
> implements InternalHttpUpgradeH
>          // 2. Completed streams used for a request with children
>          // 3. Closed final streams
>          //
> -        // Steps 1 and 2 will always be completed.
> -        // Step 3 will be completed to the minimum extent necessary to
> bring the
> -        // total number of streams under the limit.
> +        // The pruning halts as soon as enough streams have been pruned.
>
>          // Use these sets to track the different classes of streams
> -        TreeSet<Integer> candidatesStepOne = new TreeSet<>();
>          TreeSet<Integer> candidatesStepTwo = new TreeSet<>();
>          TreeSet<Integer> candidatesStepThree = new TreeSet<>();
>
> -        for (Entry<Integer, AbstractNonZeroStream> entry :
> streams.entrySet()) {
> -            AbstractNonZeroStream stream = entry.getValue();
> +        // Step 1
> +        // Iterator is in key order so we automatically have the oldest
> streams
> +        // first
> +        for (AbstractNonZeroStream stream : streams.values()) {
>              // Never remove active streams
>              if (stream instanceof Stream && ((Stream) stream).isActive())
> {
>                  continue;
>              }
>
> -            final Integer streamIdentifier = entry.getKey();
>              if (stream.isClosedFinal()) {
>                  // This stream went from IDLE to CLOSED and is likely to
> have
>                  // been created by the client as part of the priority
> tree.
> -                candidatesStepThree.add(streamIdentifier);
> +                // Candidate for steo 3.
> +                candidatesStepThree.add(stream.getIdentifier());
>              } else if (stream.getChildStreams().size() == 0) {
> -                // Closed, no children
> -                candidatesStepOne.add(streamIdentifier);
> -            } else {
> -                // Closed, with children
> -                candidatesStepTwo.add(streamIdentifier);
> -            }
> -        }
> -
> -        // Process the step one list
> -        for (Integer streamIdToRemove : candidatesStepOne) {
> -            // Remove this childless stream
> -            AbstractNonZeroStream removedStream =
> streams.remove(streamIdToRemove);
> -            removedStream.detachFromParent();
> -            toClose--;
> -            if (log.isDebugEnabled()) {
> -                log.debug(sm.getString("upgradeHandler.pruned",
> connectionId, streamIdToRemove));
> -            }
> -
> -            // Did this make the parent childless?
> -            AbstractStream parent = removedStream.getParentStream();
> -            while (parent instanceof Stream && !((Stream)
> parent).isActive() &&
> -                    !((Stream) parent).isClosedFinal() &&
> parent.getChildStreams().size() == 0) {
> -                streams.remove(parent.getIdentifier());
> -                parent.detachFromParent();
> -                toClose--;
> +                // Prune it
> +                AbstractStream parent = stream.getParentStream();
> +                streams.remove(stream.getIdentifier());
> +                stream.detachFromParent();
>                  if (log.isDebugEnabled()) {
> -                    log.debug(sm.getString("upgradeHandler.pruned",
> connectionId, streamIdToRemove));
> +                    log.debug(sm.getString("upgradeHandler.pruned",
> connectionId, stream.getIdAsString()));
> +                }
> +                if (--toClose < 1) {
> +                    return;
>                  }
> -                // Also need to remove this stream from the p2 list
> -                candidatesStepTwo.remove(parent.getIdentifier());
> -                parent = parent.getParentStream();
> +
> +                // If removing this child made the parent childless then
> see if
> +                // the parent can be removed.
> +                // Don't try and remove Stream 0 as that is the connection
> +                // Don't try and remove 'newer' streams. We'll get to
> them as we
> +                // work through the ordered list of streams.
> +                while (toClose > 0 && parent.getIdAsInt() > 0 &&
> parent.getIdAsInt() < stream.getIdAsInt() &&
> +                        parent.getChildStreams().isEmpty()) {
> +                    // This case is safe since we know parent ID > 0
> therefore
> +                    // this isn't the connection
> +                    stream = (AbstractNonZeroStream) parent;
> +                    parent = stream.getParentStream();
> +                    streams.remove(stream.getIdentifier());
> +                    stream.detachFromParent();
> +                    if (log.isDebugEnabled()) {
> +                        log.debug(sm.getString("upgradeHandler.pruned",
> connectionId, stream.getIdAsString()));
> +                    }
> +                    if (--toClose < 1) {
> +                        return;
> +                    }
> +                    // Also need to remove this stream from the step 2
> list
> +                    candidatesStepTwo.remove(stream.getIdentifier());
> +                }
> +            } else {
> +                // Closed, with children. Candidate for step 2.
> +                candidatesStepTwo.add(stream.getIdentifier());
>              }
>          }
>
>          // Process the P2 list
>          for (Integer streamIdToRemove : candidatesStepTwo) {
>              removeStreamFromPriorityTree(streamIdToRemove);
> -            toClose--;
>              if (log.isDebugEnabled()) {
>                  log.debug(sm.getString("upgradeHandler.pruned",
> connectionId, streamIdToRemove));
>              }
> +            if (--toClose < 1) {
> +                return;
> +            }
>          }
>
>          while (toClose > 0 && candidatesStepThree.size() > 0) {
>              Integer streamIdToRemove = candidatesStepThree.pollLast();
>              removeStreamFromPriorityTree(streamIdToRemove);
> -            toClose--;
>              if (log.isDebugEnabled()) {
>                  log.debug(sm.getString("upgradeHandler.prunedPriority",
> connectionId, streamIdToRemove));
>              }
> +            if (--toClose < 1) {
> +                return;
> +            }
>          }
>
>          if (toClose > 0) {
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
>
>

Reply via email to