You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/02/26 21:16:58 UTC

incubator-tinkerpop git commit: added a GroupCountTest that uses two sideEffect cap() calls in the same traversal with OLTP projecting select(keys). Little bit of a hole I got into trying to get it to work. However, now SupplyingBarrierSteps can be anywh

Repository: incubator-tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1166 6f5132080 -> 104496c06


added a GroupCountTest that uses two sideEffect cap() calls in the same traversal with OLTP projecting select(keys). Little bit of a hole I got into trying to get it to work. However, now SupplyingBarrierSteps can be anywhere in an OLAP traversal. One less ComputerVerificationStrategy exclusion.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/104496c0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/104496c0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/104496c0

Branch: refs/heads/TINKERPOP-1166
Commit: 104496c062d0772f82a30f401eb9ef52cc49999e
Parents: 6f51320
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Fri Feb 26 13:16:51 2016 -0700
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Fri Feb 26 13:16:51 2016 -0700

----------------------------------------------------------------------
 .../traversal/TraversalVertexProgram.java       | 60 +++++++++---------
 .../computer/traversal/TraverserExecutor.java   |  9 ++-
 .../traversal/step/filter/RangeGlobalStep.java  |  4 ++
 .../step/sideEffect/SideEffectCapStep.java      | 67 ++++++++++++--------
 .../decoration/VertexProgramStrategy.java       | 14 +---
 .../ComputerVerificationStrategy.java           | 18 +-----
 .../step/sideEffect/GroovyGroupCountTest.groovy |  7 +-
 .../step/sideEffect/GroupCountTest.java         | 24 +++++++
 .../structure/TinkerGraphPlayTest.java          |  4 +-
 9 files changed, 116 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java
index 1b1988a..5b11f84 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java
@@ -50,6 +50,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.SupplyingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ScriptTraversal;
@@ -89,12 +90,11 @@ import java.util.Set;
  */
 public final class TraversalVertexProgram implements VertexProgram<TraverserSet<?>> {
 
+    public static final String TRAVERSAL = "gremlin.traversalVertexProgram.traversal";
     public static final String HALTED_TRAVERSERS = "gremlin.traversalVertexProgram.haltedTraversers";
+    public static final String ACTIVE_TRAVERSERS = "gremlin.traversalVertexProgram.activeTraversers";
+    protected static final String MUTATED_MEMORY_KEYS = "gremlin.traversalVertexProgram.mutatedMemoryKeys";
     private static final String VOTE_TO_HALT = "gremlin.traversalVertexProgram.voteToHalt";
-    public static final String TRAVERSAL = "gremlin.traversalVertexProgram.traversal";
-
-    public static final String ALIVE_TRAVERSERS = Graph.Hidden.hide("aliveTraversers");
-    public static final String MUTATED_MEMORY_KEYS = Graph.Hidden.hide("mutatedMemoryKeys");
 
     // TODO: if not an adjacent traversal, use Local message scope -- a dual messaging system.
     private static final Set<MessageScope> MESSAGE_SCOPES = new HashSet<>(Collections.singletonList(MessageScope.Global.instance()));
@@ -102,12 +102,12 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
     private Set<String> sideEffectKeys = new HashSet<>();
     private static final Set<VertexComputeKey> VERTEX_COMPUTE_KEYS = new HashSet<>(Arrays.asList(
             VertexComputeKey.of(HALTED_TRAVERSERS, false),
-            VertexComputeKey.of(TraversalSideEffects.SIDE_EFFECTS, false)));
+            VertexComputeKey.of(TraversalSideEffects.SIDE_EFFECTS, false))); // TODO: when MapReducers are not longer, remove this
 
     private PureTraversal<?, ?> traversal;
     private TraversalMatrix<?, ?> traversalMatrix;
     private final Set<MapReduce> mapReducers = new HashSet<>();
