This is an automated email from the ASF dual-hosted git repository. jorgebg pushed a commit to branch TINKERPOP-2154 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit c66c497880c0dae6df5e5450bee6a2376f10a149 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> AuthorDate: Fri Feb 8 12:31:53 2019 +0100 TINKERPOP-2154 GraphBinary: Release working buffers on failure --- .../driver/ser/binary/GraphBinaryWriter.java | 21 +++-- .../driver/ser/binary/types/BindingSerializer.java | 9 +- .../driver/ser/binary/types/BulkSetSerializer.java | 12 ++- .../ser/binary/types/ByteCodeSerializer.java | 25 ++++-- .../ser/binary/types/CollectionSerializer.java | 10 ++- .../ser/binary/types/CustomTypeSerializer.java | 3 - .../driver/ser/binary/types/EdgeSerializer.java | 33 +++++--- .../driver/ser/binary/types/GraphSerializer.java | 79 ++++++++++++------ .../driver/ser/binary/types/LambdaSerializer.java | 14 +++- .../driver/ser/binary/types/MapSerializer.java | 14 ++-- .../driver/ser/binary/types/MetricsSerializer.java | 27 ++++-- .../driver/ser/binary/types/PathSerializer.java | 11 ++- .../binary/types/TraversalMetricsSerializer.java | 16 +++- .../binary/types/TraversalStrategySerializer.java | 11 ++- .../ser/binary/types/TraverserSerializer.java | 11 ++- .../driver/ser/binary/types/TreeSerializer.java | 16 ++-- .../ser/binary/types/VertexPropertySerializer.java | 23 +++-- .../driver/ser/binary/types/VertexSerializer.java | 19 +++-- .../ser/binary/TypeSerializerFailureTests.java | 97 ++++++++++++++++++++++ 19 files changed, 341 insertions(+), 110 deletions(-) diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java index b3c2c0f..1703658 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java @@ -71,14 +71,16 @@ public class GraphBinaryWriter { final TypeSerializer<T> serializer = (TypeSerializer<T>) registry.getSerializer(objectClass); if (serializer instanceof CustomTypeSerializer) { + // It's a custom type CustomTypeSerializer customTypeSerializer = (CustomTypeSerializer) serializer; - // Is a custom type - // Write type code, custom type name and let the serializer write - // the rest of {custom_type_info} followed by {value_flag} and {value} - return allocator.compositeBuffer(3).addComponents(true, - Unpooled.wrappedBuffer(customTypeCodeBytes), - writeValue(customTypeSerializer.getTypeName(), allocator, false), - customTypeSerializer.write(value, allocator, this)); + + // Try to serialize the custom value before allocating a composite buffer + ByteBuf customTypeValueBuffer = customTypeSerializer.write(value, allocator, this); + + return allocator.compositeBuffer(3) + .addComponent(true, Unpooled.wrappedBuffer(customTypeCodeBytes)) + .addComponent(true, writeValue(customTypeSerializer.getTypeName(), allocator, false)) + .addComponent(true, customTypeValueBuffer); } if (serializer instanceof TransformSerializer) { @@ -88,11 +90,14 @@ public class GraphBinaryWriter { return write(transformSerializer.transform(value), allocator); } + // Try to serialize the value before creating a new composite buffer + ByteBuf typeInfoAndValueBuffer = serializer.write(value, allocator, this); + return allocator.compositeBuffer(2).addComponents(true, // {type_code} Unpooled.wrappedBuffer(serializer.getDataType().getDataTypeBuffer()), // {type_info}{value_flag}{value} - serializer.write(value, allocator, this)); + typeInfoAndValueBuffer); } /** diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BindingSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BindingSerializer.java index de73fb2..3d9f61a 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BindingSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BindingSerializer.java @@ -45,8 +45,13 @@ public class BindingSerializer extends SimpleTypeSerializer<Bytecode.Binding> { @Override protected ByteBuf writeValue(final Bytecode.Binding value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(2); - result.addComponent(true, context.writeValue(value.variable(), allocator, false)); - result.addComponent(true, context.write(value.value(), allocator)); + try { + result.addComponent(true, context.writeValue(value.variable(), allocator, false)); + result.addComponent(true, context.write(value.value(), allocator)); + } catch (Exception ex) { + result.release(); + throw ex; + } return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BulkSetSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BulkSetSerializer.java index 99880de..04f3d63 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BulkSetSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/BulkSetSerializer.java @@ -55,9 +55,15 @@ public class BulkSetSerializer extends SimpleTypeSerializer<BulkSet> { final CompositeByteBuf result = allocator.compositeBuffer(1 + raw.size() * 2); result.addComponent(true, allocator.buffer(4).writeInt(raw.size())); - for (Object key : raw.keySet()) { - result.addComponents(true, context.write(key, allocator), - allocator.buffer(8).writeLong(value.get(key))); + try { + for (Object key : raw.keySet()) { + result.addComponents(true, context.write(key, allocator), + allocator.buffer(8).writeLong(value.get(key))); + } + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; } return result; diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ByteCodeSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ByteCodeSerializer.java index 8fe9a4a..3b39eb9 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ByteCodeSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ByteCodeSerializer.java @@ -67,8 +67,14 @@ public class ByteCodeSerializer extends SimpleTypeSerializer<Bytecode> { // 2 buffers for the length + plus 2 buffers per each step and source final CompositeByteBuf result = allocator.compositeBuffer(2 + steps.size() * 2 + sources.size() * 2); - writeInstructions(allocator, context, steps, result); - writeInstructions(allocator, context, sources, result); + try { + writeInstructions(allocator, context, steps, result); + writeInstructions(allocator, context, sources, result); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } return result; } @@ -79,10 +85,8 @@ public class ByteCodeSerializer extends SimpleTypeSerializer<Bytecode> { result.addComponent(true, context.writeValue(instructions.size(), allocator, false)); for (Bytecode.Instruction instruction : instructions) { - result.addComponents( - true, - context.writeValue(instruction.getOperator(), allocator, false), - getArgumentsBuffer(instruction.getArguments(), allocator, context)); + result.addComponent(true, context.writeValue(instruction.getOperator(), allocator, false)); + result.addComponent(true, getArgumentsBuffer(instruction.getArguments(), allocator, context)); } } @@ -90,8 +94,13 @@ public class ByteCodeSerializer extends SimpleTypeSerializer<Bytecode> { final CompositeByteBuf result = allocator.compositeBuffer(1 + arguments.length); result.addComponent(true, context.writeValue(arguments.length, allocator, false)); - for (Object value : arguments) { - result.addComponent(true, context.write(value, allocator)); + try { + for (Object value : arguments) { + result.addComponent(true, context.write(value, allocator)); + } + } catch (Exception ex) { + result.release(); + throw ex; } return result; diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CollectionSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CollectionSerializer.java index 1b09a5f..858483f 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CollectionSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CollectionSerializer.java @@ -51,8 +51,14 @@ class CollectionSerializer extends SimpleTypeSerializer<Collection> { final CompositeByteBuf result = allocator.compositeBuffer(1 + value.size()); result.addComponent(true, allocator.buffer(4).writeInt(value.size())); - for (Object item : value) { - result.addComponent(true, context.write(item, allocator)); + try { + for (Object item : value) { + result.addComponent(true, context.write(item, allocator)); + } + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; } return result; diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CustomTypeSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CustomTypeSerializer.java index 4d25e7c..d718bce 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CustomTypeSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/CustomTypeSerializer.java @@ -22,9 +22,6 @@ import org.apache.tinkerpop.gremlin.driver.ser.binary.TypeSerializer; /** * Represents a serializer for a custom (provider specific) serializer. - * <p> - * Note that invocations to - * </p> * @param <T> */ public interface CustomTypeSerializer<T> extends TypeSerializer<T> { diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/EdgeSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/EdgeSerializer.java index c9e8549..3c9f950 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/EdgeSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/EdgeSerializer.java @@ -61,20 +61,27 @@ public class EdgeSerializer extends SimpleTypeSerializer<Edge> { @Override protected ByteBuf writeValue(final Edge value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(8); - result.addComponent(true, context.write(value.id(), allocator)); - result.addComponent(true, context.writeValue(value.label(), allocator, false)); - result.addComponent(true, context.write(value.inVertex().id(), allocator)); - result.addComponent(true, context.writeValue(value.inVertex().label(), allocator, false)); - result.addComponent(true, context.write(value.outVertex().id(), allocator)); - result.addComponent(true, context.writeValue(value.outVertex().label(), allocator, false)); - - // we don't serialize the parent Vertex for edges. they are "references", but we leave a place holder - // here as an option for the future as we've waffled this soooooooooo many times now - result.addComponent(true, context.write(null, allocator)); - // we don't serialize properties for graph vertices/edges. they are "references", but we leave a place holder - // here as an option for the future as we've waffled this soooooooooo many times now - result.addComponent(true, context.write(null, allocator)); + try { + result.addComponent(true, context.write(value.id(), allocator)); + result.addComponent(true, context.writeValue(value.label(), allocator, false)); + + result.addComponent(true, context.write(value.inVertex().id(), allocator)); + result.addComponent(true, context.writeValue(value.inVertex().label(), allocator, false)); + result.addComponent(true, context.write(value.outVertex().id(), allocator)); + result.addComponent(true, context.writeValue(value.outVertex().label(), allocator, false)); + + // we don't serialize the parent Vertex for edges. they are "references", but we leave a place holder + // here as an option for the future as we've waffled this soooooooooo many times now + result.addComponent(true, context.write(null, allocator)); + // we don't serialize properties for graph vertices/edges. they are "references", but we leave a place holder + // here as an option for the future as we've waffled this soooooooooo many times now + result.addComponent(true, context.write(null, allocator)); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } return result; } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/GraphSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/GraphSerializer.java index 8db6e70..0f228c1 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/GraphSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/GraphSerializer.java @@ -117,44 +117,68 @@ public class GraphSerializer extends SimpleTypeSerializer<Graph> { final List<Edge> edgeList = IteratorUtils.list(value.edges()); final CompositeByteBuf result = allocator.compositeBuffer(2 + edgeList.size() + vertexList.size()); - result.addComponent(true, context.writeValue(vertexList.size(), allocator, false)); - for (Vertex v : vertexList) { - final List<VertexProperty<Object>> vertexProperties = IteratorUtils.list(v.properties()); - final CompositeByteBuf vbb = allocator.compositeBuffer(3 + vertexProperties.size()); - vbb.addComponent(true, context.write(v.id(), allocator)); - vbb.addComponent(true, context.writeValue(v.label(), allocator, false)); + try { + result.addComponent(true, context.writeValue(vertexList.size(), allocator, false)); + + for (Vertex v : vertexList) { + result.addComponent(true, writeVertex(allocator, context, v)); + } + + result.addComponent(true, context.writeValue(edgeList.size(), allocator, false)); + + for (Edge e : edgeList) { + result.addComponent(true, writeEdge(allocator, context, e)); + } + + } catch (Exception ex) { + result.release(); + throw ex; + } + + return result; + } + + private ByteBuf writeVertex(ByteBufAllocator allocator, GraphBinaryWriter context, Vertex vertex) throws SerializationException { + final List<VertexProperty<Object>> vertexProperties = IteratorUtils.list(vertex.properties()); + final CompositeByteBuf vbb = allocator.compositeBuffer(3 + vertexProperties.size() * 5); + + try { + vbb.addComponent(true, context.write(vertex.id(), allocator)); + vbb.addComponent(true, context.writeValue(vertex.label(), allocator, false)); vbb.addComponent(true, context.writeValue(vertexProperties.size(), allocator, false)); for (VertexProperty<Object> vp : vertexProperties) { - final CompositeByteBuf vpbb = allocator.compositeBuffer(5); - vpbb.addComponent(true, context.write(vp.id(), allocator)); - vpbb.addComponent(true, context.writeValue(vp.label(), allocator, false)); - vpbb.addComponent(true, context.write(vp.value(), allocator)); + vbb.addComponent(true, context.write(vp.id(), allocator)); + vbb.addComponent(true, context.writeValue(vp.label(), allocator, false)); + vbb.addComponent(true, context.write(vp.value(), allocator)); // maintain the VertexProperty format we have with this empty parent......... - vpbb.addComponent(true, context.write(null, allocator)); + vbb.addComponent(true, context.write(null, allocator)); // write those properties out using the standard Property serializer - vpbb.addComponent(true, context.writeValue(IteratorUtils.list(vp.properties()), allocator, false)); - - vbb.addComponent(true, vpbb); + vbb.addComponent(true, context.writeValue(IteratorUtils.list(vp.properties()), allocator, false)); } - - result.addComponent(true, vbb); + } catch (Exception ex) { + vbb.release(); + throw ex; } - result.addComponent(true, context.writeValue(edgeList.size(), allocator, false)); - for (Edge e : edgeList) { - final CompositeByteBuf ebb = allocator.compositeBuffer(8); - ebb.addComponent(true, context.write(e.id(), allocator)); - ebb.addComponent(true, context.writeValue(e.label(), allocator, false)); + return vbb; + } + + private ByteBuf writeEdge(ByteBufAllocator allocator, GraphBinaryWriter context, Edge edge) throws SerializationException { + final CompositeByteBuf ebb = allocator.compositeBuffer(8); + + try { + ebb.addComponent(true, context.write(edge.id(), allocator)); + ebb.addComponent(true, context.writeValue(edge.label(), allocator, false)); - ebb.addComponent(true, context.write(e.inVertex().id(), allocator)); + ebb.addComponent(true, context.write(edge.inVertex().id(), allocator)); // vertex labels aren't needed but maintaining the Edge form that we have ebb.addComponent(true, context.write(null, allocator)); - ebb.addComponent(true, context.write(e.outVertex().id(), allocator)); + ebb.addComponent(true, context.write(edge.outVertex().id(), allocator)); // vertex labels aren't needed but maintaining the Edge form that we have ebb.addComponent(true, context.write(null, allocator)); @@ -163,12 +187,13 @@ public class GraphSerializer extends SimpleTypeSerializer<Graph> { ebb.addComponent(true, context.write(null, allocator)); // write those properties out using the standard Property serializer - ebb.addComponent(true, context.writeValue(IteratorUtils.list(e.properties()), allocator, false)); - - result.addComponent(true, ebb); + ebb.addComponent(true, context.writeValue(IteratorUtils.list(edge.properties()), allocator, false)); + } catch (Exception ex) { + ebb.release(); + throw ex; } - return result; + return ebb; } private static Map<String, List<VertexProperty>> indexedVertexProperties(final Vertex v) { diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/LambdaSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/LambdaSerializer.java index c7cd0d5..545d018 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/LambdaSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/LambdaSerializer.java @@ -54,9 +54,17 @@ public class LambdaSerializer extends SimpleTypeSerializer<Lambda> { @Override protected ByteBuf writeValue(final Lambda value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(3); - result.addComponent(true, context.writeValue(value.getLambdaLanguage(), allocator, false)); - result.addComponent(true, context.writeValue(value.getLambdaScript(), allocator, false)); - result.addComponent(true, context.writeValue(value.getLambdaArguments(), allocator, false)); + + try { + result.addComponent(true, context.writeValue(value.getLambdaLanguage(), allocator, false)); + result.addComponent(true, context.writeValue(value.getLambdaScript(), allocator, false)); + result.addComponent(true, context.writeValue(value.getLambdaArguments(), allocator, false)); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } + return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java index b03e414..ad76a74 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java @@ -51,11 +51,15 @@ public class MapSerializer extends SimpleTypeSerializer<Map> { final CompositeByteBuf result = allocator.compositeBuffer(1 + value.size() * 2); result.addComponent(true, allocator.buffer(4).writeInt(value.size())); - for (Object key : value.keySet()) { - result.addComponents( - true, - context.write(key, allocator), - context.write(value.get(key), allocator)); + try { + for (Object key : value.keySet()) { + result.addComponent(true, context.write(key, allocator)); + result.addComponent(true, context.write(value.get(key), allocator)); + } + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; } return result; diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MetricsSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MetricsSerializer.java index dc65183..4ce12a0 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MetricsSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MetricsSerializer.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.driver.ser.binary.types; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; import org.apache.tinkerpop.gremlin.driver.ser.SerializationException; import org.apache.tinkerpop.gremlin.driver.ser.binary.DataType; import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryReader; @@ -56,13 +57,23 @@ public class MetricsSerializer extends SimpleTypeSerializer<Metrics> { @Override protected ByteBuf writeValue(final Metrics value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { - return allocator.compositeBuffer(6).addComponents(true, - context.writeValue(value.getId(), allocator, false), - context.writeValue(value.getName(), allocator, false), - context.writeValue(value.getDuration(TimeUnit.NANOSECONDS), allocator, false), - context.writeValue(value.getCounts(), allocator, false), - context.writeValue(value.getAnnotations(), allocator, false), - // Avoid changing type to List - collectionSerializer.writeValue(value.getNested(), allocator, context)); + final CompositeByteBuf result = allocator.compositeBuffer(6); + + try { + result.addComponent(true, context.writeValue(value.getId(), allocator, false)); + result.addComponent(true, context.writeValue(value.getName(), allocator, false)); + result.addComponent(true, context.writeValue(value.getDuration(TimeUnit.NANOSECONDS), allocator, false)); + result.addComponent(true, context.writeValue(value.getCounts(), allocator, false)); + result.addComponent(true, context.writeValue(value.getAnnotations(), allocator, false)); + + // Avoid changing type to List + result.addComponent(true, collectionSerializer.writeValue(value.getNested(), allocator, context)); + } catch (Exception ex) { + // We should release the CompositeByteBuf as it's not going to be yielded for a reader + result.release(); + throw ex; + } + + return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/PathSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/PathSerializer.java index 4c640d7..344d9ca 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/PathSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/PathSerializer.java @@ -61,8 +61,15 @@ public class PathSerializer extends SimpleTypeSerializer<Path> { @Override protected ByteBuf writeValue(final Path value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(2); - result.addComponent(true, context.write(value.labels(), allocator)); - result.addComponent(true, context.write(value.objects(), allocator)); + + try { + result.addComponent(true, context.write(value.labels(), allocator)); + result.addComponent(true, context.write(value.objects(), allocator)); + } catch (Exception ex) { + result.release(); + throw ex; + } + return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalMetricsSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalMetricsSerializer.java index f2bba8a..90650ef 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalMetricsSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalMetricsSerializer.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.driver.ser.binary.types; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; import org.apache.tinkerpop.gremlin.driver.ser.SerializationException; import org.apache.tinkerpop.gremlin.driver.ser.binary.DataType; import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryReader; @@ -48,8 +49,17 @@ public class TraversalMetricsSerializer extends SimpleTypeSerializer<TraversalMe @Override protected ByteBuf writeValue(TraversalMetrics value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { - return allocator.compositeBuffer(2).addComponents(true, - context.writeValue(value.getDuration(TimeUnit.NANOSECONDS), allocator, false), - collectionSerializer.writeValue(value.getMetrics(), allocator, context)); + final CompositeByteBuf result = allocator.compositeBuffer(2); + + try { + result.addComponent(true, context.writeValue(value.getDuration(TimeUnit.NANOSECONDS), allocator, false)); + result.addComponent(true, collectionSerializer.writeValue(value.getMetrics(), allocator, context)); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } + + return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalStrategySerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalStrategySerializer.java index 0de52a2..0b63907 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalStrategySerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraversalStrategySerializer.java @@ -55,8 +55,15 @@ public class TraversalStrategySerializer extends SimpleTypeSerializer<TraversalS @Override protected ByteBuf writeValue(final TraversalStrategy value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(2); - result.addComponent(true, context.writeValue(value.getClass(), allocator, false)); - result.addComponent(true, context.writeValue(translateToBytecode(ConfigurationConverter.getMap(value.getConfiguration())), allocator, false)); + + try { + result.addComponent(true, context.writeValue(value.getClass(), allocator, false)); + result.addComponent(true, context.writeValue(translateToBytecode(ConfigurationConverter.getMap(value.getConfiguration())), allocator, false)); + } catch (Exception ex) { + result.release(); + throw ex; + } + return result; } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraverserSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraverserSerializer.java index 8dc026a..c9f4bd7 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraverserSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TraverserSerializer.java @@ -47,8 +47,15 @@ public class TraverserSerializer extends SimpleTypeSerializer<Traverser> { @Override protected ByteBuf writeValue(final Traverser value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(2); - result.addComponent(true, context.writeValue(value.bulk(), allocator, false)); - result.addComponent(true, context.write(value.get(), allocator)); + + try { + result.addComponent(true, context.writeValue(value.bulk(), allocator, false)); + result.addComponent(true, context.write(value.get(), allocator)); + } catch (Exception ex) { + result.release(); + throw ex; + } + return result; } } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TreeSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TreeSerializer.java index 0d769dc..bc7fa73 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TreeSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/TreeSerializer.java @@ -49,11 +49,17 @@ public class TreeSerializer extends SimpleTypeSerializer<Tree> { final CompositeByteBuf result = allocator.compositeBuffer(1 + value.size() * 2); result.addComponent(true, allocator.buffer(4).writeInt(value.size())); - for (Object key : value.keySet()) { - result.addComponents( - true, - context.write(key, allocator), - context.writeValue(value.get(key), allocator, false)); + try { + for (Object key : value.keySet()) { + result.addComponents( + true, + context.write(key, allocator), + context.writeValue(value.get(key), allocator, false)); + } + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; } return result; diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexPropertySerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexPropertySerializer.java index 196ebe6..b1d134c 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexPropertySerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexPropertySerializer.java @@ -55,15 +55,22 @@ public class VertexPropertySerializer extends SimpleTypeSerializer<VertexPropert @Override protected ByteBuf writeValue(final VertexProperty value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(5); - result.addComponent(true, context.write(value.id(), allocator)); - result.addComponent(true, context.writeValue(value.label(), allocator, false)); - result.addComponent(true, context.write(value.value(), allocator)); - // we don't serialize the parent vertex even as a "reference", but, let's hold a place for it - result.addComponent(true, context.write(null, allocator)); - // we don't serialize properties for graph elements. they are "references", but we leave a place holder - // here as an option for the future as we've waffled this soooooooooo many times now - result.addComponent(true, context.write(null, allocator)); + try { + result.addComponent(true, context.write(value.id(), allocator)); + result.addComponent(true, context.writeValue(value.label(), allocator, false)); + result.addComponent(true, context.write(value.value(), allocator)); + + // we don't serialize the parent vertex even as a "reference", but, let's hold a place for it + result.addComponent(true, context.write(null, allocator)); + // we don't serialize properties for graph elements. they are "references", but we leave a place holder + // here as an option for the future as we've waffled this soooooooooo many times now + result.addComponent(true, context.write(null, allocator)); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } return result; } diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexSerializer.java index a989ab1..5ed5e16 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/VertexSerializer.java @@ -50,12 +50,19 @@ public class VertexSerializer extends SimpleTypeSerializer<Vertex> { @Override protected ByteBuf writeValue(final Vertex value, final ByteBufAllocator allocator, final GraphBinaryWriter context) throws SerializationException { final CompositeByteBuf result = allocator.compositeBuffer(3); - result.addComponent(true, context.write(value.id(), allocator)); - result.addComponent(true, context.writeValue(value.label(), allocator, false)); - - // we don't serialize properties for graph vertices/edges. they are "references", but we leave a place holder - // here as an option for the future as we've waffled this soooooooooo many times now - result.addComponent(true, context.write(null, allocator)); + + try { + result.addComponent(true, context.write(value.id(), allocator)); + result.addComponent(true, context.writeValue(value.label(), allocator, false)); + + // we don't serialize properties for graph vertices/edges. they are "references", but we leave a place holder + // here as an option for the future as we've waffled this soooooooooo many times now + result.addComponent(true, context.write(null, allocator)); + } catch (Exception ex) { + // We should release it as the ByteBuf is not going to be yielded for a reader + result.release(); + throw ex; + } return result; } diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerFailureTests.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerFailureTests.java new file mode 100644 index 0000000..c8f935e --- /dev/null +++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerFailureTests.java @@ -0,0 +1,97 @@ +package org.apache.tinkerpop.gremlin.driver.ser.binary; + +import io.netty.buffer.UnpooledByteBufAllocator; +import org.apache.tinkerpop.gremlin.driver.ser.SerializationException; +import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser; +import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics; +import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdge; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferencePath; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexProperty; +import org.apache.tinkerpop.gremlin.util.function.Lambda; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(Parameterized.class) +public class TypeSerializerFailureTests { + + private final GraphBinaryWriter writer = new GraphBinaryWriter(); + private final UnpooledByteBufAllocator allocator = new UnpooledByteBufAllocator(false); + + @Parameterized.Parameters(name = "Value={0}") + public static Collection input() { + final Bytecode.Binding b = new Bytecode.Binding(null, "b"); + + final ReferenceVertex vertex = new ReferenceVertex("a vertex", null); + + final Bytecode bytecode = new Bytecode(); + bytecode.addStep(null); + + final BulkSet<Object> bulkSet = new BulkSet<>(); + bulkSet.add(vertex, 1L); + + final MutableMetrics metrics = new MutableMetrics("a metric", null); + + final Tree<Vertex> tree = new Tree<>(); + tree.put(vertex, null); + + // Provide instances that are malformed for serialization to fail + return Arrays.asList( + b, + vertex, + Collections.singletonMap("one", b), + bulkSet, + bytecode, + Collections.singletonList(vertex), + new ReferenceEdge("an edge", null, vertex, vertex), + Lambda.supplier(null), + metrics, + new DefaultTraversalMetrics(1L, Collections.singletonList(metrics)), + new DefaultRemoteTraverser<>(new Object(), 1L), + tree, + new ReferenceVertexProperty<>("a prop", null, "value"), + new InvalidPath() + ); + } + + @Parameterized.Parameter(value = 0) + public Object value; + + @Test + public void shouldReleaseMemoryWhenFails() { + try { + writer.write(value, allocator); + fail("Should throw exception"); + } catch (SerializationException | RuntimeException e) { + // Do nothing + } + + assertEquals(0, allocator.metric().usedHeapMemory()); + } + + public static class InvalidPath extends ReferencePath { + public InvalidPath() { + super(EmptyPath.instance()); + } + + @Override + public List<Object> objects() { + return Collections.singletonList(new Object()); + } + } +}