I think your understanding is correct. I'm not sure how that squares with
traversals that don't use remoting. Some testing indicates that if an
exception is thrown during iteration, that as long as there are items to
iterate in the stream the traversal can continue:

gremlin> t = g.V(1,2).map{ if (r.nextBoolean()) { throw new Exception()}
else { "x" }};[]
gremlin> t.toList()
java.lang.reflect.UndeclaredThrowableException
Type ':help' or ':h' for help.
Display stack trace? [yN]n
gremlin> t.toList()
java.lang.reflect.UndeclaredThrowableException
Type ':help' or ':h' for help.
Display stack trace? [yN]n
gremlin> t.toList()
gremlin> t = g.V(1,2).map{ if (r.nextBoolean()) { throw new Exception()}
else { "x" }};[]
gremlin> t.toList()
java.lang.reflect.UndeclaredThrowableException
Type ':help' or ':h' for help.
Display stack trace? [yN]n
gremlin> t.toList()
==>x
gremlin> t.toList()
gremlin>

 I don't know that we can accomplish the same behavior because a failure
during iteration on the server likely means the death of that request. Once
we get an exception there, the server abandons that traversal and returns
the exception. I wonder if this is a case where non-remote traversals
should follow that pattern? Maybe, an exception during iteration should
render that traversal "empty" and allow no additional iteration?

Anyway, I think that if you are doing retry, you should clone() or
otherwise reconstruct the traversal after an exception prior to retry. You
probably shouldn't retry with a partially iterated traversal instance:

void execute(GraphTraversal gt) {
    retryStrategy.apply(() -> {
         gt.clone().iterate();
    }
}

Does that fix your problem?


On Mon, Mar 11, 2019 at 7:22 PM Sainath <[email protected]> wrote:

> Hello,
>
> We noticed that remote traversals in Tinkerpop client only evaluate the
> request exactly once, just wanted to check if its by design. For example
> lets say if I have client code that looks like below (retryStrategy will
> retry the supplied lambda if exceptions are thrown from it),
>
> GraphTraversal buildQuery() {
>     return g.addV().
>                  ... // some more steps here
> }
>
> void execute(GraphTraversal gt) {
>     retryStrategy.apply(() -> {
>          gt.iterate();
>     }
> }
>
> As per this code [1], RemoteStep only submits the request the first time it
> executes. After this it reuses the same future and for someone calling a
> terminal step it will continue to give the same error if the first request
> errored out. In above example code, retry strategy is ineffective since it
> will continue to get the same exception.
>
> I think this is misleading since it makes it seem like the server is
> throwing the same exception even though no new requests are being enqueued.
> Instead of this client can explicitly throw an error that indicates results
> are already consumed for this traversal (say TraversalClosedException).
> That way it clearly indicates to the user that they will need to create
> another traversal for sending the request. Another alternative is to
> resubmit the bytecode to the server every-time a terminal method is called
> by making a copy of the traversal.
>
> I would like to hear your thoughts on how to handle this or let me know if
> I misunderstood anything here. In addition to this, it would be good to add
> some documentation around client error handling to reference documentation.
> Happy to contribute to both these areas.
>
> Thanks,
> -Sainath
>
> [1]
>
> https://github.com/apache/tinkerpop/blob/master/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/remote/traversal/step/map/RemoteStep.java#L88
>

Reply via email to