TINKERPOP-1676 Improved speed and memory usage of GraphSON This change is specific to TinkerGraph and the serialization of vertices/edges/properties. Removed a "middle layer" of JSON to Object serialization which was coercing JSON to Map first and then converting that to the graph elements. Also made deserializers cacheable.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/251f5b7e Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/251f5b7e Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/251f5b7e Branch: refs/heads/TINKERPOP-1681 Commit: 251f5b7e34e8ebd9a8bc36802e633be8c91eeb5e Parents: 46e6e97 Author: Stephen Mallette <sp...@genoprime.com> Authored: Tue May 23 16:57:12 2017 -0400 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Thu May 25 14:52:30 2017 -0400 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 1 + .../io/graphson/AbstractObjectDeserializer.java | 5 + .../io/graphson/GraphSONSerializersV2d0.java | 164 +++++++++++++++---- .../structure/TinkerIoRegistryV2d0.java | 5 + 4 files changed, 145 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251f5b7e/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 77d612e..f0d7b17 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima TinkerPop 3.2.5 (Release Date: NOT OFFICIALLY RELEASED YET) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Improved performance and memory usage of GraphSON when serializing `TinkerGraph` and graph elements. * Removed use of `stream()` in `DetachedEdge` and `DetachedVertex`. * Deprecated a constructor in `DetachedEdge` that made use of `Pair` in favor of a new one that just uses the objects that were in the `Pair`. * Improved error messaging on the `g.addV(Object...)` when passing an invalid arguments. http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251f5b7e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractObjectDeserializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractObjectDeserializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractObjectDeserializer.java index bd7f4b6..9e4e102 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractObjectDeserializer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractObjectDeserializer.java @@ -47,5 +47,10 @@ public abstract class AbstractObjectDeserializer<T> extends StdDeserializer<T> { return createObject(mapData); } + @Override + public boolean isCachable() { + return true; + } + public abstract T createObject(final Map<String, Object> data); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251f5b7e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java index 9a27279..0008d3a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java @@ -43,7 +43,9 @@ import org.apache.tinkerpop.shaded.jackson.core.JsonGenerationException; import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; import org.apache.tinkerpop.shaded.jackson.core.JsonParser; import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.core.JsonToken; import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; @@ -416,52 +418,117 @@ class GraphSONSerializersV2d0 { //////////////////////////// DESERIALIZERS /////////////////////////// - - static class VertexJacksonDeserializer extends AbstractObjectDeserializer<Vertex> { + static class VertexJacksonDeserializer extends StdDeserializer<Vertex> { public VertexJacksonDeserializer() { super(Vertex.class); } + public Vertex deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final JavaType propertiesType = deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class, String.class, Object.class); + + Object id = null; + String label = null; + Map<String, Object> properties = null; + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) { + jsonParser.nextToken(); + id = deserializationContext.readValue(jsonParser, Object.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) { + jsonParser.nextToken(); + label = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) { + jsonParser.nextToken(); + properties = deserializationContext.readValue(jsonParser, propertiesType); + } + } + + return new DetachedVertex(id, label, properties); + } + @Override - public Vertex createObject(final Map<String, Object> vertexData) { - return new DetachedVertex( - vertexData.get(GraphSONTokens.ID), - vertexData.get(GraphSONTokens.LABEL).toString(), - (Map<String, Object>) vertexData.get(GraphSONTokens.PROPERTIES) - ); + public boolean isCachable() { + return true; } } - static class EdgeJacksonDeserializer extends AbstractObjectDeserializer<Edge> { + static class EdgeJacksonDeserializer extends StdDeserializer<Edge> { public EdgeJacksonDeserializer() { super(Edge.class); } @Override - public Edge createObject(final Map<String, Object> edgeData) { - return new DetachedEdge( - edgeData.get(GraphSONTokens.ID), - edgeData.get(GraphSONTokens.LABEL).toString(), - (Map) edgeData.get(GraphSONTokens.PROPERTIES), - Pair.with(edgeData.get(GraphSONTokens.OUT), edgeData.get(GraphSONTokens.OUT_LABEL).toString()), - Pair.with(edgeData.get(GraphSONTokens.IN), edgeData.get(GraphSONTokens.IN_LABEL).toString()) - ); + public Edge deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final JavaType propertiesType = deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class, String.class, Object.class); + + Object id = null; + String label = null; + Object outVId = null; + String outVLabel = null; + Object inVId = null; + String inVLabel = null; + Map<String, Object> properties = null; + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) { + jsonParser.nextToken(); + id = deserializationContext.readValue(jsonParser, Object.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) { + jsonParser.nextToken(); + label = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.OUT)) { + jsonParser.nextToken(); + outVId = deserializationContext.readValue(jsonParser, Object.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.OUT_LABEL)) { + jsonParser.nextToken(); + outVLabel = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.IN)) { + jsonParser.nextToken(); + inVId = deserializationContext.readValue(jsonParser, Object.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.IN_LABEL)) { + jsonParser.nextToken(); + inVLabel = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) { + jsonParser.nextToken(); + properties = deserializationContext.readValue(jsonParser, propertiesType); + } + } + + return new DetachedEdge(id, label, properties, outVId, outVLabel, inVId, inVLabel); + } + + @Override + public boolean isCachable() { + return true; } } - static class PropertyJacksonDeserializer extends AbstractObjectDeserializer<Property> { + static class PropertyJacksonDeserializer extends StdDeserializer<Property> { public PropertyJacksonDeserializer() { super(Property.class); } @Override - public Property createObject(final Map<String, Object> propData) { - return new DetachedProperty( - (String) propData.get(GraphSONTokens.KEY), - propData.get(GraphSONTokens.VALUE)); + public Property deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + String key = null; + Object value = null; + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.KEY)) { + jsonParser.nextToken(); + key = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) { + jsonParser.nextToken(); + value = deserializationContext.readValue(jsonParser, Object.class); + } + } + + return new DetachedProperty<>(key, value); + } + + @Override + public boolean isCachable() { + return true; } } @@ -485,20 +552,42 @@ class GraphSONSerializersV2d0 { } } - static class VertexPropertyJacksonDeserializer extends AbstractObjectDeserializer<VertexProperty> { + static class VertexPropertyJacksonDeserializer extends StdDeserializer<VertexProperty> { protected VertexPropertyJacksonDeserializer() { super(VertexProperty.class); } @Override - public VertexProperty createObject(final Map<String, Object> propData) { - return new DetachedVertexProperty( - propData.get(GraphSONTokens.ID), - (String) propData.get(GraphSONTokens.LABEL), - propData.get(GraphSONTokens.VALUE), - (Map) propData.get(GraphSONTokens.PROPERTIES) - ); + public VertexProperty deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final JavaType propertiesType = deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class, String.class, Object.class); + + Object id = null; + String label = null; + Object value = null; + Map<String, Object> properties = null; + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) { + jsonParser.nextToken(); + id = deserializationContext.readValue(jsonParser, Object.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) { + jsonParser.nextToken(); + label = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) { + jsonParser.nextToken(); + value = deserializationContext.readValue(jsonParser, Object.class); + }else if (jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) { + jsonParser.nextToken(); + properties = deserializationContext.readValue(jsonParser, propertiesType); + } + } + + return new DetachedVertexProperty<>(id, label, value, properties); + } + + @Override + public boolean isCachable() { + return true; } } @@ -555,6 +644,11 @@ class GraphSONSerializersV2d0 { } return t; } + + @Override + public boolean isCachable() { + return true; + } } static class IntegerJackonsDeserializer extends StdDeserializer<Integer> { @@ -567,6 +661,11 @@ class GraphSONSerializersV2d0 { public Integer deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { return jsonParser.getIntValue(); } + + @Override + public boolean isCachable() { + return true; + } } static class DoubleJackonsDeserializer extends StdDeserializer<Double> { @@ -579,6 +678,11 @@ class GraphSONSerializersV2d0 { public Double deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { return jsonParser.getDoubleValue(); } + + @Override + public boolean isCachable() { + return true; + } } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251f5b7e/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java ---------------------------------------------------------------------- diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java index 394216b..280d1af 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java @@ -218,5 +218,10 @@ public final class TinkerIoRegistryV2d0 extends AbstractIoRegistry { return graph; } + + @Override + public boolean isCachable() { + return true; + } } }