-    private boolean keepHaltedTraversersDistributed = true;
+    private boolean keepDistributedHaltedTraversers = true;
     private Set<String> neverTouchedMemoryKeys = new HashSet<>();
 
     private TraversalVertexProgram() {
@@ -131,32 +131,33 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
         this.traversal = PureTraversal.loadState(configuration, TRAVERSAL, graph);
         if (!this.traversal.get().isLocked())
             this.traversal.get().applyStrategies();
+        /// traversal is compiled and ready to be introspected
         this.traversalMatrix = new TraversalMatrix<>(this.traversal.get());
-        this.memoryComputeKeys.add(MemoryComputeKey.of(VOTE_TO_HALT, Operator.and, false, true));
-        for (final MapReducer<?, ?, ?, ?, ?> mapReducer : TraversalHelper.getStepsOfAssignableClassRecursively(MapReducer.class, this.traversal.get())) {
-            this.mapReducers.add(mapReducer.getMapReduce());
-            this.memoryComputeKeys.add(MemoryComputeKey.of(mapReducer.getMapReduce().getMemoryKey(), Operator.assign, false, false));
-        }
-
-        if (this.traversal.get().getParent().asStep().getNextStep() instanceof ComputerResultStep)
-            this.keepHaltedTraversersDistributed = false;
+        // if results will be serialized out, don't save halted traversers across the cluster
+        this.keepDistributedHaltedTraversers = !(this.traversal.get().getParent().asStep().getNextStep() instanceof ComputerResultStep);
+        // dangling MapReduces left
         if (this.traversal.get().getEndStep() instanceof SampleGlobalStep ||
                 this.traversal.get().getEndStep() instanceof LambdaCollectingBarrierStep ||
                 this.traversal.get().getEndStep() instanceof AggregateStep) {
             this.mapReducers.add(new TraverserMapReduce(this.traversal.get()));
-            this.keepHaltedTraversersDistributed = true;
+            this.keepDistributedHaltedTraversers = true;
         }
-
+        // register MapReducer memory compute keys
+        this.memoryComputeKeys.add(MemoryComputeKey.of(VOTE_TO_HALT, Operator.and, false, true));
+        for (final MapReducer<?, ?, ?, ?, ?> mapReducer : TraversalHelper.getStepsOfAssignableClassRecursively(MapReducer.class, this.traversal.get())) {
+            this.mapReducers.add(mapReducer.getMapReduce());
+            this.memoryComputeKeys.add(MemoryComputeKey.of(mapReducer.getMapReduce().getMemoryKey(), Operator.assign, false, false));
+        }
+        // register GraphComputing memory compute keys
         for (final GraphComputing<?> graphComputing : TraversalHelper.getStepsOfAssignableClassRecursively(GraphComputing.class, this.traversal.get())) {
             graphComputing.getMemoryComputeKey().ifPresent(this.memoryComputeKeys::add);
+            graphComputing.getMemoryComputeKey().ifPresent(memoryComputeKey -> this.neverTouchedMemoryKeys.add(memoryComputeKey.getKey()));
             graphComputing.getMemoryComputeKey().ifPresent(x -> this.sideEffectKeys.add(x.getKey())); // TODO: when no more MapReducers, you can remove this
-            graphComputing.getMemoryComputeKey().ifPresent(x -> this.neverTouchedMemoryKeys.add(x.getKey()));
         }
-
+        // register TraversalVertexProgram specific memory compute keys
         this.memoryComputeKeys.add(MemoryComputeKey.of(HALTED_TRAVERSERS, Operator.addAll, false, false));
-        this.memoryComputeKeys.add(MemoryComputeKey.of(ALIVE_TRAVERSERS, Operator.addAll, true, true));
+        this.memoryComputeKeys.add(MemoryComputeKey.of(ACTIVE_TRAVERSERS, Operator.addAll, true, true));
         this.memoryComputeKeys.add(MemoryComputeKey.of(MUTATED_MEMORY_KEYS, Operator.addAll, false, true));
-
     }
 
     @Override
@@ -172,7 +173,7 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
             memory.set(reducingBarrierStep.getId(), reducingBarrierStep.getSeedSupplier().get());
         }
         memory.set(HALTED_TRAVERSERS, new TraverserSet<>());
