Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Added typed numeric wrappers and `preciseNumbers` connection option to `gremlin-javascript` for explicit control over numeric type serialization and deserialization.
* Added `NextN(n)` to `Traversal` in `gremlin-go` for batched result iteration, providing API parity with `next(n)` in the Java, Python, and .NET GLVs, and updated the Go translators in `gremlin-core` and `gremlin-javascript` to emit `NextN(n)` for the batched form.
* Added Gremlator, a single page web application, that translates Gremlin into various programming languages like Javascript and Python.
* Added explicit transaction support to all non-Java GLVs (gremlin-python, gremlin-go, gremlin-javascript, gremlin-dotnet).
* Changed default transaction close behavior from commit to rollback across all GLVs to align with embedded graph defaults.
* Refactored Go driver connection to block until response headers arrive, enabling synchronous error returns and proper transaction ordering.
* Removed `uuid` dependency from `gremlin-javascript` in favor of the built-in `globalThis.crypto.randomUUID()`.
* Added streaming HTTP response support to `gremlin-driver` for incremental result deserialization over GraphBinary.
* Connected HTTP streaming response deserialization to the traversal API in `gremlin-javascript`, enabling `next()` to return the first result without waiting for the full response.
Expand Down
156 changes: 145 additions & 11 deletions docs/src/reference/gremlin-variants.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,42 @@ re-construction machine-side.
[[gremlin-go-transactions]]
=== Transactions

IMPORTANT: Transaction support over HTTP is not yet implemented in Gremlin-Go. This will be addressed by the official
4.0.0 release.
Gremlin-Go supports remote transactions over HTTP. Transactions can be created from a `GraphTraversalSource` or
directly from a `Client`.

==== Via GraphTraversalSource

[source,go]
----
g := gremlingo.Traversal_().With(remote)

tx := g.Tx()
gtx, err := tx.Begin()
if err != nil { log.Fatal(err) }

_, err = gtx.AddV("person").Property("name", "alice").Iterate()
if err != nil { tx.Rollback(); log.Fatal(err) }

err = tx.Commit()
if err != nil { log.Fatal(err) }
----

==== Via Client

[source,go]
----
client, _ := gremlingo.NewClient(url, func(settings *gremlingo.ClientSettings) {
settings.TraversalSource = "g"
})

tx := client.Transact()
tx.Begin()
tx.Submit("g.addV('person').property('name','alice')")
tx.Commit()
----

NOTE: Transactions are not thread-safe. All operations within a transaction must be sequential. The default behavior
of `Close()` is to rollback. Transactions are single-use and cannot be reused after commit or rollback.

[[gremlin-go-gvalue]]
=== GValue Parameterization
Expand Down Expand Up @@ -958,15 +992,15 @@ sequentially.

==== Close Behavior

The default behavior of `close()` on a transaction is to commit. This can be changed:
The default behavior of `close()` on a transaction is to rollback. This can be changed:

[source,java]
----
RemoteTransaction tx = cluster.transact("g");
tx.onClose(Transaction.CLOSE_BEHAVIOR.ROLLBACK);
tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT);
tx.begin();
// ... do work ...
tx.close(); // rolls back instead of committing
tx.close(); // commits instead of rolling back
----

[[gremlin-java-serialization]]
Expand Down Expand Up @@ -1756,8 +1790,35 @@ To get a full understanding of this section, it would be good to start by readin
section of this documentation, which discusses transactions in the general context of TinkerPop itself. This section
builds on that content by demonstrating the transactional syntax for Javascript.

IMPORTANT: 4.0.0-beta.2 Release - Transactions are not yet implemented for the HTTP driver. This functionality is
planned for a future release.
Gremlin-Javascript supports remote transactions over HTTP via the Traversal API or the Client API.

==== Via GraphTraversalSource

[source,javascript]
----
const g = traversal().with_(new DriverRemoteConnection('http://localhost:8182/gremlin'));
const tx = g.tx();
const gtx = await tx.begin();

await gtx.addV("person").property("name", "jorge").iterate();
await gtx.addV("person").property("name", "josh").iterate();

await tx.commit();
----

==== Via Client

[source,javascript]
----
const client = new Client('http://localhost:8182/gremlin', { traversalSource: 'g' });
const tx = client.transact();
await tx.begin();
await tx.submit("g.addV('person').property('name','alice')");
await tx.commit();
----

IMPORTANT: Each traversal must be `await`ed before submitting the next. Transactions are not thread-safe. The default
behavior of `close()` is to rollback. Transactions are single-use and cannot be reused after commit or rollback.

