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

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

commit 0db826ae685d49d6a8a7c41347d52da57db75e92
Merge: e0a65c729f 8e286c5d97
Author: Ken Hu <[email protected]>
AuthorDate: Tue Mar 31 13:42:22 2026 -0700

    Merge branch '3.7-dev' into 3.8-dev
    
    Note that this moves the session reuse entry from 3.8.1 to 3.7.6 where
    it belongs.

 docs/src/dev/provider/index.asciidoc             |  10 ++
 docs/src/reference/gremlin-applications.asciidoc |   2 +-
 docs/src/reference/gremlin-variants.asciidoc     | 122 ++++++++++++++++++++--
 docs/src/upgrade/release-3.7.x.asciidoc          | 127 ++++++++++++++++++++++-
 docs/src/upgrade/release-3.8.1.asciidoc          | 122 ----------------------
 5 files changed, 250 insertions(+), 133 deletions(-)

diff --cc docs/src/upgrade/release-3.8.1.asciidoc
index 2a786bdb74,0000000000..97aff2dab7
mode 100644,000000..100644
--- a/docs/src/upgrade/release-3.8.1.asciidoc
+++ b/docs/src/upgrade/release-3.8.1.asciidoc
@@@ -1,258 -1,0 +1,136 @@@
 +////
 +Licensed to the Apache Software Foundation (ASF) under one
 +or more contributor license agreements.  See the NOTICE file
 +distributed with this work for additional information
 +regarding copyright ownership.  The ASF licenses this file
 +to you under the Apache License, Version 2.0 (the
 +"License"); you may not use this file except in compliance
 +with the License.  You may obtain a copy of the License at
 +
 +  http://www.apache.org/licenses/LICENSE-2.0
 +
 +Unless required by applicable law or agreed to in writing,
 +software distributed under the License is distributed on an
 +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +KIND, either express or implied.  See the License for the
 +specific language governing permissions and limitations
 +under the License.
 +////
 +== TinkerPop 3.8.1
 +
 +*Release Date: NOT OFFICIALLY RELEASED YET*
 +
 +Please see the 
link:https://github.com/apache/tinkerpop/blob/3.8.1/CHANGELOG.asciidoc#release-3-8-1[changelog]
 for a
 +complete list of all the modifications that are part of this release.
 +
 +=== Upgrading for Users
 +
 +==== Gremlint MCP
 +
 +The Gremlin MCP server now exposes Gremlint formatting for Gremlin 
traversals, which can be a convenient way to make
 +a long string of Gremlin easier to read directly from an AI assistant. 
Provide a simple prompt like the following to
 +an AI coding agent:
 +
 +[source,text]
 +----
 +Format this Gremlin query:
 +```
 +g.V().union(limit(3).fold(),tail(3).fold()).local(
 +  unfold().order().by(bothE().count(),desc).limit(1).fold())
 +```
 +----
 +
 +It will trigger a call to Gremlint within the Gremlin MCP Server to format it 
with better indentation and spacing.
 +
 +==== Gremlint Improvements
 +
 +Gremlint's approach to newlines often produced formatting that didn't 
generally follow the espoused best practices.
 +Specifically, if Gremlint found that arguments to a step would exceed the 
line length, it would instantly apply a
 +newline and then do an indent. This formatting rule made certain Gremlin 
appear stretched out vertically in many cases.
 +
 +The following example demonstrates this stretching. The following bit of 
Gremlin is manually formatted according to
 +common norms and best practices:
 +
 +[source,groovy]
 +----
 +g.V().as("v").
 +  repeat(both().simplePath().as("v")).emit().
 +  filter(project("x","y","z").by(select(first, "v")).
 +                              by(select(last, "v")).
 +                              by(select(all, "v").count(local)).as("triple").
 +         coalesce(select("x","y").as("a").
 +                    select("triples").unfold().as("t").
 +                    select("x","y").where(eq("a")).
 +                    select("t"),
 +                  local(aggregate("triples"))).
 +         select("z").as("length").
 +         select("triple").select("z").where(eq("length"))).
 +  select(all, "v").unfold().
 +  groupCount()
 +----
 +
 +In earlier versions of Gremlint, it would format that query to:
 +
 +[source,groovy]
 +----
 +g.V().as("v").
 +  repeat(both().simplePath().as("v")).emit().
 +  filter(
 +    project("x", "y", "z").
 +      by(select(first, "v")).
 +      by(select(last, "v")).
 +      by(select(all, "v").count(local)).
 +      as("triple").
 +    coalesce(
 +      select("x", "y").as("a").
 +      select("triples").
 +      unfold().as("t").
 +      select("x", "y").
 +      where(eq("a")).
 +      select("t"),
 +      local(aggregate("triples"))).
 +    select("z").as("length").
 +    select("triple").
 +    select("z").
 +    where(eq("length"))).
 +  select(all, "v").
 +  unfold().
 +  groupCount()
 +----
 +
 +In this version, after the improvements mentioned above, Gremlint now 
