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());