[source,javascript]
----
Expand Down Expand Up @@ -2283,8 +2344,40 @@ encoded in the Gremlin.Net GremlinLang and transmitted to the Gremlin traversal
[[gremlin-dotnet-transactions]]
=== Transactions

IMPORTANT: Transaction support over HTTP is not yet implemented in Gremlin.Net. This will be addressed by the official
4.0.0 release.
Gremlin.Net supports remote transactions over HTTP. Transactions can be created from a `GraphTraversalSource` or
directly from a `GremlinClient`.

==== Via GraphTraversalSource

[source,csharp]
----
var g = AnonymousTraversalSource.Traversal().With(new DriverRemoteConnection("localhost", 8182));

var tx = g.Tx();
var gtx = await tx.BeginAsync();

await gtx.AddV("person").Property("name", "jorge").Promise(t => t.Iterate());
await gtx.AddV("person").Property("name", "josh").Promise(t => t.Iterate());

await tx.CommitAsync();
----

==== Via GremlinClient

[source,csharp]
----
using var client = new GremlinClient(new GremlinServer("localhost", 8182));
var tx = client.Transact("g");
await tx.BeginAsync();

await tx.SubmitAsync<object>("g.addV('person').property('name','alice')");

await tx.CommitAsync();
----

NOTE: Submissions within a transaction are serialized internally via a semaphore to guarantee the server receives
requests in order. Transactions are not thread-safe. The default behavior of `DisposeAsync()` is to rollback. Use
`await using` for automatic cleanup. Transactions are single-use and cannot be reused after commit or rollback.

[[gremlin-dotnet-gvalue]]
=== GValue Parameterization
Expand Down Expand Up @@ -2785,8 +2878,49 @@ re-construction machine-side.
[[gremlin-python-transactions]]
=== Transactions

IMPORTANT: Transaction support over HTTP is not yet implemented in Gremlin-Python. This will be addressed by the
official 4.0.0 release.
Gremlin-Python supports remote transactions over HTTP. Transactions can be created from a `GraphTraversalSource` or
directly from a `Client`.

==== Via GraphTraversalSource

[source,python]
----
g = traversal().with_remote(DriverRemoteConnection('http://localhost:8182/gremlin', 'g'))

tx = g.tx()
gtx = tx.begin()

gtx.addV('person').property('name', 'jorge').iterate()
gtx.addV('person').property('name', 'josh').iterate()

tx.commit()
----

==== Via Client

[source,python]
----
client = Client('http://localhost:8182/gremlin', 'g')

tx = client.transact()
tx.begin()
tx.submit("g.addV('person').property('name','alice')")
tx.commit()
----

==== Context Manager

[source,python]
----
with client.transact() as tx:
tx.begin()
tx.submit("g.addV('person').property('name','alice')")
tx.commit()
# if commit() not called or exception occurs, rollback happens on exit
----

NOTE: Transactions are not thread-safe. All operations must be sequential. The default behavior of `close()` is to
rollback. Transactions are single-use and cannot be reused after commit or rollback.

[[gremlin-python-gvalue]]
=== GValue Parameterization
Expand Down
3 changes: 2 additions & 1 deletion docs/src/reference/intro.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,8 @@ regard. Prefer explicit property key names in Gremlin unless it is completely im
The third and final point involves transactions. Under this model, one traversal is equivalent to a single transaction
and there is no way in TinkerPop to string together multiple traversals into the same transaction.
IMPORTANT: 4.0.0-beta.1 Release - Remote transactions are not supported in this beta.
Remote transactions are supported across all GLVs. See the <<transactions,Transactions>> section and each GLV's
specific transaction documentation for syntax and usage details.
[[connecting-rgp]]
=== Remote Gremlin Provider
Expand Down
18 changes: 2 additions & 16 deletions docs/src/reference/the-traversal.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,8 @@ GraphTraversalSource gtx2 = tx2.begin();
----

In remote cases, `GraphTraversalSource` instances spawned from `begin()` are safe to use from a single thread. The
default behavior of `close()` on a `Transaction` for remote cases is to `commit()`, so the following re-write of the
earlier example is also valid:

[source,java]
----
// note here that we dispense with creating a Transaction object and
// simply spawn the gtx in a more inline fashion
GraphTraversalSource gtx = g.tx().begin();
try {
gtx.addV('person').iterate();
gtx.addV('software').iterate();
gtx.close();
} catch (Exception ex) {
tx.rollback();
}
----
default behavior of `close()` on a `Transaction` for remote cases is to `rollback()`, ensuring partial work is
discarded if the user forgets to commit. Users must call `commit()` explicitly to persist data.

