You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/06/13 17:46:09 UTC

[tinkerpop] 01/01: TINKERPOP-2014 SeedStrategy to specify the seed for traversal randoms

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2014
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit d5508e17812893dca3ea01d18d145f453f2fcc57
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Thu Jun 11 12:33:53 2020 -0400

    TINKERPOP-2014 SeedStrategy to specify the seed for traversal randoms
---
 CHANGELOG.asciidoc                                 |   1 +
 docs/src/reference/the-traversal.asciidoc          |  38 ++++++++
 docs/src/upgrade/release-3.5.x.asciidoc            |  22 +++++
 .../tinkerpop/gremlin/process/traversal/Order.java |   6 +-
 .../gremlin/process/traversal/step/Seedable.java   |  32 +++++++
 .../process/traversal/step/filter/CoinStep.java    |  36 ++-----
 .../traversal/step/filter/SampleGlobalStep.java    |  12 ++-
 .../traversal/step/map/OrderGlobalStep.java        |  24 ++++-
 .../process/traversal/step/map/OrderLocalStep.java |  22 +++--
 .../traversal/step/map/SampleLocalStep.java        |  14 ++-
 .../strategy/decoration/SeedStrategy.java          |  79 ++++++++++++++++
 .../traversal/traverser/util/TraverserSet.java     |   6 +-
 .../structure/io/graphson/GraphSONModule.java      |   5 +
 .../gremlin/structure/io/gryo/GryoVersion.java     |   7 +-
 .../traverser/util/IndexedTraverserSetTest.java    |   3 +-
 .../Traversal/Strategy/Decoration/SeedStrategy.cs  |  51 ++++++++++
 .../DriverRemoteConnection/GraphTraversalTests.cs  |  14 +++
 .../python/gremlin_python/process/strategies.py    |   6 ++
 .../tests/driver/test_driver_remote_connection.py  |  10 +-
 .../gremlin/process/ProcessComputerSuite.java      |   2 +
 .../gremlin/process/ProcessStandardSuite.java      |   2 +
 .../decoration/SeedStrategyProcessTest.java        | 105 +++++++++++++++++++++
 22 files changed, 442 insertions(+), 55 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 33ede9d..abe9b78 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Allowed `property(T.label,Object)` to be used if no value was supplied to `addV(String)`.
 * Added a `Graph.Feature` for `supportsNullPropertyValues`.
 * Modified `TokenTraversal` to support `Property` thus `by(key)` and `by(value)` can now apply to `Edge` and meta-properties.
+* Added `SeedStrategy` to allow deterministic behavior for `coin()`, `sample()` and `Order.shuffle`.
 * Added `Grouping` step interface.
 * Added `TraversalParent.replaceTraversal()` which can replace a direct child traversal.
 * Added `ByModulatorOptimizationStrategy` which replaces certain standard traversals w/ optimized traversals (e.g. `TokenTraversal`).
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 6b6f965..297d65c 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -4075,6 +4075,44 @@ Type ':help' or ':h' for help.
 Display stack trace? [yN]
 ----
 
+=== SeedStrategy
+
+There are number of components of the Gremlin language that, by design, can produce non-deterministic results:
+
+* <<coin-step,coin()>>
+* <<order-step,order()>> when `Order.shuffle` is used
+* <<sample-step,sample()>>
+
+To get these steps to return deterministic results, `SeedStrategy` allows assignment of a seed value to the `Random`
+operations of the steps. The following example demonstrates the random nature of `shuffle`:
+
+[gremlin-groovy,modern]
+----
+g.V().values('name').fold().order(local).by(shuffle)
+g.V().values('name').fold().order(local).by(shuffle)
+g.V().values('name').fold().order(local).by(shuffle)
+g.V().values('name').fold().order(local).by(shuffle)
+g.V().values('name').fold().order(local).by(shuffle)
+----
+
+With `SeedStrategy` in place, however, the same order is applied each time:
+
+[gremlin-groovy,modern]
+----
+seedStrategy = new SeedStrategy(999998L)
+g.withStategy(seedStrategy).V().values('name').fold().order(local).by(shuffle)
+g.withStategy(seedStrategy).V().values('name').fold().order(local).by(shuffle)
+g.withStategy(seedStrategy).V().values('name').fold().order(local).by(shuffle)
+g.withStategy(seedStrategy).V().values('name').fold().order(local).by(shuffle)
+g.withStategy(seedStrategy).V().values('name').fold().order(local).by(shuffle)
+----
+
+IMPORTANT: `SeedStrategy` only makes specific steps behave in a deterministic fashion and does not necessarily make
+the entire traversal deterministic itself. If the underlying graph database or processing engine happens to not
+guarantee iteration order, then it is possible that the final result of the traversal will appear to be
+non-deterministic. In these cases, it would be necessary to enforce a deterministic iteration with `order()` prior to
+these steps that make use of randomness to return results.
+
 === SubgraphStrategy
 
 `SubgraphStrategy` is similar to `PartitionStrategy` in that it constrains a `Traversal` to certain vertices, edges,
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 56b7757..03bbe7d 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -235,6 +235,28 @@ be replaced by `by(id)`, thus replacing a step-based traversal with a token-base
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-1682[TINKERPOP-1682]
 
