This is an automated email from the ASF dual-hosted git repository. dkuppitz pushed a commit to branch TINKERPOP-2126 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 62622c28696531206cf57ff9630dd7f9b6e0d3ce Author: Daniel Kuppitz <daniel_kupp...@hotmail.com> AuthorDate: Tue Jan 22 15:07:09 2019 -0700 TINKERPOP-2126 Fixed concurrency issue in `ObjectWritable::toString()`. --- CHANGELOG.asciidoc | 2 +- docker/Dockerfile | 2 +- .../hadoop/structure/io/ObjectWritable.java | 33 ++++++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index e0e4620..a8b10f0 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,7 +30,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Added `globalFunctionCacheEnabled` to the `GroovyCompilerGremlinPlugin` to allow that cache to be disabled. * Added `globalFunctionCacheEnabled` override to `SessionOpProcessor` configuration. * Added status code to `GremlinServerError` so that it would be more directly accessible during failures. -* Fixed a concurrency issue in `TraverserSet`. +* Fixed concurrency issues in `TraverserSet.toString()` and `ObjectWritable.toString()`. * Fixed a bug in `InlineFilterStrategy` that mixed up and's and or's when folding merging conditions together. [[release-3-3-5]] diff --git a/docker/Dockerfile b/docker/Dockerfile index 85c9865..a908b22 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,7 +20,7 @@ FROM ubuntu:trusty MAINTAINER Daniel Kuppitz <m...@gremlin.guru> RUN apt-get update \ - && apt-get -y install software-properties-common python-software-properties apt-transport-https curl \ + && apt-get -y install software-properties-common python-software-properties apt-transport-https curl dpkg \ && echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections \ && add-apt-repository -y ppa:webupd8team/java \ && sh -c 'curl -s https://packages.microsoft.com/config/ubuntu/14.04/packages-microsoft-prod.deb -o packages-microsoft-prod.deb' \ diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/io/ObjectWritable.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/io/ObjectWritable.java index 0379ee6..979cbfc 100644 --- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/io/ObjectWritable.java +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/io/ObjectWritable.java @@ -22,6 +22,8 @@ import org.apache.hadoop.io.WritableComparable; import org.apache.hadoop.io.WritableUtils; import org.apache.tinkerpop.gremlin.process.computer.MapReduce; import org.apache.tinkerpop.gremlin.structure.io.gryo.kryoshim.KryoShimServiceLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.DataInput; @@ -30,13 +32,16 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.ConcurrentModificationException; +import java.util.Objects; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ public final class ObjectWritable<T> implements WritableComparable<ObjectWritable>, Serializable { - private static final String NULL = "null"; + private static final Logger logger = LoggerFactory.getLogger(ObjectWritable.class); + private static final ObjectWritable<MapReduce.NullObject> NULL_OBJECT_WRITABLE = new ObjectWritable<>(MapReduce.NullObject.instance()); T t; @@ -45,7 +50,7 @@ public final class ObjectWritable<T> implements WritableComparable<ObjectWritabl } public ObjectWritable(final T t) { - this.t = t; + this.set(t); } public T get() { @@ -58,7 +63,29 @@ public final class ObjectWritable<T> implements WritableComparable<ObjectWritabl @Override public String toString() { - return null == this.t ? NULL : this.t.toString(); + // Spark's background logging apparently tries to log a `toString()` of certain objects while they're being + // modified, which then throws a ConcurrentModificationException. We probably can't make any arbitrary object + // thread-safe, but we can easily retry on such cases and eventually we should always get a result. + final int maxAttempts = 5; + for (int i = maxAttempts; ;) { + try { + return Objects.toString(this.t); + } + catch (ConcurrentModificationException cme) { + if (--i > 0) { + logger.warn(String.format("Failed to toString() object held by ObjectWritable, retrying %d more %s.", + i, i == 1 ? "time" : "times"), cme); + } else break; + if (i < maxAttempts - 1) { + try { + Thread.sleep((maxAttempts - i - 1) * 100); + } catch (InterruptedException ignored) { + break; + } + } + } + } + return this.t.getClass().toString(); } @Override