-        memory.set(ALIVE_TRAVERSERS, new TraverserSet<>());
+        memory.set(ACTIVE_TRAVERSERS, new TraverserSet<>());
         memory.set(MUTATED_MEMORY_KEYS, new HashSet<>());
     }
 
@@ -221,7 +222,7 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
                 vertex.<Map<String, Object>>property(VertexTraversalSideEffects.SIDE_EFFECTS).value().remove(key);
             }
         });
-        if (!this.keepHaltedTraversersDistributed)
+        if (!this.keepDistributedHaltedTraversers)
             vertex.<TraverserSet>property(HALTED_TRAVERSERS).value().clear();
     }
 
@@ -232,14 +233,16 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
         final boolean voteToHalt = memory.<Boolean>get(VOTE_TO_HALT);
         memory.set(VOTE_TO_HALT, true);
         memory.set(MUTATED_MEMORY_KEYS, new HashSet<>());
-        memory.set(ALIVE_TRAVERSERS, new TraverserSet<>());
+        memory.set(ACTIVE_TRAVERSERS, new TraverserSet<>());
         ///
         if (voteToHalt) {
+            memory.keys().forEach(key -> this.traversal.get().getSideEffects().set(key, memory.get(key)));
             final TraverserSet<Object> toProcessTraversers = new TraverserSet<>();
             final TraverserSet<?> localAliveTraversers = new TraverserSet<>();
             final TraverserSet<?> remoteAliveTraversers = new TraverserSet<>();
             final TraverserSet<?> haltedTraversers = memory.get(HALTED_TRAVERSERS);
             this.processMemory(memory, mutatedMemoryKeys, toProcessTraversers);
+            memory.keys().forEach(key -> this.traversal.get().getSideEffects().set(key, memory.get(key)));
             while (!toProcessTraversers.isEmpty()) {
                 this.processTraversers(toProcessTraversers, localAliveTraversers, remoteAliveTraversers, haltedTraversers);
                 toProcessTraversers.clear();
@@ -247,7 +250,7 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
                 localAliveTraversers.clear();
             }
             if (!remoteAliveTraversers.isEmpty()) {
-                memory.set(ALIVE_TRAVERSERS, remoteAliveTraversers);
+                memory.set(ACTIVE_TRAVERSERS, remoteAliveTraversers);
                 return false;
             } else {
                 if (!this.neverTouchedMemoryKeys.isEmpty())
@@ -267,13 +270,12 @@ public final class TraversalVertexProgram implements VertexProgram<TraverserSet<
                 if (memory.exists(key) && toProcessMemoryKeys.contains(key)) {
                     if (graphComputing instanceof RangeGlobalStep || graphComputing instanceof TailGlobalStep || graphComputing instanceof OrderGlobalStep || graphComputing instanceof DedupGlobalStep) {
                         traverserSet.addAll(((TraverserSet) graphComputing.generateFinalResult(memory.get(key))));
-                    } else if (graphComputing instanceof ReducingBarrierStep) {
+                    } else if (graphComputing instanceof ReducingBarrierStep || graphComputing instanceof SupplyingBarrierStep) {
                         final Traverser.Admin traverser = this.traversal.get().getTraverserGenerator().generate(graphComputing.generateFinalResult(memory.get(key)), ((Step) graphComputing), 1l);
-                        if (((ReducingBarrierStep) graphComputing).hasNext())
-                            ((ReducingBarrierStep) graphComputing).next(); // or else you will get a seed
-                        traverser.setStepId(((Step) graphComputing).getNextStep().getId()); // should really for ReducingBarrierStep seed.
+                        if (((Step) graphComputing).hasNext())
+                            ((Step) graphComputing).next(); // or else you will get a seed
+                        traverser.setStepId(((Step) graphComputing).getNextStep().getId());
                         traverserSet.add(traverser);
-                        memory.set(key, ((ReducingBarrierStep) graphComputing).getSeedSupplier().get());
                     } else {
                         memory.set(key, graphComputing.generateFinalResult(memory.get(key))); // XXXSideEffectSteps (may need to relocate this to post HALT)
                     }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraverserExecutor.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraverserExecutor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraverserExecutor.java
index 917e737..f6ee905 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraverserExecutor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/TraverserExecutor.java
@@ -28,8 +28,10 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Bypassing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.TailGlobalStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SideEffectCapStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.SupplyingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMatrix;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -56,7 +58,7 @@ public final class TraverserExecutor {
         final TraverserSet<Object> aliveTraversers = new TraverserSet<>();
         final TraverserSet<Object> toProcessTraversers = new TraverserSet<>();
 
-        final TraverserSet<Object> maybeAliveTraversers = memory.get(TraversalVertexProgram.ALIVE_TRAVERSERS);
+        final TraverserSet<Object> maybeAliveTraversers = memory.get(TraversalVertexProgram.ACTIVE_TRAVERSERS);
         final Iterator<Traverser.Admin<Object>> iterator = maybeAliveTraversers.iterator();
         while (iterator.hasNext()) {
             final Traverser.Admin<Object> traverser = iterator.next();
@@ -126,6 +128,9 @@ public final class TraverserExecutor {
         if (step instanceof ReducingBarrierStep) {
             memory.add(step.getId(), step.next().get());
             memory.add(TraversalVertexProgram.MUTATED_MEMORY_KEYS, new HashSet<>(Collections.singleton(step.getId())));
+        } else if (step instanceof SupplyingBarrierStep) {
+            memory.add(step.getId(), true);
+            memory.add(TraversalVertexProgram.MUTATED_MEMORY_KEYS, new HashSet<>(Collections.singleton(step.getId())));
         } else if (step instanceof RangeGlobalStep || step instanceof TailGlobalStep || step instanceof OrderGlobalStep) {
             ((Bypassing) step).setBypass(true);
             final TraverserSet<?> traverserSet = new TraverserSet<>();
@@ -137,7 +142,7 @@ public final class TraverserExecutor {
             });
             memory.add(step.getId(), traverserSet);
             memory.add(TraversalVertexProgram.MUTATED_MEMORY_KEYS, new HashSet<>(Collections.singleton(step.getId())));
-        } else {
+        } else { // LOCAL PROCESSING
             step.forEachRemaining(traverser -> {
                 if (traverser.asAdmin().isHalted()) {
                     traverser.asAdmin().detach();

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
index 5a43a57..e6d7031 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/RangeGlobalStep.java
@@ -158,6 +158,10 @@ public final class RangeGlobalStep<S> extends FilterStep<S> implements Ranging,
 
         private final long highRange;
 
+        private RangeBiOperator() {
+            this.highRange = Long.MAX_VALUE;
+        }
+
         public RangeBiOperator(final long highRange) {
             this.highRange = highRange;
         }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java
index 1a973a3..f132897 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/SideEffectCapStep.java
@@ -18,8 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
 
+import org.apache.tinkerpop.gremlin.process.computer.MemoryComputeKey;
+import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.SupplyingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -31,12 +32,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class SideEffectCapStep<S, E> extends SupplyingBarrierStep<S, E> implements GraphComputing {
+public final class SideEffectCapStep<S, E> extends SupplyingBarrierStep<S, E> implements GraphComputing<E> {
 
     private List<String> sideEffectKeys;
     private transient Map<String, GraphComputing> sideEffectFinalizer;
@@ -75,7 +77,40 @@ public final class SideEffectCapStep<S, E> extends SupplyingBarrierStep<S, E> im
     }
 
     @Override
-    public Traverser<E> processNextStart() {
+    protected E supply() {
+        if (this.onGraphComputer)
+            return null;
+        else
+            return this.generateFinalResult(null);
+    }
+
+    private Map<String, Object> getMapOfSideEffects() {
+        final Map<String, Object> sideEffects = new HashMap<>();
+        for (final String sideEffectKey : this.sideEffectKeys) {
+            this.getTraversal().asAdmin().getSideEffects().get(sideEffectKey).ifPresent(value -> {
+                if (this.onGraphComputer) {
+                    sideEffects.put(sideEffectKey, value);
+                } else {
+                    final GraphComputing finalizer = this.sideEffectFinalizer.get(sideEffectKey);
+                    sideEffects.put(sideEffectKey, null == finalizer ? value : finalizer.generateFinalResult(value));
+                }
+            });
+        }
+        return sideEffects;
+    }
+
+    @Override
+    public void onGraphComputer() {
+        this.onGraphComputer = true;
+    }
+
+    @Override
+    public Optional<MemoryComputeKey> getMemoryComputeKey() {
+        return Optional.of(MemoryComputeKey.of(this.getId(), Operator.assign, false, true));
+    }
+
+    @Override
+    public E generateFinalResult(final E nothing) {
         if (!this.onGraphComputer && null == this.sideEffectFinalizer) {
             this.sideEffectFinalizer = new HashMap<>();
             for (final String key : this.sideEffectKeys) {
@@ -86,11 +121,7 @@ public final class SideEffectCapStep<S, E> extends SupplyingBarrierStep<S, E> im
                 }
             }
         }
-        return super.processNextStart();
-    }
-
-    @Override
-    protected E supply() {
+        ////////////
         if (this.sideEffectKeys.size() == 1) {
             final String sideEffectKey = this.sideEffectKeys.get(0);
             final E result = this.getTraversal().getSideEffects().<E>get(sideEffectKey).get();
@@ -103,24 +134,4 @@ public final class SideEffectCapStep<S, E> extends SupplyingBarrierStep<S, E> im
         } else
             return (E) this.getMapOfSideEffects();
     }
-
-    public Map<String, Object> getMapOfSideEffects() {
-        final Map<String, Object> sideEffects = new HashMap<>();
-        for (final String sideEffectKey : this.sideEffectKeys) {
-            this.getTraversal().asAdmin().getSideEffects().get(sideEffectKey).ifPresent(value -> {
-                if (this.onGraphComputer) {
-                    sideEffects.put(sideEffectKey, value);
-                } else {
-                    final GraphComputing finalizer = this.sideEffectFinalizer.get(sideEffectKey);
-                    sideEffects.put(sideEffectKey, null == finalizer ? value : finalizer.generateFinalResult(value));
-                }
-            });
-        }
-        return sideEffects;
-    }
-
-    @Override
-    public void onGraphComputer() {
-        this.onGraphComputer = true;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/VertexProgramStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/VertexProgramStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/VertexProgramStrategy.java
index 48780ff..cb2ec31 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/VertexProgramStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/VertexProgramStrategy.java
@@ -112,7 +112,7 @@ public final class VertexProgramStrategy extends AbstractTraversalStrategy<Trave
             }
             currentStep = traversal.getStartStep();
             while (!(currentStep instanceof EmptyStep)) {
-                if (!(currentStep instanceof VertexComputing) && !lastTraversalVertexProgramDone(traversal))
+                if (!(currentStep instanceof VertexComputing))
                     break;
                 currentStep = currentStep.getNextStep();
             }
@@ -134,8 +134,6 @@ public final class VertexProgramStrategy extends AbstractTraversalStrategy<Trave
         }
         // all vertex computing steps needs the graph computer function
         traversal.getSteps().stream().filter(step -> step instanceof VertexComputing).forEach(step -> ((VertexComputing) step).setGraphComputerFunction(this.graphComputerFunction));
-
-        //System.out.println(traversal + "!!!!!!!!!!!");
     }
 
     private static Step<?, ?> getFirstLegalOLAPStep(Step<?, ?> currentStep) {
@@ -151,8 +149,6 @@ public final class VertexProgramStrategy extends AbstractTraversalStrategy<Trave
         while (currentStep instanceof VertexComputing)
             currentStep = currentStep.getNextStep();
         while (!(currentStep instanceof EmptyStep)) {
-            if (ComputerVerificationStrategy.isStepInstanceOfEndStep(currentStep))
-                return currentStep;
             if (currentStep instanceof VertexComputing)
                 return currentStep.getPreviousStep();
             currentStep = currentStep.getNextStep();
@@ -160,14 +156,6 @@ public final class VertexProgramStrategy extends AbstractTraversalStrategy<Trave
         return EmptyStep.instance();
     }
 
-    private static boolean lastTraversalVertexProgramDone(final Traversal.Admin<?, ?> traversal) {
-        Optional<TraversalVertexProgramStep> optional = TraversalHelper.getLastStepOfAssignableClass(TraversalVertexProgramStep.class, traversal);
-        if (!optional.isPresent())
-            return false;
-        else
-            return ComputerVerificationStrategy.isStepInstanceOfEndStep(optional.get().getGlobalChildren().get(0).getEndStep());
-    }
-
     public static Optional<GraphComputer> getGraphComputer(final Graph graph, final TraversalStrategies strategies) {
         final Optional<TraversalStrategy<?>> optional = strategies.toList().stream().filter(strategy -> strategy instanceof VertexProgramStrategy).findAny();
         return optional.isPresent() ? Optional.of(((VertexProgramStrategy) optional.get()).graphComputerFunction.apply(graph)) : Optional.empty();

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
index b277c43..61bcb31 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/ComputerVerificationStrategy.java
@@ -39,7 +39,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrie
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ComputerAwareStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.SupplyingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -59,21 +58,9 @@ public final class ComputerVerificationStrategy extends AbstractTraversalStrateg
             InjectStep.class, Mutating.class, SubgraphStep.class
     ));
 
-    public static final Set<Class<? extends Step>> END_STEPS = new HashSet<>(Arrays.asList(SupplyingBarrierStep.class));
-
     private ComputerVerificationStrategy() {
     }
 
-    public static boolean isStepInstanceOfEndStep(final Step<?, ?> step) {
-        final Class<? extends Step> stepClass = step.getClass();
-        for (final Class<? extends Step> endClass : END_STEPS) {
-            if (endClass.isAssignableFrom(stepClass))
-                return true;
-        }
-        return false;
-    }
-
-
     private static void onlyGlobalChildren(final Traversal.Admin<?, ?> traversal) {
         for (final Step step : traversal.getSteps()) {
             if (step instanceof GraphComputing)
@@ -114,9 +101,8 @@ public final class ComputerVerificationStrategy extends AbstractTraversalStrateg
             if (step instanceof ReducingBarrierStep && step.getTraversal().getParent() instanceof UnionStep)
                 throw new VerificationException("Reducing barriers within union()-step are not allowed: " + step, traversal);
 
-            if ((step instanceof SupplyingBarrierStep || step instanceof LambdaCollectingBarrierStep)
-                    && (step != endStep || !(traversal.getParent() instanceof TraversalVertexProgramStep)))
-                throw new VerificationException("Global traversals on GraphComputer may not contain mid-traversal barriers: " + step, traversal);
+            if ((step instanceof LambdaCollectingBarrierStep) && (step != endStep || !(traversal.getParent() instanceof TraversalVertexProgramStep)))
+                throw new VerificationException("Global traversals on GraphComputer may not contain mid-traversal LambdaCollectingBarrierSteps: " + step, traversal);
 
             if (step instanceof DedupGlobalStep && (!((DedupGlobalStep) step).getLocalChildren().isEmpty() || !((DedupGlobalStep) step).getScopeKeys().isEmpty()))
                 throw new VerificationException("Global traversals on GraphComputer may not contain by()-projecting de-duplication steps: " + step, traversal);

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyGroupCountTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyGroupCountTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyGroupCountTest.groovy
index 0ba8323..44b2419 100644
--- a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyGroupCountTest.groovy
+++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroovyGroupCountTest.groovy
@@ -85,7 +85,12 @@ public abstract class GroovyGroupCountTest {
 
         @Override
         public Traversal<Vertex, Long> get_g_V_unionXoutXknowsX__outXcreatedX_inXcreatedXX_groupCount_selectXvaluesX_unfold_sum() {
-            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.union(out('knows'), out('created').in('created')).groupCount.select(values).unfold.sum");
+            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.union(out('knows'), out('created').in('created')).groupCount.select(values).unfold.sum")
+        }
+
+        @Override
+        public Traversal<Vertex, Map<Vertex, Long>> get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX() {
+            new ScriptTraversal<>(g, "gremlin-groovy", "g.V.both.groupCount('a').out.cap('a').select(keys).unfold.both.groupCount('a').cap('a')")
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
index 69e5fa8..57196ee 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/GroupCountTest.java
@@ -68,6 +68,8 @@ public abstract class GroupCountTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Long> get_g_V_unionXoutXknowsX__outXcreatedX_inXcreatedXX_groupCount_selectXvaluesX_unfold_sum();
 
+    public abstract Traversal<Vertex, Map<Vertex, Long>> get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX();
+
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_outXcreatedX_groupCount_byXnameX() {
@@ -212,6 +214,23 @@ public abstract class GroupCountTest extends AbstractGremlinProcessTest {
         assertFalse(traversal.hasNext());
     }
 
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX() {
+        final Traversal<Vertex, Map<Vertex, Long>> traversal = get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX();
+        printTraversalForm(traversal);
+        //  [{v[1]=6, v[2]=2, v[3]=6, v[4]=6, v[5]=2, v[6]=2}]
+        final Map<Vertex, Long> map = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals(6, map.size());
+        assertEquals(6l, map.get(convertToVertex(graph, "marko")).longValue());
+        assertEquals(2l, map.get(convertToVertex(graph, "vadas")).longValue());
+        assertEquals(6l, map.get(convertToVertex(graph, "lop")).longValue());
+        assertEquals(6l, map.get(convertToVertex(graph, "josh")).longValue());
+        assertEquals(2l, map.get(convertToVertex(graph, "ripple")).longValue());
+        assertEquals(6l, map.get(convertToVertex(graph, "marko")).longValue());
+    }
+
     public static class Traversals extends GroupCountTest {
 
         @Override
@@ -270,5 +289,10 @@ public abstract class GroupCountTest extends AbstractGremlinProcessTest {
         public Traversal<Vertex, Long> get_g_V_unionXoutXknowsX__outXcreatedX_inXcreatedXX_groupCount_selectXvaluesX_unfold_sum() {
             return g.V().union(out("knows"), out("created").in("created")).groupCount().select(Column.values).unfold().sum();
         }
+
+        @Override
+        public Traversal<Vertex, Map<Vertex, Long>> get_g_V_both_groupCountXaX_out_capXaX_selectXkeysX_unfold_both_groupCountXaX_capXaX() {
+            return g.V().both().groupCount("a").out().cap("a").select(Column.keys).unfold().both().groupCount("a").cap("a");
+        }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/104496c0/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
index 37f2587..bb6b687 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
@@ -68,10 +68,10 @@ public class TinkerGraphPlayTest {
     @Ignore
     public void testPlay8() throws Exception {
         Graph graph = TinkerFactory.createModern();
-        GraphTraversalSource g = graph.traversal().withComputer(); //GraphTraversalSource.computer());
+        GraphTraversalSource g = graph.traversal().withComputer();//GraphTraversalSource.computer());
         //System.out.println(g.V().outE("knows").identity().inV().count().is(P.eq(5)).explain());
         //System.out.println(g.V().hasLabel("person").fold().order(Scope.local).by("age").toList());
-        final Traversal<?,?> traversal = g.V().union(out("knows"), out("created").in("created")).groupCount().select(Column.values).unfold().sum(); // unfold.select(values)
+        final Traversal<?,?> traversal = g.V().both().groupCount("a").out().cap("a").select(Column.keys).unfold().both().groupCount("a").cap("a"); // unfold.select(values)    [{v[1]=6, v[2]=2, v[3]=6, v[4]=6, v[5]=2, v[6]=2}]
 
         System.out.println(traversal.asAdmin().clone().toString());
         final Traversal<?,?> clone = traversal.asAdmin().clone();