IMPORTANT: Transactions with non-JVM languages are always "remote". For specific transaction syntax in a particular
language, please see the "Transactions" sub-section of your language of interest in the
Expand Down
28 changes: 28 additions & 0 deletions docs/src/upgrade/release-4.x.x.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,34 @@ anonymized form. The original gremlator.com was a prototype built by TinkerPop c
previous implementation required Java and a running Gremlin Server, whereas the new version runs entirely in the
browser with no server infrastructure needed.

==== GLV Transaction Support

All non-Java Gremlin Language Variants (gremlin-python, gremlin-go, gremlin-javascript, gremlin-dotnet) now support
explicit remote transactions over HTTP. The transaction model is the same across all GLVs: begin a transaction, submit
traversals within it, then commit or rollback. Data is isolated until committed.

Each GLV provides two entry points:

* **Traversal API**: `g.tx().begin()` returns a transaction-bound `GraphTraversalSource`
* **Driver API**: `client.transact()` (or `client.Transact()` in .NET) returns a `Transaction` with `submit()` methods

Key behaviors consistent across all GLVs:

* Transactions are not thread-safe. All operations must be sequential.
* The default close behavior is rollback (partial work discarded if commit is not called explicitly).
* Transactions are single-use. After commit or rollback, a new transaction must be created.
* `gtx.tx()` returns the same transaction object (enabling `gtx.tx().commit()` pattern from the Traversal API).

See the <<gremlin-drivers-variants,Gremlin Drivers and Variants>> reference documentation for language-specific
syntax and examples.

==== Transaction Default Close Behavior Changed

The default behavior of `close()` on a remote transaction has been changed from `commit` to `rollback` across all
GLVs (including the Java driver). This aligns with the embedded graph transaction default and is the safer behavior:
partial work is discarded if the user forgets to call `commit()`. In Java, the behavior can still be overridden via
`tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT)`.

==== Removed `uuid` Dependency in gremlin-javascript

The `uuid` npm package has been removed from `gremlin-javascript`. UUID generation now uses the built-in
Expand Down
9 changes: 9 additions & 0 deletions gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ public async Task<ResultSet<T>> SubmitAsync<T>(RequestMessage requestMessage,
headers["bulkResults"] = "true";
}

// Promote transactionId to HTTP header before serialization.
// The field remains in the serialized body as well (dual transmission
// per the HTTP transaction protocol specification).
if (requestMessage.Fields.TryGetValue(Tokens.ArgsTransactionId, out var txIdObj) &&
txIdObj is string txId && !string.IsNullOrEmpty(txId))
{
headers["X-Transaction-Id"] = txId;
}

object body;
if (_requestSerializer != null)
{
Expand Down
12 changes: 12 additions & 0 deletions gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ public async Task<ResultSet<T>> SubmitAsync<T>(RequestMessage requestMessage,
.ConfigureAwait(false);
}

/// <summary>
/// Creates a new <see cref="RemoteTransaction"/> for executing operations within an explicit
/// server-side transaction. Transactions are short-lived and single-use: after commit
/// or rollback, create a new RemoteTransaction for the next unit of work.
/// </summary>
/// <param name="traversalSource">The traversal source alias (e.g. "g" or "gtx").</param>
/// <returns>A new <see cref="RemoteTransaction"/>.</returns>
public RemoteTransaction Transact(string traversalSource)
{
return new RemoteTransaction(this, traversalSource);
}

#region IDisposable Support

private bool _disposed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ public class DriverRemoteConnection : IRemoteConnection, IDisposable
// The server filters out options that don't apply, and this allows
// providers to use custom request fields via the Client directly or DRC.

/// <inheritdoc />
public bool IsSessionBound => false;

/// <summary>
/// Initializes a new <see cref="IRemoteConnection" />.
/// </summary>
Expand Down Expand Up @@ -141,12 +138,11 @@ public async Task<ITraversal<TStart, TEnd>> SubmitAsync<TStart, TEnd>(GremlinLan
}

/// <inheritdoc />
/// <remarks>
/// Transaction support over HTTP is not yet implemented. This will be addressed in a future release.
/// </remarks>
public RemoteTransaction Tx(GraphTraversalSource g)
{
throw new NotSupportedException("Transaction support over HTTP is not yet implemented.");
// The g parameter satisfies the IRemoteConnection interface but is unused.
// The traversal source is taken from this connection's configuration.
return new RemoteTransaction(_client, _traversalSource);
}

/// <inheritdoc />
Expand Down
Loading
Loading