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

kenhuuu pushed a commit to branch ken/txtests
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit e3215405ee56f2f0c80a086f6c6e2c8d0e42d064
Author: Ken Hu <[email protected]>
AuthorDate: Thu Jul 27 09:11:27 2023 -0700

    add tests
---
 .../structure/TransactionMultiThreadedTest.java    |  30 ++
 .../structure/TinkerTransactionGraphTest.java      | 343 +++++++++++++++++++++
 2 files changed, 373 insertions(+)

diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/TransactionMultiThreadedTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/TransactionMultiThreadedTest.java
index ee57728571..ef3e3f1e00 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/TransactionMultiThreadedTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/TransactionMultiThreadedTest.java
@@ -526,6 +526,36 @@ public class TransactionMultiThreadedTest extends 
AbstractGremlinTest {
         countElementsInNewThreadTx(g, 0, 0);
     }
 
+    @Test
+    @FeatureRequirement(featureClass = Graph.Features.GraphFeatures.class, 
feature = FEATURE_TRANSACTIONS)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = FEATURE_ADD_VERTICES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = FEATURE_ADD_PROPERTY)
+    public void 
shouldHandleAddingPropertyWhenOtherTxAttemptsDeleteThenRollsback() throws 
InterruptedException {
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        final Vertex v1 = gtx.addV().next();
+        gtx.tx().commit();
+
+        // tx1 try to add property
+        gtx.V(v1.id()).property("test", 1).iterate();
+
+        // tx2 in same time delete vertex used by tx1
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+            gtx2.V(v1.id()).drop().iterate();
+            gtx2.tx().rollback();
+        });
+        thread.start();
+        thread.join();
+
+        gtx.tx().commit();
+
+        assertEquals(1, (long) gtx.V().count().next());
+        assertEquals(1, gtx.V(v1.id()).values("test").next());
+
+        countElementsInNewThreadTx(g, 1, 0);
+    }
+
     @Test
     @FeatureRequirement(featureClass = Graph.Features.GraphFeatures.class, 
feature = FEATURE_TRANSACTIONS)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, 
feature = FEATURE_ADD_VERTICES)
diff --git 
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraphTest.java
 
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraphTest.java
index 3bc8874fb5..824abe796e 100644
--- 
a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraphTest.java
+++ 
b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransactionGraphTest.java
@@ -18,7 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.tinkergraph.structure;
 
+import org.apache.tinkerpop.gremlin.process.traversal.P;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -26,9 +28,13 @@ import 
org.apache.tinkerpop.gremlin.structure.util.TransactionException;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -731,4 +737,341 @@ public class TinkerTransactionGraphTest {
         thread.start();
         thread.join();
     }
+
+    @Test
+    public void shouldNotAllowDirtyReadsOfVertexForReadOnlyTransaction() 
throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV().next();
+
+        AtomicLong gtx2Read = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2Read.set((long) gtx2.V().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(0, gtx2Read.get());
+    }
+
+    @Test
+    public void shouldNotAllowDirtyReadsOfVertexDropForReadOnlyTransaction() 
throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV().next();
+        gtx.tx().commit();
+
+        gtx.V().drop();
+
+        AtomicLong gtx2Read = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2Read.set((long) gtx2.V().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(1, gtx2Read.get());
+    }
+
+    @Test
+    public void 
shouldNotAllowDirtyReadsOfVertexPropertyForReadOnlyTransaction() throws 
InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV().property("a", 1).iterate();
+        gtx.tx().commit();
+
+        gtx.V().property("b", 2).iterate();
+
+        AtomicLong gtx2Read = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2Read.set((long) gtx2.V().properties().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(1, gtx2Read.get());
+    }
+
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterDeletingVertexForReadOnlyTransaction() 
throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV().iterate();
+        gtx.tx().commit();
+
+        long firstRead = gtx.V().count().next();
+
+        AtomicLong gtx2Read = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.V().drop().iterate();
+            gtx2.tx().commit();
+
+            gtx2Read.set((long) gtx2.V().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(0, gtx2Read.get());
+
+        long secondRead = gtx.V().count().next();
+        assertEquals(firstRead, secondRead);
+    }
+
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterUpdatingVertexPropertyForReadOnlyTransaction()
 throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV("test").property("a", 1).iterate();
