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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4728b38eaa Add remote transaction upgrade documentation CTR
4728b38eaa is described below

commit 4728b38eaa1b1cc3523add43977416780ad498f2
Author: Ken Hu <[email protected]>
AuthorDate: Fri Mar 27 16:43:57 2026 -0700

    Add remote transaction upgrade documentation CTR
---
 docs/src/upgrade/release-4.x.x.asciidoc            | 51 ++++++++++++++++++++++
 .../GremlinDriverTransactionIntegrateTest.java     | 16 +++----
 .../GremlinServerHttpTransactionIntegrateTest.java | 12 ++---
 3 files changed, 65 insertions(+), 14 deletions(-)

diff --git a/docs/src/upgrade/release-4.x.x.asciidoc 
b/docs/src/upgrade/release-4.x.x.asciidoc
index 33b212c6e6..4b4057797b 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -189,6 +189,57 @@ The WebSocket-specific settings (`TransporterType`, 
`ReadBufferSize`, `WriteBuff
 `NewConnectionThreshold`, `Session`) have been removed. New HTTP connection 
pool settings have been added:
 `MaximumConcurrentConnections`, `MaxIdleConnections`, `IdleConnectionTimeout`, 
and `KeepAliveInterval`.
 
+==== Transactions Re-enabled
+
+IMPORTANT: The remote transaction API is still under active discussion and the 
specification has not been finalized.
+This implementation is experimental and subject to breaking changes in future 
releases. It is provided to enable early
+feedback and testing, but should not be relied upon for production use until 
the specification is complete.
+
+The 4.0.0-beta.1 release disabled transactions and noted that `tx()` would 
return an error. Transactions have now been
+re-enabled over HTTP for both Gremlin Server and the Java driver. The 
transaction model uses server-managed sessions
+where each transaction is identified by a server-generated transaction ID that 
the client passes on subsequent requests.
+
+The basic lifecycle is: begin a transaction, submit one or more traversals 
within it, then commit or rollback. Data
+written inside a transaction is isolated from other clients until committed.
+
+Transactions also work through `DriverRemoteConnection` with the familiar 
`g.tx()` pattern:
+
+[source,java]
+----
+final GraphTraversalSource g = 
traversal().with(DriverRemoteConnection.using(cluster, "g"));
+final GraphTraversalSource gtx = g.tx().begin();
+gtx.addV("person").iterate();
+gtx.tx().commit();
+----
+
+The `RemoteTransaction` can be used directly from `Cluster.transact()`:
+
+[source,java]
+----
+final Cluster cluster = Cluster.open();
+
+// begin, write, and commit
+final RemoteTransaction tx = cluster.transact("g");
+tx.begin();
+tx.submit("g.addV('person').property('name','alice')");
+tx.commit();
+----
+
+The `RemoteTransaction` also supports configurable close behavior. By default, 
`close()` commits the transaction. To
+rollback on close instead:
+
+[source,java]
+----
+final RemoteTransaction tx = cluster.transact("g");
+tx.onClose(Transaction.CLOSE_BEHAVIOR.ROLLBACK);
+tx.begin();
+tx.submit("g.addV('person')");
+tx.close(); // rolls back instead of committing
+----
+
+NOTE: The current implementation creates a dedicated HTTP connection pool per 
transaction. Connection sharing between
+requests will be improved in the final release for better resource efficiency 
and performance.
+
 === Upgrading for Providers
 
 ==== Graph System Providers
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverTransactionIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverTransactionIntegrateTest.java
index 2198db5525..9a88487ddf 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverTransactionIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverTransactionIntegrateTest.java
@@ -103,18 +103,18 @@ public class GremlinDriverTransactionIntegrateTest 
extends AbstractGremlinServer
 
         final RemoteTransaction tx = cluster.transact(GTX);
         tx.begin();
-        // #4: isOpen true after begin
+        // isOpen true after begin
         assertTrue(tx.isOpen());
 
         tx.submit("g.addV('person').property('name','alice')");
 
-        // #6: uncommitted data not visible outside the transaction
+        // uncommitted data not visible outside the transaction
         assertEquals(0L, 
client.submit("g.V().hasLabel('person').count()").one().getLong());
 
         tx.commit();
-        // #4: isOpen false after commit
+        // isOpen false after commit
         assertFalse(tx.isOpen());
-        // #1, #7: committed data visible to non-transactional reads
+        // committed data visible to non-transactional reads
         assertEquals(1L, 
client.submit("g.V().hasLabel('person').count()").one().getLong());
 
         client.close();
@@ -131,9 +131,9 @@ public class GremlinDriverTransactionIntegrateTest extends 
AbstractGremlinServer
         tx.submit("g.addV('person').property('name','bob')");
 
         tx.rollback();
-        // #5: isOpen false after rollback
+        // isOpen false after rollback
         assertFalse(tx.isOpen());
-        // #2: data discarded after rollback
+        // data discarded after rollback
         assertEquals(0L, 
client.submit("g.V().hasLabel('person').count()").one().getLong());
 
         client.close();
@@ -146,7 +146,7 @@ public class GremlinDriverTransactionIntegrateTest extends 
AbstractGremlinServer
         final RemoteTransaction tx = cluster.transact(GTX);
         tx.begin();
         tx.submit("g.addV('test').property('name','A')");
-        // #8: read-your-own-writes — vertex A visible within the transaction
+        // read-your-own-writes — vertex A visible within the transaction
         assertEquals(1L, 
tx.submit("g.V().hasLabel('test').count()").all().get().get(0).getLong());
 
         tx.submit("g.addV('test').property('name','B')");
@@ -158,7 +158,7 @@ public class GremlinDriverTransactionIntegrateTest extends 
AbstractGremlinServer
 
         tx.commit();
 
-        // #3: vertices and edges persist after commit
+        // vertices and edges persist after commit
         assertEquals(2L, 
client.submit("g.V().hasLabel('test').count()").all().get().get(0).getLong());
         assertEquals(1L, 
client.submit("g.E().hasLabel('knows').count()").all().get().get(0).getLong());
     }
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpTransactionIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpTransactionIntegrateTest.java
index fef1241c44..dd61bfa1cc 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpTransactionIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpTransactionIntegrateTest.java
@@ -232,14 +232,14 @@ public class GremlinServerHttpTransactionIntegrateTest 
extends AbstractGremlinSe
             assertEquals(200, r.getStatusLine().getStatusCode());
         }
 
-        // #10: submit traversal on committed tx -> 404
+        // submit traversal on committed tx -> 404
         try (final CloseableHttpResponse r = submitInTx(client, txId, 
"g.V().count()", GTX)) {
             assertEquals(404, r.getStatusLine().getStatusCode());
             final String msg = extractStatusMessage(r);
             assertTrue(msg.contains("Transaction not found"));
         }
 
-        // #13: commit again on committed tx -> 404
+        // commit again on committed tx -> 404
         try (final CloseableHttpResponse r = commitTx(client, txId, GTX)) {
             assertEquals(404, r.getStatusLine().getStatusCode());
             final String msg = extractStatusMessage(r);
@@ -266,14 +266,14 @@ public class GremlinServerHttpTransactionIntegrateTest 
extends AbstractGremlinSe
             assertEquals(200, r.getStatusLine().getStatusCode());
         }
 
-        // #11: submit traversal on rolled-back tx -> 404
+        // submit traversal on rolled-back tx -> 404
         try (final CloseableHttpResponse r = submitInTx(client, txId, 
"g.V().count()", GTX)) {
             final String msg = extractStatusMessage(r);
             assertEquals(404, r.getStatusLine().getStatusCode());
             assertTrue(msg.contains("Transaction not found"));
         }
 
-        // #14: rollback again on rolled-back tx -> 404
+        // rollback again on rolled-back tx -> 404
         try (final CloseableHttpResponse r = rollbackTx(client, txId, GTX)) {
             assertEquals(404, r.getStatusLine().getStatusCode());
             final String msg = extractStatusMessage(r);
@@ -283,12 +283,12 @@ public class GremlinServerHttpTransactionIntegrateTest 
extends AbstractGremlinSe
 
     @Test
     public void shouldReturnValidTransactionId() throws Exception {
-        // #20: begin returns a valid transaction ID
+        // begin returns a valid transaction ID
         final String txId1 = beginTx(client, GTX);
         assertNotNull(txId1);
         assertFalse(txId1.isBlank());
 
-        // #27: second begin returns a different ID
+        // second begin returns a different ID
         final String txId2 = beginTx(client, GTX);
         assertNotNull(txId2);
         assertFalse(txId2.isBlank());

Reply via email to