This is an automated email from the ASF dual-hosted git repository.
lyndonb pushed a commit to branch TINKERPOP-3002
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/TINKERPOP-3002 by this push:
new 00c6f5eac9 TINKERPOP-3002 initial commit with test that reproduces
issue and switch to TraverSet that fixes this. This is not a long term solution
because it effectively forces everything to be eager, which is also not
correct. It should toggle between eager and lazy behavior as is appropriate
with the barriers present. To do this we must use IteratorUtils.mappings, which
will come next commit.
00c6f5eac9 is described below
commit 00c6f5eac9153ff1908e5e90f8acd56012cc2fa8
Author: lyndon <[email protected]>
AuthorDate: Thu Oct 12 20:07:10 2023 -0700
TINKERPOP-3002 initial commit with test that reproduces issue and switch to
TraverSet that fixes this. This is not a long term solution because it
effectively forces everything to be eager, which is also not correct. It should
toggle between eager and lazy behavior as is appropriate with the barriers
present. To do this we must use IteratorUtils.mappings, which will come next
commit.
---
.../process/traversal/step/branch/RepeatStep.java | 74 +++++++++++------
.../process/traversal/util/TraversalUtil.java | 32 ++++++++
.../structure/TinkerGraphRepeatBarrierTest.java | 94 ++++++++++++++++++++++
3 files changed, 177 insertions(+), 23 deletions(-)
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
index 955dacbcb0..0f2757ab43 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/RepeatStep.java
@@ -24,6 +24,8 @@ import
org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import
org.apache.tinkerpop.gremlin.process.traversal.step.util.ComputerAwareStep;
import
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import
org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -113,14 +115,22 @@ public final class RepeatStep<S> extends
ComputerAwareStep<S, S> implements Trav
return list;
}
- public final boolean doUntil(final Traverser.Admin<S> traverser, boolean
utilFirst) {
- return utilFirst == this.untilFirst && null != this.untilTraversal &&
TraversalUtil.test(traverser, this.untilTraversal);
+ public final boolean doUntil(final Traverser.Admin<S> traverser, boolean
untilFirst) {
+ return untilFirst == this.untilFirst && null != this.untilTraversal &&
TraversalUtil.test(traverser, this.untilTraversal);
+ }
+
+ public final boolean doUntil(final TraverserSet<S> traverserSet, boolean
untilFirst) {
+ return untilFirst == this.untilFirst && null != this.untilTraversal &&
TraversalUtil.test(traverserSet, this.untilTraversal);
}
public final boolean doEmit(final Traverser.Admin<S> traverser, boolean
emitFirst) {
return emitFirst == this.emitFirst && null != this.emitTraversal &&
TraversalUtil.test(traverser, this.emitTraversal);
}
+ public final boolean doEmit(final TraverserSet<S> traverserSet, boolean
emitFirst) {
+ return emitFirst == this.emitFirst && null != this.emitTraversal &&
TraversalUtil.test(traverserSet, this.emitTraversal);
+ }
+
@Override
public String toString() {
if (this.untilFirst && this.emitFirst)
@@ -196,17 +206,26 @@ public final class RepeatStep<S> extends
ComputerAwareStep<S, S> implements Trav
if (this.repeatTraversal.getEndStep().hasNext()) {
return this.repeatTraversal.getEndStep();
} else {
- final Traverser.Admin<S> start = this.starts.next();
- start.initialiseLoops(this.getId(), this.loopName);
- if (doUntil(start, true)) {
- start.resetLoops();
- return IteratorUtils.of(start);
+ TraverserSet<S> traverserSet = new TraverserSet<>();
+ do {
+ Traverser.Admin<S> start = starts.next();
+ start.initialiseLoops(this.getId(), this.loopName);
+ traverserSet.add(start);
+ } while ((starts.hasNext()));
+
+ if (doUntil(traverserSet, true)) {
+ traverserSet.forEach(Traverser.Admin::resetLoops);
+ return traverserSet.iterator();
}
- this.repeatTraversal.addStart(start);
- if (doEmit(start, true)) {
- final Traverser.Admin<S> emitSplit = start.split();
- emitSplit.resetLoops();
- return IteratorUtils.of(emitSplit);
+ this.repeatTraversal.addStarts(traverserSet.iterator());
+ if (doEmit(traverserSet, true)) {
+ TraverserSet<S> emitSet = new TraverserSet<>();
+ traverserSet.forEach(traverser -> {
+ Traverser.Admin<S> emitSplit = traverser.split();
+ emitSplit.resetLoops();
+ emitSet.add(emitSplit);
+ });
+ return emitSet.iterator();
}
}
}
@@ -293,20 +312,29 @@ public final class RepeatStep<S> extends
ComputerAwareStep<S, S> implements Trav
protected Iterator<Traverser.Admin<S>> standardAlgorithm() throws
NoSuchElementException {
final RepeatStep<S> repeatStep = (RepeatStep<S>)
this.getTraversal().getParent();
while (true) {
- final Traverser.Admin<S> start = this.starts.next();
- start.incrLoops();
- if (repeatStep.doUntil(start, false)) {
- start.resetLoops();
- return IteratorUtils.of(start);
+ TraverserSet<S> traverserSet = new TraverserSet<>();
+ do {
+ Traverser.Admin<S> start = starts.next();
+ start.incrLoops();
+ traverserSet.add(start);
+ } while ((starts.hasNext()));
+
+ if (repeatStep.doUntil(traverserSet, false)) {
+ traverserSet.forEach(Traverser.Admin::resetLoops);
+ return traverserSet.iterator();
} else {
if (!repeatStep.untilFirst && !repeatStep.emitFirst)
- repeatStep.repeatTraversal.addStart(start);
+
repeatStep.repeatTraversal.addStarts(traverserSet.iterator());
else
- repeatStep.addStart(start);
- if (repeatStep.doEmit(start, false)) {
- final Traverser.Admin<S> emitSplit = start.split();
- emitSplit.resetLoops();
- return IteratorUtils.of(emitSplit);
+ repeatStep.addStarts(traverserSet.iterator());
+ if (repeatStep.doEmit(traverserSet, false)) {
+ TraverserSet<S> emitSet = new TraverserSet<>();
+ traverserSet.forEach(traverser -> {
+ final Traverser.Admin<S> emitSplit =
traverser.split();
+ emitSplit.resetLoops();
+ emitSet.add(emitSplit);
+ });
+ return emitSet.iterator();
}
}
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
index 1acb54743b..2f908bab36 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.util;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
import java.util.Iterator;
@@ -136,6 +137,21 @@ public final class TraversalUtil {
}
}
+ public static <S, E> boolean test(final TraverserSet<S> traverserSet,
final Traversal.Admin<S, E> traversal) {
+ prepare(traverserSet, traversal);
+
+ boolean val = false;
+ while (traversal.hasNext()) {
+ val = true;
+ traversal.next();
+ }
+
+ //Close the traversal to release any underlying resources.
+ CloseableIterator.closeIterator(traversal);
+
+ return val;
+ }
+
public static <S, E> boolean test(final Traverser.Admin<S> traverser,
final Traversal.Admin<S, E> traversal) {
prepare(traverser, traversal);
final boolean val = traversal.hasNext(); // filter
@@ -186,4 +202,20 @@ public final class TraversalUtil {
traversal.addStart(split);
return split;
}
+
+ public static <S, E> TraverserSet<S> prepare(final TraverserSet<S>
traverserSet, final Traversal.Admin<S, E> traversal) {
+ final TraverserSet<S> traverserSetClone = new TraverserSet<>();
+ final TraverserSet<S> traverserSetClone2 = new TraverserSet<>();
+ while (!traverserSet.isEmpty()) {
+ Traverser.Admin<S> split = traverserSet.remove().split();
+ traverserSetClone2.add(split);
+ split.setSideEffects(traversal.getSideEffects());
+ split.setBulk(1L);
+ traversal.reset();
+ traverserSetClone.add(split);
+ }
+ traversal.addStarts(traverserSetClone.iterator());
+ traverserSet.addAll(traverserSetClone2);
+ return traverserSetClone;
+ }
}
diff --git
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphRepeatBarrierTest.java
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphRepeatBarrierTest.java
new file mode 100644
index 0000000000..a6f67c9f00
--- /dev/null
+++
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphRepeatBarrierTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.tinkergraph.structure;
+
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
+import
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class TinkerGraphRepeatBarrierTest {
+
+ @Test
+ public void test() {
+ final TinkerGraph tg = TinkerGraph.open();
+ final GraphTraversalSource g = tg.traversal();
+
+ // Create structure:
+ // root-branch1->[leaf_1, leaf_2, leaf_3]
+ // leaf_1-branch2->[leaf2_1, leaf2_2, leaf2_3]
+ // leaf_2-branch2->[leaf2_4, leaf2_5, leaf2_6]
+ // leaf_3-branch2->[leaf2_7, leaf2_8, leaf2_9]
+ g.addV("root").as("root").
+ addE("branch1").to(__.addV("leaf1_1").as("leaf1_1").
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_1"))).
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_2"))).
+
sideEffect(__.addE("branch2").to(__.addV("leaf2_3"))).select("leaf1_1")).
+ select("root").
+ addE("branch1x").to(__.addV("leaf1_2").as("leaf1_2").
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_4"))).
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_5"))).
+
sideEffect(__.addE("branch2").to(__.addV("leaf2_6"))).select("leaf1_2")).
+ select("root").
+ addE("branch1xx").to(__.addV("leaf1_3").as("leaf1_3").
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_7"))).
+ sideEffect(__.addE("branch2").to(__.addV("leaf2_8"))).
+
sideEffect(__.addE("branch2").to(__.addV("leaf2_9"))).select("leaf1_3")).iterate();
+
+ final BarrierConsumerInputCounter untilBlockBarrierConsumer = new
BarrierConsumerInputCounter("until");
+ final BarrierConsumerInputCounter repeatBlockBarrierConsumer = new
BarrierConsumerInputCounter("repeat");
+
+ // If the barrier is respected by the repeat step, we will get 3 then
9.
+ final List<?> data1 = g.
+ withoutStrategies(RepeatUnrollStrategy.class)
+ .V().hasLabel("root").
+ repeat(
+ __.barrier(repeatBlockBarrierConsumer).out()).
+ until(
+
__.barrier(untilBlockBarrierConsumer).out().count().is(P.eq(0))).
+ toList();
+ // Should also try barrier of 1 < barrierSize < count per stage
+ System.out.println("data1: " + data1);
+ System.out.println("untilBlockBarrierConsumer.barrierInputs: " +
untilBlockBarrierConsumer.barrierInputs);
+ System.out.println("repeatBlockBarrierConsumer.barrierInputs: " +
repeatBlockBarrierConsumer.barrierInputs);
+ }
+
+ static class BarrierConsumerInputCounter implements
Consumer<TraverserSet<Object>> {
+ public final List<Integer> barrierInputs = new ArrayList<>();
+ public final String label;
+
+ public BarrierConsumerInputCounter(final String label) {
+ this.label = label;
+ }
+
+ @Override
+ public void accept(TraverserSet<Object> admins) {
+ barrierInputs.add(admins.size());
+ System.out.println(label + "-" + admins.size());
+ // Pass through.
+ }
+ }
+}