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

colegreer pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/3.7-dev by this push:
     new 818a1b32a2 Fix Bug in TinkerTransactionGraph transaction used counts 
(#2992)
818a1b32a2 is described below

commit 818a1b32a243aff9c3a0eea5f8f520bfb612b685
Author: Cole Greer <[email protected]>
AuthorDate: Wed Jan 29 11:12:57 2025 -0800

    Fix Bug in TinkerTransactionGraph transaction used counts (#2992)
    
    Fixed bug where elements which are read in a transaction end up with 
incorrect usedInTransaction counts which may trigger unintended 
TransactionExceptions
---
 CHANGELOG.asciidoc                                 |  1 +
 .../structure/TinkerElementContainer.java          | 16 +++++++-
 .../tinkergraph/structure/TinkerTransaction.java   |  2 +-
 .../structure/TinkerTransactionGraphTest.java      | 46 ++++++++++++++++++++++
 4 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 79c6ce8c9c..447a9b33e3 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Changed `IdentityRemovalStrategy` to omit `IdentityStep` if only with 
`RepeatEndStep` under `RepeatStep`.
 * Changed Gremlin grammar to make use of `g` to spawn child traversals a 
syntax error.
 * Added `unexpected-response` handler to `ws` for `gremlin-javascript`
+* Fixed bug in `TinkerTransactionGraph` where a read-only transaction may 
leave elements trapped in a "zombie transaction".
 
 [[release-3-7-3]]
 === TinkerPop 3.7.3 (October 23, 2024)
diff --git 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerElementContainer.java
 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerElementContainer.java
index a74337926c..4d7216d21b 100644
--- 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerElementContainer.java
+++ 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerElementContainer.java
@@ -133,10 +133,15 @@ final class TinkerElementContainer<T extends 
TinkerElement> {
     }
 
     /**
-     * Used to understand if the element has deleted in the current transaction
+     * Used to understand if the element has been deleted in the current 
transaction
      */
     public boolean isDeleted() { return isDeleted || isDeletedInTx.get(); }
 
+    /**
+     * Used to understand if the element has been read in the current 
transaction
+     */
+    public boolean isRead() { return isReadInTx.get(); }
+
     /**
      * Mark element as deleted in the current transaction.
      */
@@ -185,6 +190,13 @@ final class TinkerElementContainer<T extends 
TinkerElement> {
                 element != null && updatedValue != null && 
updatedValue.version() != element.version();
     }
 
+    /**
+     * Used to understand if element is in use by any transaction.
+     */
+    public boolean inUse() {
+        return usesInTransactions.get() > 0;
+    }
+
     /**
      * Commit changes for the stored element.
      * @param txVersion version of transaction
@@ -197,7 +209,7 @@ final class TinkerElementContainer<T extends TinkerElement> 
{
                 element.removed = true;
             element = null;
             isDeleted = true;
-        } else {
+        } else if (isModifiedInTx.get()){
             element = transactionUpdatedValue.get();
             element.currentVersion = txVersion;
         }
diff --git 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransaction.java
 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransaction.java
index 077b6adbc0..4e4cde497c 100644
--- 
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransaction.java
+++ 
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerTransaction.java
@@ -203,7 +203,7 @@ final class TinkerTransaction extends 
AbstractThreadLocalTransaction {
 
             final Set<TinkerElementContainer> readElements = 
txReadElements.get();
             if (readElements != null)
-                readElements.stream().forEach(e -> e.reset());
+                readElements.stream().forEach(e -> e.commit(txVersion));
 
             txChangedVertices.remove();
             txChangedEdges.remove();
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 9d240625bb..88b52413b6 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
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -1457,4 +1458,49 @@ public class TinkerTransactionGraphTest {
 
         countElementsInNewThreadTx(g, 1, 0);
     }
+
+    @Test
+    public void shouldHandleSequenceOfCreateReadDeleteCreateSameVertex() {
+        final TinkerTransactionGraph graph = TinkerTransactionGraph.open();
+        final GraphTraversalSource g = graph.traversal();
+
+        g.addV().property(T.id, 1).next();
+        graph.tx().commit();
+        g.V().next();
+        graph.tx().commit();
+        g.V().drop().iterate();
+        graph.tx().commit();
+
+        assertEquals(false, graph.hasVertex(1));
+
+        g.addV().property(T.id, 1).next();
+        graph.tx().commit();
+
+        assertFalse(graph.getVertices().get(1).inUse());
+        assertEquals(true, graph.hasVertex(1));
+    }
+
+    @Test
+    public void shouldHandleCorrectlyHandleCountForChangedAndReadElement() {
+        final TinkerTransactionGraph graph = TinkerTransactionGraph.open();
+        final GraphTraversalSource g = graph.traversal();
+
+        g.addV().property(T.id, 1).next();
+        graph.tx().commit();
+        TinkerElementContainer vertex = graph.getVertices().get(1);
+
+        g.V().next();
+        g.V().property("prop", 5).next();
+        g.V().next();
+        g.V().property("prop2", "foo").next();
+        assertTrue(vertex.isChanged());
+        assertTrue(vertex.isRead());
+        assertTrue(vertex.inUse());
+
+        graph.tx().commit();
+        assertFalse(vertex.isChanged());
+        assertFalse(vertex.isRead());
+
+        assertFalse(vertex.inUse());
+    }
 }

Reply via email to