produces:
 +
 +[source,groovy]
 +----
 +g.V().as("v").
 +  repeat(both().simplePath().as("v")).emit().
 +  filter(project("x", "y", "z").
 +           by(select(first, "v")).
 +           by(select(last, "v")).
 +           by(select(all, "v").count(local)).
 +           as("triple").
 +         coalesce(select("x", "y").as("a").
 +                  select("triples").
 +                  unfold().as("t").
 +                  select("x", "y").
 +                  where(eq("a")).
 +                  select("t"), local(aggregate("triples"))).
 +         select("z").as("length").
 +         select("triple").
 +         select("z").
 +         where(eq("length"))).
 +  select(all, "v").
 +  unfold().
 +  groupCount()
 +----
 +
 +This more compact representation presents a form much more in line with the 
manually formatted one. While there is still
 +room to improve, Gremlint now produces a format that is more likely to be 
usable without additional manual formatting
 +intervention.
 +
- ==== Remote Transaction Performance Improvements
- 
- The Java driver now supports reusing existing pooled WebSocket connections 
for session-based requests rather than
- establishing a dedicated connection per session. This behavior is controlled 
by the `Cluster.Builder` option
- `reuseConnectionsForSessions`, which defaults to `false`.
- 
- When enabled, a `Client.SessionedChildClient` will attempt to borrow a 
connection from the connection pool of a standard
- `Client` rather than opening its own WebSocket connection. This avoids the 
overhead of the TCP handshake and WebSocket
- upgrade for each session, which can be significant when issuing many 
short-lived transactions.
- 
- [source,java]
- ----
- // Enable connection reuse for sessions
- Cluster cluster = Cluster.build(host)
-         .reuseConnectionsForSessions(true)
-         .create();
- ----
- 
- This feature was designed specifically for use with remote transactions, 
where sessions are short-lived and terminate
- after a `commit()` or `rollback()`. It should not be used for classic 
long-running session use cases where a session
- is used for purposes other than transactions such as remote console.
- 
- ===== Server Configuration
- 
- When using `reuseConnectionsForSessions`, the server must be configured with 
`closeSessionPostGraphOp` set to `true`.
- This setting instructs the server to close the session immediately after a 
graph-level operation such as `commit()` or
- `rollback()` completes. Without this setting, sessions will not be closed 
until the session timeout expires, leading to
- a buildup of idle sessions on the server side.
- 
- [source,yaml]
- ----
- # gremlin-server.yaml
- closeSessionPostGraphOp: true
- ----
- 
- IMPORTANT: Failing to enable `closeSessionPostGraphOp` on the server when 
using `reuseConnectionsForSessions` on the
- client will result in sessions that are not properly cleaned up. These leaked 
sessions will accumulate until the
- configured `sessionLifetimeTimeout` is reached, consuming server resources 
unnecessarily.
- 
- ===== Performance
- 
- Performance was measured with an ad-hoc benchmark application. The 
application executes a configurable number of
- complete transaction lifecycles (begin, mutate, commit) and reports 
throughput and latency percentiles. Each transaction
- opens a session, submits one or more `addV()` operations, commits, and closes 
the session.
- 
- The benchmark varies the following parameters:
- 
- * *Concurrent clients* (`threads`): The number of threads issuing 
transactions simultaneously. A value of 1 means
-   transactions are executed sequentially by a single client. Higher values 
simulate multiple application threads or
-   service instances issuing transactions concurrently against the same server.
- * *Connection pool size* (`pool`): The number of WebSocket connections 
maintained in the pool when
-   `reuseConnectionsForSessions` is enabled. When reuse is disabled, each 
session creates its own dedicated connection
-   and this parameter does not apply (shown as `n/a`).
- * *Transaction weight* (`weight`): "light" transactions perform a single 
`addV()` plus commit. "heavy" transactions
-   perform ten `addV()` operations plus commit, simulating a more substantial 
unit of work per transaction.
- 
- Tests were conducted both locally (client and server on the same machine) and 
remotely (client on the US west coast,
- server on the US east coast) to isolate the effect of network latency on 
connection setup overhead. Each scenario
- executed 1000 transactions after a warmup phase of 50 transactions.
- 
- *Local Results (same machine)*
- 
- [cols="3,1,1,1", options="header"]
- |=========================================================
- |Configuration |No-Reuse (tx/s) |Best-Reuse (tx/s) |Speedup
- |1 client, light |23.1 |26.7 |1.16x
- |8 clients, light |25.2 |28.5 |1.13x
- |16 clients, light |25.4 |27.9 |1.10x
- |1 client, heavy |26.0 |26.9 |1.03x
- |8 clients, heavy |26.4 |27.9 |1.06x
- |16 clients, heavy |25.8 |26.5 |1.03x
- |=========================================================
- 
- *Remote Results (west coast to east coast)*
- 
- [cols="3,1,1,1", options="header"]
- |=========================================================
- |Configuration |No-Reuse (tx/s) |Best-Reuse (tx/s) |Speedup
- |1 client, light |3.6 |7.6 |2.10x
- |8 clients, light |15.6 |23.0 |1.48x
- |16 clients, light |15.4 |25.3 |1.64x
- |1 client, heavy |1.4 |1.8 |1.26x
- |8 clients, heavy |9.2 |10.8 |1.17x
- |16 clients, heavy |14.5 |15.9 |1.10x
- |=========================================================
- 
- The "Best-Reuse" column reflects the highest throughput observed across all 
tested pool sizes (2, 4, and 8 connections)
- for each scenario.
- 
- The benefit of connection reuse is most pronounced in remote scenarios with 
light transactions. When the network
- round-trip cost is high and the transaction payload is small, the WebSocket 
connection setup overhead represents a
- larger proportion of the total transaction time. In the single-client remote 
light workload, connection reuse yielded a
- 2.10x throughput improvement, as the connection handshake cost dominated the 
per-transaction time. With 16 concurrent
- clients in the same remote light scenario, throughput improved from 15.4 tx/s 
to 25.3 tx/s (1.64x), as the connection
- pool amortized the setup cost across many parallel sessions.
- 
- As transaction weight increases, the relative benefit diminishes because the 
graph operations themselves become the
- bottleneck rather than connection setup. In the local heavy workload 
scenarios, the improvement was only 3-6%, as the
- connection overhead was already negligible relative to the cost of the graph 
mutations. Even in the remote heavy
- scenarios, the improvement ranged from 10-26%, as the ten `addV()` operations 
per transaction shifted the time
- distribution toward server-side processing.
- 
- In summary, `reuseConnectionsForSessions` provides the greatest benefit when:
- 
- * Network latency between client and server is significant (remote 
deployments)
- * Transactions are lightweight (few operations per transaction)
- * Many short-lived transactions are issued in sequence or concurrently
- 
- See: link:https://issues.apache.org/jira/browse/TINKERPOP-3213[TINKERPOP-3213]
- 
 +=== Upgrading for Providers
 +
 +==== Graph System Providers
 +
- ===== Closing Sessions On Graph Operations
- 
- An option has been added to the Java GLV (`reuseConnectionsForSessions`) that 
allows for borrowing open WebSocket
- connections for sessions. This is primarily to reduce the overhead of new 
connection setup per session. This can lead
- to large performance gains in remote transaction scenarios where there are 
many small mutation traversals.
- 
- This option is disabled by default on the driver but providers may want to 
add an option that will allow sessions to end
- on the successful completion of a graph operation (commit/rollback). This 
will prevent a buildup of sessions if a user
- has enabled this option as the driver will *not* close the underlying 
WebSocket connection as a signal to end the
- session. Gremlin Server has added an option like this called 
`closeSessionPostGraphOp`. Remote graph providers are
- encouraged to add the same functionality.
- 
 +==== Graph Driver Providers
 +

Reply via email to