Fixed a leak in a Netty ReferenceCounted resource in Gremlin Server. This is essentially a backport from 3.2.2 on master of 9913d64cd1f9050b3d047b59dcea34ee8507d2dd which fixes a problem that happens if a Frame contained a Netty Bytebuf which is ReferenceCounted and that Frame did not write downstream because of an exception (perhaps during a transactional commit seemed like the likely place that would happen) then the Bytebuf would not get cleaned up properly and Netty would issue warnings. TINKERPOP-1375 CTR
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/6be28270 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/6be28270 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/6be28270 Branch: refs/heads/TINKERPOP-1360 Commit: 6be28270598862a7a92b88a67d18e5e8f198b92b Parents: 90e5af1 Author: Stephen Mallette <sp...@genoprime.com> Authored: Mon Jul 25 12:15:30 2016 -0400 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Mon Jul 25 12:18:05 2016 -0400 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 6 +++ .../tinkerpop/gremlin/server/handler/Frame.java | 11 +++++ .../server/op/AbstractEvalOpProcessor.java | 43 ++++++++++++-------- 3 files changed, 44 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6be28270/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index bff4996..8735a87 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -22,6 +22,12 @@ TinkerPop 3.1.0 (A 187 On The Undercover Gremlinz) image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/images/gremlin-gangster.png[width=185] +[[release-3-1-4]] +TinkerPop 3.1.4 (NOT OFFICIALLY RELEASED YET) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Fixed a potential leak of a `ReferenceCounted` resource in Gremlin Server. + [[release-3-1-3]] TinkerPop 3.1.3 (Release Date: July 18, 2016) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6be28270/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/Frame.java ---------------------------------------------------------------------- diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/Frame.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/Frame.java index e6a616f..76d772e 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/Frame.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/Frame.java @@ -18,6 +18,8 @@ */ package org.apache.tinkerpop.gremlin.server.handler; +import io.netty.util.ReferenceCounted; + /** * A holder for a {@code String} or {@code ByteBuf} that represents a message to be written back to the requesting * client. @@ -34,4 +36,13 @@ public class Frame { public Object getMsg() { return msg; } + + /** + * If the object contained in the frame is {@code ReferenceCounted} then it may need to be released or else + * Netty will generate warnings that counted resources are leaking. + */ + public void tryRelease() { + if (msg instanceof ReferenceCounted) + ((ReferenceCounted) msg).release(); + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6be28270/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java ---------------------------------------------------------------------- diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java index 8f18ade..20ba1ff 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java @@ -379,31 +379,42 @@ public abstract class AbstractEvalOpProcessor implements OpProcessor { // serialize here because in sessionless requests the serialization must occur in the same // thread as the eval. as eval occurs in the GremlinExecutor there's no way to get back to the // thread that processed the eval of the script so, we have to push serialization down into that - Frame frame; + Frame frame = null; try { frame = makeFrame(ctx, msg, serializer, useBinary, aggregate, code); } catch (Exception ex) { + // a frame may use a Bytebuf which is a countable release - if it does not get written + // downstream it needs to be released here + if (frame != null) frame.tryRelease(); + // exception is handled in makeFrame() - serialization error gets written back to driver // at that point if (manageTransactions) attemptRollback(msg, context.getGraphManager(), settings.strictTransactionManagement); break; } - // only need to reset the aggregation list if there's more stuff to write - if (itty.hasNext()) - aggregate = new ArrayList<>(resultIterationBatchSize); - else { - // iteration and serialization are both complete which means this finished successfully. note that - // errors internal to script eval or timeout will rollback given GremlinServer's global configurations. - // local errors will get rolledback below because the exceptions aren't thrown in those cases to be - // caught by the GremlinExecutor for global rollback logic. this only needs to be committed if - // there are no more items to iterate and serialization is complete - if (managedTransactionsForRequest) attemptCommit(msg, context.getGraphManager(), settings.strictTransactionManagement); - - // exit the result iteration loop as there are no more results left. using this external control - // because of the above commit. some graphs may open a new transaction on the call to - // hasNext() - hasMore = false; + try { + // only need to reset the aggregation list if there's more stuff to write + if (itty.hasNext()) + aggregate = new ArrayList<>(resultIterationBatchSize); + else { + // iteration and serialization are both complete which means this finished successfully. note that + // errors internal to script eval or timeout will rollback given GremlinServer's global configurations. + // local errors will get rolledback below because the exceptions aren't thrown in those cases to be + // caught by the GremlinExecutor for global rollback logic. this only needs to be committed if + // there are no more items to iterate and serialization is complete + if (managedTransactionsForRequest) attemptCommit(msg, context.getGraphManager(), settings.strictTransactionManagement); + + // exit the result iteration loop as there are no more results left. using this external control + // because of the above commit. some graphs may open a new transaction on the call to + // hasNext() + hasMore = false; + } + } catch (Exception ex) { + // a frame may use a Bytebuf which is a countable release - if it does not get written + // downstream it needs to be released here + if (frame != null) frame.tryRelease(); + throw ex; } // the flush is called after the commit has potentially occurred. in this way, if a commit was