+==== SeedStrategy
+
+The new `SeedStrategy` allows the user to set a seed value for steps that make use of `Random` so that the traversal
+has the ability to return deterministic results. While this feature is useful for testing and debugging purposes,
+there are also some practical applications as well.
+
+[source,text]
+----
+gremlin> g.V().values('name').fold().order(local).by(shuffle)
+==>[josh,marko,vadas,peter,ripple,lop]
+gremlin> g.V().values('name').fold().order(local).by(shuffle)
+==>[vadas,lop,marko,peter,josh,ripple]
+gremlin> g.V().values('name').fold().order(local).by(shuffle)
+==>[peter,ripple,josh,lop,marko,vadas]
+gremlin> g.withStrategies(new SeedStrategy(22323)).V().values('name').fold().order(local).by(shuffle)
+==>[lop,peter,josh,marko,vadas,ripple]
+gremlin> g.withStrategies(new SeedStrategy(22323)).V().values('name').fold().order(local).by(shuffle)
+==>[lop,peter,josh,marko,vadas,ripple]
+gremlin> g.withStrategies(new SeedStrategy(22323)).V().values('name').fold().order(local).by(shuffle)
+==>[lop,peter,josh,marko,vadas,ripple]
+----
+
 ==== by(T) for Property
 
 The `Property` interface is not included in the hierarchy of `Element`. This means that an edge property or a
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java
index 1616898..d4d55b0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java
@@ -32,14 +32,16 @@ import org.apache.tinkerpop.gremlin.util.NumberHelper;
 public enum Order implements Comparator<Object> {
 
     /**
-     * Order in a random fashion.
+     * Order in a random fashion. While this enum implements {@code Comparator}, the {@code compare(a,b)} method is not
+     * supported as a direct call. This change to the implementation of {@code compare(a,b)} occurred at 3.5.0 but
+     * this implementation was never used directly within the TinkerPop code base.
      *
      * @since 3.0.0-incubating
      */
     shuffle {
         @Override
         public int compare(final Object first, final Object second) {
-            return RANDOM.nextBoolean() ? -1 : 1;
+            throw new UnsupportedOperationException("Order.shuffle should not be used as an actual Comparator - it is a marker only");
         }
 
         @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Seedable.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Seedable.java
new file mode 100644
index 0000000..29d1cb5
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Seedable.java
@@ -0,0 +1,32 @@
+/*
+ * 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.process.traversal.step;
+
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
+
+/**
+ * An interface implemented by steps that have some form of {@code Random} usage that can be configured by way of a
+ * {@code seed} to allow for deterministic behavior of the step.
+ *
+ * @see SeedStrategy
+ */
+public interface Seedable {
+
+    public void resetSeed(final long seed);
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
index db2480d..a7d86cb 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
@@ -30,9 +31,9 @@ import java.util.Set;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class CoinStep<S> extends FilterStep<S> {
+public final class CoinStep<S> extends FilterStep<S> implements Seedable {
 
-    private static final Random RANDOM = new Random();
+    private final Random random = new Random();
     private final double probability;
 
     public CoinStep(final Traversal.Admin traversal, final double probability) {
@@ -41,19 +42,18 @@ public final class CoinStep<S> extends FilterStep<S> {
     }
 
     @Override
+    public void resetSeed(final long seed) {
+        random.setSeed(seed);
+    }
+
+    @Override
     protected boolean filter(final Traverser.Admin<S> traverser) {
         long newBulk = 0l;
         if (traverser.bulk() < 100) {
             for (int i = 0; i < traverser.bulk(); i++) {
-                if (this.probability >= RANDOM.nextDouble())
+                if (this.probability >= random.nextDouble())
                     newBulk++;
             }
-        /*} else if (traverser.bulk() < 1000000) {
-            final double cumulative = RANDOM.nextDouble();
-            final long current = Double.valueOf(traverser.bulk() / 2.0d).longValue();
-            final double next = choose(traverser.bulk(), current) * Math.pow(this.probability,current) * Math.pow(1.0d - this.probability,traverser.bulk() - current);
-            if()
-            */
         } else {
             newBulk = Math.round(this.probability * traverser.bulk());
         }
@@ -76,22 +76,4 @@ public final class CoinStep<S> extends FilterStep<S> {
     public Set<TraverserRequirement> getRequirements() {
         return Collections.singleton(TraverserRequirement.BULK);
     }
-
-    //////
-
-    private static double choose(long x, long y) {
-        if (y < 0 || y > x) return 0;
-        if (y > x / 2) {
-            // choose(n,k) == choose(n,n-k),
-            // so this could save a little effort
-            y = x - y;
-        }
-
-        double denominator = 1.0, numerator = 1.0;
-        for (long i = 1; i <= y; i++) {
-            denominator *= i;
-            numerator *= (x + 1 - i);
-        }
-        return numerator / denominator;
-    }
 }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
index fe8fb03..e0f772a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/SampleGlobalStep.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
@@ -38,11 +39,11 @@ import java.util.Set;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implements TraversalParent, ByModulating {
+public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implements TraversalParent, ByModulating, Seedable {
 
     private Traversal.Admin<S, Number> probabilityTraversal = new ConstantTraversal<>(1.0d);
     private final int amountToSample;
-    private static final Random RANDOM = new Random();
+    private final Random random = new Random();
 
     public SampleGlobalStep(final Traversal.Admin traversal, final int amountToSample) {
         super(traversal);
@@ -50,6 +51,11 @@ public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implemen
     }
 
     @Override
+    public void resetSeed(final long seed) {
+        random.setSeed(seed);
+    }
+
+    @Override
     public List<Traversal.Admin<S, Number>> getLocalChildren() {
         return Collections.singletonList(this.probabilityTraversal);
     }
@@ -99,7 +105,7 @@ public final class SampleGlobalStep<S> extends CollectingBarrierStep<S> implemen
                     final double currentWeight = ((ProjectedTraverser<S, Number>) s).getProjections().get(0).doubleValue();
                     for (int i = 0; i < (s.bulk() - sampleBulk); i++) {
                         runningWeight = runningWeight + currentWeight;
-                        if (RANDOM.nextDouble() <= ((runningWeight / totalWeight))) {
+                        if (random.nextDouble() <= ((runningWeight / totalWeight))) {
                             final Traverser.Admin<S> split = s.split();
                             split.setBulk(1L);
                             sampledSet.add(split);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
index 1780139..e255fc6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ComparatorHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrierStep;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
@@ -41,6 +42,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 import java.util.function.BinaryOperator;
 import java.util.stream.Collectors;
@@ -48,22 +50,28 @@ import java.util.stream.Collectors;
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBarrierStep<S> implements ComparatorHolder<S, C>, TraversalParent, ByModulating {
+public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBarrierStep<S> implements ComparatorHolder<S, C>, TraversalParent, ByModulating, Seedable {
 
     private List<Pair<Traversal.Admin<S, C>, Comparator<C>>> comparators = new ArrayList<>();
     private MultiComparator<C> multiComparator = null;
     private long limit = Long.MAX_VALUE;
+    private final Random random = new Random();
 
     public OrderGlobalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
+    public void resetSeed(long seed) {
+        this.random.setSeed(seed);
+    }
+
+    @Override
     public void barrierConsumer(final TraverserSet<S> traverserSet) {
         if (null == this.multiComparator) this.multiComparator = this.createMultiComparator();
         //
         if (this.multiComparator.isShuffle())
-            traverserSet.shuffle();
+            traverserSet.shuffle(random);
         else
             traverserSet.sort((Comparator) this.multiComparator);
     }
@@ -159,10 +167,14 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
     @Override
     public MemoryComputeKey<TraverserSet<S>> getMemoryComputeKey() {
         if (null == this.multiComparator) this.multiComparator = this.createMultiComparator();
-        return MemoryComputeKey.of(this.getId(), new OrderBiOperator<>(this.limit, this.multiComparator), false, true);
+        return MemoryComputeKey.of(this.getId(), new OrderBiOperator<>(this.limit, this.multiComparator, this.random), false, true);
     }
 
     private final ProjectedTraverser<S, Object> createProjectedTraverser(final Traverser.Admin<S> traverser) {
+        // this was ProjectedTraverser<S, C> but the projection may not be C in the case of a lambda where a
+        // Comparable may not be expected but rather an object that can be compared in any way given a lambda.
+        // not sure why this is suddenly an issue but Intellij would not let certain tests pass without this
+        // adjustment here.
         final List<Object> projections = new ArrayList<>(this.comparators.size());
         for (final Pair<Traversal.Admin<S, C>, Comparator<C>> pair : this.comparators) {
             projections.add(TraversalUtil.apply(traverser, pair.getValue0()));
@@ -184,14 +196,16 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
 
         private long limit;
         private MultiComparator comparator;
+        private Random random;
 
         private OrderBiOperator() {
             // for serializers that need a no-arg constructor
         }
 
-        public OrderBiOperator(final long limit, final MultiComparator multiComparator) {
+        public OrderBiOperator(final long limit, final MultiComparator multiComparator, final Random random) {
             this.limit = limit;
             this.comparator = multiComparator;
+            this.random = random;
         }
 
         @Override
@@ -199,7 +213,7 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
             setA.addAll(setB);
             if (this.limit != -1 && setA.bulkSize() > this.limit) {
                 if (this.comparator.isShuffle())
-                    setA.shuffle();
+                    setA.shuffle(random);
                 else
                     setA.sort(this.comparator);
                 long counter = 0L;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
index 6597418..eb3a5f8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderLocalStep.java
@@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.ComparatorHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -37,30 +38,37 @@ import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep<S, S> implements ComparatorHolder<S, C>, ByModulating, TraversalParent {
+public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep<S, S> implements ComparatorHolder<S, C>, ByModulating, TraversalParent, Seedable {
 
     private List<Pair<Traversal.Admin<S, C>, Comparator<C>>> comparators = new ArrayList<>();
     private ChainedComparator<S, C> chainedComparator = null;
+    private final Random random = new Random();
 
     public OrderLocalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
+    public void resetSeed(long seed) {
+        this.random.setSeed(seed);
+    }
+
+    @Override
     protected S map(final Traverser.Admin<S> traverser) {
         if (null == this.chainedComparator)
             this.chainedComparator = new ChainedComparator<>(false, this.comparators);
         final S start = traverser.get();
         if (start instanceof Collection)
-            return (S) OrderLocalStep.sortCollection((Collection) start, this.chainedComparator);
+            return (S) sortCollection((Collection) start, this.chainedComparator);
         else if (start instanceof Map)
-            return (S) OrderLocalStep.sortMap((Map) start, this.chainedComparator);
+            return (S) sortMap((Map) start, this.chainedComparator);
         else
             return start;
     }
@@ -142,10 +150,10 @@ public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep
 
     /////////////
 
-    private static final <A> List<A> sortCollection(final Collection<A> collection, final ChainedComparator comparator) {
+    private <A> List<A> sortCollection(final Collection<A> collection, final ChainedComparator comparator) {
         if (collection instanceof List) {
             if (comparator.isShuffle())
-                Collections.shuffle((List) collection);
+                Collections.shuffle((List) collection, random);
             else
                 Collections.sort((List) collection, comparator);
             return (List<A>) collection;
@@ -154,10 +162,10 @@ public final class OrderLocalStep<S, C extends Comparable> extends ScalarMapStep
         }
     }
 
-    private static final <K, V> Map<K, V> sortMap(final Map<K, V> map, final ChainedComparator comparator) {
+    private <K, V> Map<K, V> sortMap(final Map<K, V> map, final ChainedComparator comparator) {
         final List<Map.Entry<K, V>> entries = new ArrayList<>(map.entrySet());
         if (comparator.isShuffle())
-            Collections.shuffle(entries);
+            Collections.shuffle(entries, random);
         else
             Collections.sort(entries, comparator);
         final LinkedHashMap<K, V> sortedMap = new LinkedHashMap<>();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SampleLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SampleLocalStep.java
index ef65202..052ca15 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SampleLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SampleLocalStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 
 import java.util.ArrayList;
@@ -36,9 +37,9 @@ import java.util.Set;
  * @author Daniel Kuppitz (http://gremlin.guru)
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class SampleLocalStep<S> extends ScalarMapStep<S, S> {
+public final class SampleLocalStep<S> extends ScalarMapStep<S, S> implements Seedable {
 
-    private static final Random RANDOM = new Random();
+    private final Random random = new Random();
 
     private final int amountToSample;
 
@@ -48,6 +49,11 @@ public final class SampleLocalStep<S> extends ScalarMapStep<S, S> {
     }
 
     @Override
+    public void resetSeed(final long seed) {
+        this.random.setSeed(seed);
+    }
+
+    @Override
     protected S map(final Traverser.Admin<S> traverser) {
         final S start = traverser.get();
         if (start instanceof Map) {
@@ -66,7 +72,7 @@ public final class SampleLocalStep<S> extends ScalarMapStep<S, S> {
         final List<S> original = new ArrayList<>(collection);
         final List<S> target = new ArrayList<>();
         while (target.size() < this.amountToSample) {
-            target.add(original.remove(RANDOM.nextInt(original.size())));
+            target.add(original.remove(random.nextInt(original.size())));
         }
         return (S) target;
     }
@@ -78,7 +84,7 @@ public final class SampleLocalStep<S> extends ScalarMapStep<S, S> {
         final List<Map.Entry> original = new ArrayList<>(map.entrySet());
         final Map target = new LinkedHashMap<>(this.amountToSample);
         while (target.size() < this.amountToSample) {
-            final Map.Entry entry = original.remove(RANDOM.nextInt(original.size()));
+            final Map.Entry entry = original.remove(random.nextInt(original.size()));
             target.put(entry.getKey(), entry.getValue());
         }
         return (S) target;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategy.java
new file mode 100644
index 0000000..dbf9324
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategy.java
@@ -0,0 +1,79 @@
+/*
+ * 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.process.traversal.strategy.decoration;
+
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A strategy that resets the specified {@code seed} value for {@link Seedable} steps, which in turn will produce
+ * deterministic results from those steps. It is important to note that when using this strategy that it only
+ * guarantees deterministic results from a step but not from an entire traversal. For example, if a graph does no
+ * guarantee iteration order for {@code g.V()} then repeated runs of {@code g.V().coin(0.5)} with this strategy
+ * will return the same number of results but not necessarily the same ones. The same problem can occur in OLAP-based
+ * traversals where iteration order is not explicitly guaranteed. The only way to ensure completely deterministic
+ * results in that sense is to apply some form of {@code order()} in these cases
+ */
+public class SeedStrategy extends AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+        implements TraversalStrategy.DecorationStrategy {
+
+    private final long seed;
+
+    public SeedStrategy(final long seed) {
+        this.seed = seed;
+    }
+
+    public long getSeed() {
+        return seed;
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        final List<Seedable> seedableSteps = TraversalHelper.getStepsOfAssignableClass(Seedable.class, traversal);
+        for (final Seedable seedableStepsToReset : seedableSteps) {
+            seedableStepsToReset.resetSeed(seed);
+        }
+    }
+
+    public static final String ID_SEED = "seed";
+
+    public static SeedStrategy create(final Configuration configuration) {
+        if (!configuration.containsKey(ID_SEED))
+            throw new IllegalArgumentException("SeedStrategy configuration requires a 'seed' value");
+
+        return new SeedStrategy(Long.parseLong(configuration.getProperty(ID_SEED).toString()));
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+        final Map<String, Object> map = new HashMap<>();
+        map.put(STRATEGY, SeedStrategy.class.getCanonicalName());
+        map.put(ID_SEED, this.seed);
+        return new MapConfiguration(map);
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
index 52bf4d8..e25bbb1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
@@ -32,6 +32,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.Random;
 import java.util.Set;
 import java.util.Spliterator;
 
@@ -41,6 +42,7 @@ import java.util.Spliterator;
 public class TraverserSet<S> extends AbstractSet<Traverser.Admin<S>> implements Set<Traverser.Admin<S>>, Queue<Traverser.Admin<S>>, Serializable {
 
     private final Map<Traverser.Admin<S>, Traverser.Admin<S>> map = Collections.synchronizedMap(new LinkedHashMap<>());
+    private static final Random RAND = new Random();
 
     public TraverserSet() {
 
@@ -153,10 +155,10 @@ public class TraverserSet<S> extends AbstractSet<Traverser.Admin<S>> implements
         list.forEach(traverser -> this.map.put(traverser, traverser));
     }
 
-    public void shuffle() {
+    public void shuffle(final Random random) {
         final List<Traverser.Admin<S>> list = new ArrayList<>(this.map.size());
         IteratorUtils.removeOnNext(this.map.values().iterator()).forEachRemaining(list::add);
-        Collections.shuffle(list);
+        Collections.shuffle(list, random);
         this.map.clear();
         list.forEach(traverser -> this.map.put(traverser, traverser));
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index 5d3c806..f51b34c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -41,6 +41,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventS
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
@@ -186,6 +187,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             HaltedTraverserStrategy.class,
                             PartitionStrategy.class,
                             SubgraphStrategy.class,
+                            SeedStrategy.class,
                             LazyBarrierStrategy.class,
                             MatchAlgorithmStrategy.class,
                             AdjacentToIncidentStrategy.class,
@@ -313,6 +315,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     HaltedTraverserStrategy.class,
                     PartitionStrategy.class,
                     SubgraphStrategy.class,
+                    SeedStrategy.class,
                     LazyBarrierStrategy.class,
                     MatchAlgorithmStrategy.class,
                     AdjacentToIncidentStrategy.class,
@@ -420,6 +423,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             HaltedTraverserStrategy.class,
                             PartitionStrategy.class,
                             SubgraphStrategy.class,
+                            SeedStrategy.class,
                             LazyBarrierStrategy.class,
                             MatchAlgorithmStrategy.class,
                             AdjacentToIncidentStrategy.class,
@@ -537,6 +541,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     HaltedTraverserStrategy.class,
                     PartitionStrategy.class,
                     SubgraphStrategy.class,
+                    SeedStrategy.class,
                     LazyBarrierStrategy.class,
                     MatchAlgorithmStrategy.class,
                     AdjacentToIncidentStrategy.class,
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index 41e6e27..eb9566e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -50,6 +50,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.Connec
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
@@ -334,11 +335,12 @@ public enum GryoVersion {
             add(GryoTypeReg.of(HaltedTraverserStrategy.class, 139));
             add(GryoTypeReg.of(PartitionStrategy.class, 140, new JavaSerializer()));
             add(GryoTypeReg.of(SubgraphStrategy.class, 141, new JavaSerializer()));
+            add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer()));          // ***LAST ID***
             add(GryoTypeReg.of(VertexProgramStrategy.class, 142, new JavaSerializer()));
             add(GryoTypeReg.of(MatchAlgorithmStrategy.class, 143));
             add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 144));
             add(GryoTypeReg.of(AdjacentToIncidentStrategy.class, 145));
-            add(GryoTypeReg.of(ByModulatorOptimizationStrategy.class, 191));  // ***LAST ID***
+            add(GryoTypeReg.of(ByModulatorOptimizationStrategy.class, 191));
             add(GryoTypeReg.of(CountStrategy.class, 155));
             add(GryoTypeReg.of(FilterRankingStrategy.class, 146));
             add(GryoTypeReg.of(IdentityRemovalStrategy.class, 147));
@@ -572,11 +574,12 @@ public enum GryoVersion {
             add(GryoTypeReg.of(HaltedTraverserStrategy.class, 139));
             add(GryoTypeReg.of(PartitionStrategy.class, 140, new JavaSerializer()));
             add(GryoTypeReg.of(SubgraphStrategy.class, 141, new JavaSerializer()));
+            add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer()));          // ***LAST ID***
             add(GryoTypeReg.of(VertexProgramStrategy.class, 142, new JavaSerializer()));
             add(GryoTypeReg.of(MatchAlgorithmStrategy.class, 143));
             add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 144));
             add(GryoTypeReg.of(AdjacentToIncidentStrategy.class, 145));
-            add(GryoTypeReg.of(ByModulatorOptimizationStrategy.class, 191)); // ***LAST ID***
+            add(GryoTypeReg.of(ByModulatorOptimizationStrategy.class, 191));
             add(GryoTypeReg.of(CountStrategy.class, 155));
             add(GryoTypeReg.of(FilterRankingStrategy.class, 146));
             add(GryoTypeReg.of(IdentityRemovalStrategy.class, 147));
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/IndexedTraverserSetTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/IndexedTraverserSetTest.java
index 0d765f4..6f0b490 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/IndexedTraverserSetTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/IndexedTraverserSetTest.java
@@ -26,6 +26,7 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Random;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNull.nullValue;
@@ -111,7 +112,7 @@ public class IndexedTraverserSetTest {
         assertEquals(1, nopeTraversers.size());
         assertEquals(1, nopeTraversers.get(0).bulk());
 
-        ts.shuffle();
+        ts.shuffle(new Random());
 
         final List<Traverser.Admin<String>> testTraversersAfterShuffle = new ArrayList<>(ts.get("test"));
         assertEquals(1, testTraversersAfterShuffle.size());
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Strategy/Decoration/SeedStrategy.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Strategy/Decoration/SeedStrategy.cs
new file mode 100644
index 0000000..bf531f5
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Strategy/Decoration/SeedStrategy.cs
@@ -0,0 +1,51 @@
+#region License
+
+/*
+ * 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.
+ */
+
+#endregion
+
+using System.Collections.Generic;
+
+namespace Gremlin.Net.Process.Traversal.Strategy.Decoration
+{
+    /// <summary>
+    ///     A strategy that resets the specified {@code seed} value for Seedable steps like coin(), sample()
+    ///     and Order.shuffle, which in turn will produce deterministic results from those steps.
+    /// </summary>
+    public class SeedStrategy : AbstractTraversalStrategy
+    {
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="OptionsStrategy" /> class.
+        /// </summary>
+        public SeedStrategy()
+        {
+        }
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="SeedStrategy" /> class.
+        /// </summary>
+        /// <param name="seed">Specifies the seed the traversal will use.</param>
+        public SeedStrategy(long seed)
+        {
+            Configuration["seed"] = seed;
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
index 0f6bea6..5e11e33 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
@@ -220,6 +220,20 @@ namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection
         }
 
         [Fact]
+        public void ShouldUseSeedStrategyToReturnDeterministicResults()
+        {
+            var connection = _connectionFactory.CreateRemoteConnection();
+            var g = AnonymousTraversalSource.Traversal().WithRemote(connection).WithStrategies(new SeedStrategy(664664));
+
+            var shuffledResults = g.V().Values<string>("name").Order().By(Order.Shuffle).ToList();
+            Assert.Equal(shuffledResults, g.V().Values<string>("name").Order().By(Order.Shuffle).ToList());
+            Assert.Equal(shuffledResults, g.V().Values<string>("name").Order().By(Order.Shuffle).ToList());
+            Assert.Equal(shuffledResults, g.V().Values<string>("name").Order().By(Order.Shuffle).ToList());
+            Assert.Equal(shuffledResults, g.V().Values<string>("name").Order().By(Order.Shuffle).ToList());
+            Assert.Equal(shuffledResults, g.V().Values<string>("name").Order().By(Order.Shuffle).ToList());
+        }
+
+        [Fact]
         public async Task ShouldExecuteAsynchronouslyWhenPromiseIsCalled()
         {
             var connection = _connectionFactory.CreateRemoteConnection();
diff --git a/gremlin-python/src/main/python/gremlin_python/process/strategies.py b/gremlin-python/src/main/python/gremlin_python/process/strategies.py
index d483c05..c2ee615 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/strategies.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/strategies.py
@@ -63,6 +63,12 @@ class PartitionStrategy(TraversalStrategy):
             self.configuration["includeMetaProperties"] = include_meta_properties
 
 
+class SeedStrategy(TraversalStrategy):
+    def __init__(self, seed):
+        TraversalStrategy.__init__(self, fqcn="org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy")
+        self.configuration["seed"] = seed
+
+
 class SubgraphStrategy(TraversalStrategy):
 
     def __init__(self, vertices=None, edges=None, vertex_properties=None):
diff --git a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
index 1350b1d..663858e 100644
--- a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
+++ b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
@@ -28,11 +28,11 @@ from gremlin_python.driver.driver_remote_connection import (
 from gremlin_python.process.traversal import Traverser
 from gremlin_python.process.traversal import TraversalStrategy
 from gremlin_python.process.traversal import Bindings
-from gremlin_python.process.traversal import P
+from gremlin_python.process.traversal import P, Order
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.process.anonymous_traversal import traversal
 from gremlin_python.structure.graph import Vertex
-from gremlin_python.process.strategies import SubgraphStrategy, ReservedKeysVerificationStrategy
+from gremlin_python.process.strategies import SubgraphStrategy, ReservedKeysVerificationStrategy, SeedStrategy
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 
@@ -187,6 +187,12 @@ class TestDriverRemoteConnection(object):
         assert 6 == g.V().count().next()
         assert 6 == g.E().count().next()
         #
+        g = traversal().withRemote(remote_connection).withStrategies(SeedStrategy(12345))
+        shuffledResult = g.V().values("name").order().by(Order.shuffle).toList()
+        assert shuffledResult == g.V().values("name").order().by(Order.shuffle).toList()
+        assert shuffledResult == g.V().values("name").order().by(Order.shuffle).toList()
+        assert shuffledResult == g.V().values("name").order().by(Order.shuffle).toList()
+        #
         g = traversal().withRemote(remote_connection). \
             withStrategies(ReservedKeysVerificationStrategy(throw_exception=True))
         try:
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
index b1ee6d2..8785e90 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessComputerSuite.java
@@ -89,6 +89,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SideEffect
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StoreTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeTest;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategyProcessTest;
@@ -206,6 +207,7 @@ public class ProcessComputerSuite extends AbstractGremlinSuite {
 
             // decorations
             ReadOnlyStrategyProcessTest.class,
+            SeedStrategyProcessTest.class,
             SubgraphStrategyProcessTest.class,
 
             // optimizations
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index f127ca0..2f31678 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -86,6 +86,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategyProcessTest;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategyProcessTest;
@@ -193,6 +194,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             EventStrategyProcessTest.class,
             ReadOnlyStrategyProcessTest.class,
             PartitionStrategyProcessTest.class,
+            SeedStrategyProcessTest.class,
             SubgraphStrategyProcessTest.class,
 
             // optimizations
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategyProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategyProcessTest.java
new file mode 100644
index 0000000..77aaf0a
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SeedStrategyProcessTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.process.traversal.strategy.decoration;
+
+import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
+import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.GRATEFUL;
+import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.apache.tinkerpop.gremlin.process.traversal.Order.shuffle;
+import static org.apache.tinkerpop.gremlin.process.traversal.Scope.local;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(GremlinProcessRunner.class)
+public class SeedStrategyProcessTest extends AbstractGremlinProcessTest {
+    private static final SeedStrategy seedStrategy = new SeedStrategy(1235L);
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void shouldSeedCoin() {
+        final GraphTraversalSource gSeeded = create();
+        final List<Object> names = gSeeded.V().order().by("name").values("name").coin(0.31).order().toList();
+        repeatAssert(() -> {
+            assertEquals(names, gSeeded.V().order().by("name").values("name").coin(0.31).order().toList());
+            return null;
+        });
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void shouldSeedGlobalOrderShuffle() {
+        final GraphTraversalSource gSeeded = create();
+        final List<Object> names = gSeeded.V().order().by("name").values("name").order().by(shuffle).toList();
+        repeatAssert(() -> {
+            assertEquals(names, gSeeded.V().order().by("name").values("name").order().by(shuffle).toList());
+            return null;
+        });
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void shouldSeedLocalOrderShuffle() {
+        final GraphTraversalSource gSeeded = create();
+        final List<Object> names = gSeeded.V().order().by("name").values("name").fold().order(local).by(shuffle).next();
+        repeatAssert(() -> {
+            assertEquals(names, gSeeded.V().order().by("name").values("name").fold().order(local).by(shuffle).next());
+            return null;
+        });
+    }
+
+    @Test
+    @LoadGraphWith(GRATEFUL)
+    public void shouldSeedGlobalSample() {
+        final GraphTraversalSource gSeeded = create();
+        final List<Object> names = gSeeded.V().order().by("name").values("name").sample(20).toList();
+        repeatAssert(() -> {
+            assertEquals(names, gSeeded.V().order().by("name").values("name").sample(20).toList());
+            return null;
+        });
+    }
+
+    @Test
+    @LoadGraphWith(GRATEFUL)
+    public void shouldSeedLocalSample() {
+        final GraphTraversalSource gSeeded = create();
+        final List<Object> names = gSeeded.V().order().by("name").values("name").fold().sample(local,20).next();
+        repeatAssert(() -> {
+            assertEquals(names, gSeeded.V().order().by("name").values("name").fold().sample(local,20).next());
+            return null;
+        });
+    }
+
+    private void repeatAssert(final Supplier<Void> assertion) {
+        for (int ix = 0; ix < 128; ix++) {
+            assertion.get();
+        }
+    }
+
+    private GraphTraversalSource create() {
+        return graphProvider.traversal(graph, seedStrategy);
+    }
+}