+        gtx.tx().commit();
+
+        Object firstRead = gtx.V().values("a").next();
+
+        AtomicLong gtx2Read = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.V().property("a", 2L).iterate();
+            gtx2.tx().commit();
+
+            gtx2Read.set((long) gtx2.V().values("a").next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(2, gtx2Read.get());
+
+        Object secondRead = gtx.V().values("a").next();
+        assertEquals(firstRead, secondRead);
+    }
+
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterDeletingVertexPropertyForReadOnlyTransaction()
 throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV("test").property("a", 1).iterate();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.V().properties().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.V().properties().drop().iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.V().properties().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(0, gtx2Count.get());
+
+        long secondReadCount = gtx.V().properties().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterUpdatingEdgePropertyForReadOnlyTransaction()
 throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        Vertex v1 = gtx.addV().next();
+        Vertex v2 = gtx.addV().next();
+        gtx.addE("test").from(v1).to(v2).property("a", 1).iterate();
+        gtx.tx().commit();
+
+        Object firstReadCount = gtx.E().values("a").next();
+
+        AtomicInteger gtx2Count = new AtomicInteger(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.E().property("a", 2).iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set((int) gtx2.E().values("a").next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(2, gtx2Count.get());
+
+        Object secondReadCount = gtx.E().values("a").next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterDeletingEdgePropertyForReadOnlyTransaction()
 throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        Vertex v1 = gtx.addV().next();
+        Vertex v2 = gtx.addV().next();
+        gtx.addE("test").from(v1).to(v2).property("a", 1).iterate();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.E().properties().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.E().properties().drop().iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set((long) gtx2.E().properties().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(0, gtx2Count.get());
+
+        long secondReadCount = gtx.E().properties().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+    @Test
+    public void 
shouldNotAllowNonRepeatableReadsAfterDeletingEdgeForReadOnlyTransaction() 
throws InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        Vertex v1 = gtx.addV().next();
+        Vertex v2 = gtx.addV().next();
+        gtx.addE("test").from(v1).to(v2).iterate();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.E().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.E().drop().iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.E().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(0, gtx2Count.get());
+
+        long secondReadCount = gtx.E().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    @Test
+    public void 
shouldNotAllowPhantomReadsAfterAddingVertexForReadOnlyTransaction() throws 
InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        gtx.addV().iterate();
+        gtx.addV().iterate();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.V().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.addV().iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.V().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(3, gtx2Count.get());
+
+        long secondReadCount = gtx.V().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    @Test
+    public void 
shouldNotAllowPhantomReadsAfterAddingEdgeForReadOnlyTransaction() throws 
InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        Vertex v1 = gtx.addV().next();
+        Vertex v2 = gtx.addV().next();
+        gtx.addE("test").from(v1).to(v2).id().iterate();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.E().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.addE("test").from(v1).to(v1).iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.E().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(2, gtx2Count.get());
+
+        long secondReadCount = gtx.E().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    @Test
+    public void 
shouldNotAllowPhantomReadsAfterAddingPropertyForReadOnlyTransaction() throws 
InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        Object vId = gtx.addV().property("a", 1).id().next();
+        gtx.tx().commit();
+
+        long firstReadCount = gtx.V(vId).properties().count().next();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.V(vId).property("b", 2).iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.V(vId).properties().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(2, gtx2Count.get());
+
+        long secondReadCount = gtx.V(vId).properties().count().next();
+        assertEquals(firstReadCount, secondReadCount);
+    }
+
+    /**
+     * Test the case to see whether a commit that adds a vertex in another 
transaction will be read by a transaction
+     * that is already opened but hasn't yet started to read. This can 
technically be considered as a type of phantom
+     * read although in most scenarios it doesn't matter. Still, we should 
maintain consistency for this.
+     */
+    @Test
+    public void 
shouldReadCommittedWritesBeforeFirstReadForReadOnlyTransaction() throws 
InterruptedException {
+        final TinkerTransactionGraph g = TinkerTransactionGraph.open();
+        final GraphTraversalSource gtx = g.tx().begin();
+
+        AtomicLong gtx2Count = new AtomicLong(-1);
+        final Thread thread = new Thread(() -> {
+            final GraphTraversalSource gtx2 = g.tx().begin();
+
+            gtx2.addV().iterate();
+            gtx2.tx().commit();
+
+            gtx2Count.set(gtx2.V().count().next());
+        });
+        thread.start();
+        thread.join();
+        assertEquals(1, gtx2Count.get());
+
+        assertEquals(1, (long) gtx.V().count().next());
+    }
 }

Reply via email to