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

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

commit 08f779d88366c1d67a37a4ea2cde2717a3964b0d
Author: Stephen Mallette <[email protected]>
AuthorDate: Thu Jul 16 12:31:04 2026 -0400

    Reorder 4.0.0-beta.3 upgrade docs by significance
    
    Assisted-by: Claude Code:claude-opus-4-8
---
 docs/src/upgrade/release-4.x.x.asciidoc | 456 +++++++++++++++-----------------
 1 file changed, 219 insertions(+), 237 deletions(-)

diff --git a/docs/src/upgrade/release-4.x.x.asciidoc 
b/docs/src/upgrade/release-4.x.x.asciidoc
index 11c89536fc..8ef445cb7a 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -32,151 +32,6 @@ complete list of all the modifications that are part of 
this release.
 
 === Upgrading for Users
 
-==== Bindings are now Parameters
-
-The map of named values substituted into a Gremlin query was previously called 
"bindings" in some places and
-"parameters" in others. TinkerPop now refers to these consistently as *query 
parameters*, reserving "bindings" for
-the distinct concept of `ScriptEngine` variable bindings. As a result, the 
driver methods for supplying query
-parameters have been renamed. Update any code that sets them:
-
-- Java: `RequestMessage.Builder.addBindings(...)` is now `addParameters(...)`.
-- Python: `client.submit(..., bindings=...)` is now `submit(..., 
parameters=...)`, and the `request_options`
-key `bindings` is now `parameters`.
-- Go: `SetBindings`/`SetBindingsString`/`AddBinding` are now
-`SetParameters`/`SetParametersString`/`AddParameter`.
-- .NET: `AddBinding`/`AddBindings`/`AddBindingsString` are now
-`AddParameter`/`AddParameters`/`AddParametersString`.
-- JavaScript: `addBinding`/`addBindings`/`addBindingsString` are now
-`addParameter`/`addParameters`/`addParametersString`, and the 
`RequestOptions.bindings` field is now `parameters`.
-
-For example, in the Java driver:
-
-[source,java]
-----
-final Map<String, Object> params = new HashMap<>();
-params.put("x", 1);
-
-// 3.x
-RequestMessage.build("g.V(x)").addBindings(params).create();
-
-// 4.x
-RequestMessage.build("g.V(x)").addParameters(params).create();
-----
-
-See: link:https://issues.apache.org/jira/browse/TINKERPOP-3262[TINKERPOP-3262]
-
-==== Standardizing GLV Connection Options
-
-TinkerPop 4.x standardizes connection option names and defaults across all 
five Gremlin Language Variants (Java, Python,
-.NET, Go, and JavaScript). Each driver using its language-idiomatic casing 
(`camelCase`, `PascalCase`, or `snake_case`).
-These renames are breaking, the old option names have been removed.
-
-NOTE: Timeouts use a millisecond-suffixed canonical name 
(`connectTimeoutMillis`, `readTimeoutMillis`,
-`idleTimeoutMillis`, `keepAliveTimeMillis`, and the `_millis` form in Python). 
Java, Go, .NET, and Python also accept an
-idiomatic duration companion for the same setting (Java `Duration`, Go 
`time.Duration`, .NET `TimeSpan`, Python seconds);
-set only one form per option. JavaScript exposes only the millisecond form.
-
-===== Standardized options (cross-GLV)
-
-The table lists each standardized option by driver. Defaults are shown in 
parentheses; "n/a" means the driver does not
-expose that option.
-
-[width="100%",cols="2,2,2,2,2,2",options="header"]
-|=========================================================
-|Concept |Java (`Cluster.Builder`) |Python (kwarg) |.NET 
(`ConnectionSettings`) |Go (settings field) |JavaScript (option)
-|Max pooled connections |`maxConnections` (128) |`max_connections` (128) 
|`MaxConnections` (128) |`MaxConnections` (128) |`maxConnections` (128)
-|Connect timeout |`connectTimeoutMillis` (5000) |`connect_timeout_millis` 
(5000) |`ConnectTimeoutMillis` (5000) |`ConnectTimeoutMillis` (5000) |n/a
-|Idle-read timeout |`readTimeoutMillis` (0/off) |`read_timeout_millis` (off) 
|`ReadTimeoutMillis` (0/off) |`ReadTimeoutMillis` (0/off) |`readTimeoutMillis` 
(undici default)
-|Pool idle timeout |`idleTimeoutMillis` (180000) |`idle_timeout_millis` 
(180000) |`IdleTimeout` (180s) |`IdleTimeoutMillis` (180000) |n/a
-|TCP keep-alive idle |`keepAliveTimeMillis` (30000) |`keep_alive_time_millis` 
(30000) |`KeepAliveTime` (30s) |`KeepAliveTimeMillis` (30000) 
|`keepAliveTimeMillis` (30000)
-|Compression |`compression` (`DEFLATE`) |`compression` (`'deflate'`) 
|`Compression` (`Deflate`) |`Compression` (`CompressionDeflate`) |`compression` 
(`'deflate'`)
-|Connection-level batch size |`batchSize` (64) |`batch_size` (64) |`BatchSize` 
(64) |`BatchSize` (64) |`batchSize` (64)
-|Connection-level bulkResults |`bulkResults` (false) |`bulk_results` (false) 
|`BulkResults` (false) |`BulkResults` (false) |`bulkResults` (false)
-|Max response header bytes |`maxResponseHeaderBytes` (8192) |n/a 
|`MaxResponseHeaderBytes` (handler default) |`MaxResponseHeaderBytes` (net/http 
default) |`maxResponseHeaderBytes` (undici default)
-|TLS configuration |`ssl(SslContext)` + keystore builders |`ssl` 
(`SSLContext`) |`Ssl` (`SslClientAuthenticationOptions`) |`Ssl` (`*tls.Config`) 
|runtime (`NODE_EXTRA_CA_CERTS`, etc.)
-|HTTP proxy |`proxy(ProxyOptions)` |`proxy` |`Proxy` (`IWebProxy`) |`Proxy` 
(env default) |`proxy` (undici `ProxyAgent`)
-|Request interceptors |`interceptors` |`interceptors` |interceptor delegates 
|`Interceptors` |`interceptors`
-|=========================================================
-
-===== Behavior changes (all drivers)
-
-These change runtime behavior on upgrade even if you do not change your 
configuration:
-
-- *Compression defaults to on.* Every driver now defaults compression to 
`deflate` and sends `Accept-Encoding: deflate`.
-  Disable it with the compression option's "none" value (`Compression.NONE`, 
`'none'`, `Compression.None`,
-  `CompressionNone`).
-- *Connect timeout lowered to 5s* (from 15s, where applicable) and is now 
actually applied to transport establishment
-  (TCP connect plus TLS handshake), not the whole request.
-- *`readTimeout` is an idle-read timeout*, reset on each inbound response 
chunk, so it is streaming-safe and bounds only
-  the gap between chunks, not total response duration. It is off by default.
-- *`idleTimeout` reaps only pooled connections* that are idle between 
requests; it no longer bounds an in-flight
-  response (that is `readTimeout`'s job).
-
-===== Driver-specific notes
-
-- *Java* (`gremlin-driver`): Renamed: 
`maxConnectionPoolSize`->`maxConnections`, 
`connectionSetupTimeoutMillis`->`connectTimeoutMillis`,
-  `idleConnectionTimeoutMillis`->`idleTimeoutMillis`, 
`resultIterationBatchSize`->`batchSize`, `serializer`->`responseSerializer`,
-  and `RequestOptions` `addG`->`traversalSource`. New: `readTimeoutMillis`, 
`keepAliveTimeMillis`,
-  `maxResponseHeaderBytes`, `proxy(ProxyOptions)`, `url(String)`, 
`ssl(SslContext)`. Removed: `maxResponseContentLength`.
-  `validationRequest` default reconciled to `g.inject(0)`.
-- *Python* (`gremlin-python`): Renamed: `pool_size`->`max_connections` 
(default 8->128) and `ssl_options`->`ssl`.
-  New: `connect_timeout_millis`, `read_timeout_millis`, `idle_timeout_millis`, 
`keep_alive_time_millis`, `compression`,
-  `batch_size`, `proxy`, `trust_env`. `auth.sigv4` gained an optional 
credentials provider. Removed: `headers` (use interceptors)
-  and `max_content_length`.
-- *.NET* (`gremlin-dotnet`): Renamed: 
`MaxConnectionsPerServer`->`MaxConnections`, 
`ConnectionTimeout`->`ConnectTimeout`,
-  `IdleConnectionTimeout`->`IdleTimeout`, 
`KeepAliveInterval`->`KeepAliveTime`, `EnableCompression`->`Compression`, and
-  `Auth.BasicAuth`/`Auth.SigV4Auth`->`Auth.Basic`/`Auth.Sigv4`. New: 
`ReadTimeout`, `MaxResponseHeaderBytes`,
-  `Proxy`, `Ssl`, `BulkResults`.
-- *Go* (`gremlin-go`): Renamed: 
`MaximumConcurrentConnections`->`MaxConnections`, 
`IdleConnectionTimeout`->`IdleTimeout`,
-  `KeepAliveInterval`->`KeepAliveTime`, `ConnectionTimeout`->`ConnectTimeout`, 
`TlsConfig`->`Ssl`, `RequestInterceptors`->`Interceptors`,
-  `EnableCompression`->`Compression`. Auth helpers moved from package 
`gremlingo`
-  (`BasicAuth`/`SigV4Auth`/`SigV4AuthWithCredentials`) into a new `auth` 
sub-package (`auth.Basic`/`auth.SigV4`/`auth.SigV4WithCredentials`).
-  New: `ReadTimeout(Millis)`, `MaxResponseHeaderBytes`, `Proxy` (defaults to 
`http.ProxyFromEnvironment`), `BatchSize`, `BulkResults`.
-- *JavaScript* (`gremlin-javascript`): adopted `undici` as a pinned dependency 
providing the default dispatcher built from the
-  options above. Renamed: `reader`->`responseSerializer`. New: 
`readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`,
-  `proxy`, `compression`, `batchSize`, `bulkResults`, `logger`. Removed 
`headers` (use interceptors) and the
-  `ca`/`cert`/`pfx`/`rejectUnauthorized`/`agent` options (TLS is configured 
through the Node/undici runtime). undici is swapped
-  out in browser bundles, where these connection-pool options are managed by 
the browser.
-
-See: 
link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] 
Standardizing GLV connection options in TinkerPop 4].
-
-==== Renaming `evaluationTimeout` to `timeoutMillis`
-
-The per-request execution timeout is now referred to by a single name, 
`timeoutMillis`, everywhere. `timeoutMillis` is
-the maximum time in milliseconds that a request is allowed to execute on the 
server before it times out. It can be
-configured server-wide and overridden on a per-request basis. Previously the 
same concept was called
-`evaluationTimeout` in the server configuration, the `with()` script token, 
and several driver APIs. The `Millis`
-suffix aligns it with the driver connection options such as 
`connectTimeoutMillis` and `readTimeoutMillis`.
-
-This is a breaking change with no backward-compatible alias. The old 
`evaluationTimeout` name (and the long-deprecated
-`scriptEvaluationTimeout`) are no longer recognized anywhere. Update each 
surface as follows:
-
-- *Server config*: the `gremlin-server.yaml` key `evaluationTimeout` becomes 
`timeoutMillis` (default still 30000).
-- *Script token*: `g.with('evaluationTimeout', 500)` becomes 
`g.with('timeoutMillis', 500)`.
-- *Java driver*: `RequestOptions.Builder.timeout(long)` becomes 
`timeoutMillis(long)` and `getTimeout()` becomes `getTimeoutMillis()`.
-- *Go driver*: `RequestOptionsBuilder.SetEvaluationTimeout(int)` becomes 
`SetTimeoutMillis(int)`.
-- *.NET driver*: `Tokens.ArgsEvalTimeout` becomes `Tokens.ArgsTimeoutMillis` 
and `RequestMessage.Builder.AddEvaluationTimeout(...)`
-  becomes `AddTimeoutMillis(...)`.
-- *JavaScript driver*: the request option `{ evaluationTimeout: N }` becomes 
`{ timeoutMillis: N }`.
-- *Python driver*: use the token `timeoutMillis` (e.g. 
`g.with_('timeoutMillis', 500)` or `request_options={'timeoutMillis': 500}`).
-
-Driver and server should be upgraded together. A driver sending the old 
`evaluationTimeout` field to a 4.x server has
-that field silently ignored and falls back to the server's default timeout, as 
with any unrecognized request argument.
-
-==== Java Runtime Upgrade
-
-TinkerPop 4.0 raises the minimum Java version from 11 to 17 for both building 
and running, and adds support for running
-on Java 21 and Java 25. Supporting Java 25 required upgrading Groovy to 
4.0.32, Hadoop to 3.4.3, Spark to 4.1.x, and
-Netty to 4.2.x. These upgrades collectively prohibit building on Java 11, but 
enable support in Java 21 and 25.
-
-As with the earlier JDK 17 support, some libraries still rely on deep 
reflection (most notably the Kryo serialization
-library used with OLAP), so it may be necessary to `--add-opens` or 
`--add-exports` certain modules at runtime. The set
-of options used by TinkerPop's own tests is unchanged.
-
-==== .NET Runtime Upgrade
-
-The minimum target framework is now `net8.0` (previously 
`netstandard2.0;net6.0`).
-
 ==== Declarative Pattern Matching
 
 Gremlin has always offered both imperative and declarative styles to writing 
graph queries. While the imperative style
@@ -235,46 +90,14 @@ TinkerGraph uses it out of the box with no configuration 
required. Graph provide
 can add the `gql-gremlin` dependency and register 
`GqlDeclarativeMatchStrategy` as described in the
 
link:https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#tinkerpop-providers-tinkergql[provider
 documentation].
 
-==== Request Interceptors
-
-When TinkerPop supported WebSockets prior to 4.0.0, the Java driver offered a 
`RequestInterceptor` interface (and
-its predecessor, `HandshakeInterceptor`) that allowed modification of the raw 
Netty `FullHttpRequest`. For WebSocket
-connections, the interceptor only ran on the initial HTTP upgrade handshake. 
With the move to HTTP, the notion of
-the "interceptor" has shifted to a per-request concern that is now 
standardized across all GLVs.
-
-All GLVs now support request interceptors, which allow modification of the 
HTTP request before it is sent to the
-server. An interceptor is a function that receives the mutable HTTP request 
object and can modify headers, the
-request body, the URI, or the HTTP method. Interceptors are run in the order 
they are registered.
-
-The most common use case for interceptors is authentication (e.g., SigV4 
signing), but they can also be used to add
-provider-specific fields, inject custom headers, or transform the request body.
-
-Here is a simple Java example that adds a custom header:
-
-[source,java]
-----
-Cluster cluster = Cluster.build("localhost")
-    .interceptors(request -> request.headers().put("X-Custom-Header", "value"))
-    .create();
-----
-
-Authentication is also an interceptor. Each GLV provides convenience methods 
(e.g., `Auth.basic()`, `Auth.sigv4()`)
-that return interceptors and can be registered alongside custom ones.
-
-For full details on the interceptor API for each language variant, refer to 
the RequestInterceptor section in
-each GLV's documentation in the
-link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin
 Drivers and Variants]
-reference.
-
 ==== Multi-Label Support
 
 Until now, vertices in the property graph model were limited to a single, 
immutable label assigned at creation. This
 release introduces configurable label cardinality, allowing vertices to carry 
multiple labels that can be added and
-removed over their lifetime.
-
-The feature is controlled by `LabelCardinality`, a graph-level setting that 
defaults to `ONE`, preserving the
-existing single-label behavior. To enable multi-label in TinkerGraph, set the 
vertex label cardinality in the graph
-properties:
+removed over their lifetime. This is an optional feature for graph providers 
and not all will support it in the same
+way. TinkerGraph offers a configurable approach to enabling the the feature 
using one of the `LabelCardinality` settings
+as a graph-level setting that defaults to `ONE`, preserving the existing 
single-label behavior. To enable multi-label
+in TinkerGraph, set the vertex label cardinality in the graph properties:
 
 ```
 gremlin.tinkergraph.vertexLabelCardinality=ZERO_OR_MORE
@@ -283,7 +106,7 @@ gremlin.tinkergraph.vertexLabelCardinality=ZERO_OR_MORE
 The three modes are `ONE` (single, immutable, the 3.x default), `ONE_OR_MORE` 
(mutable, minimum one), and
 `ZERO_OR_MORE` (fully flexible). Edge labels remain fixed at `ONE`.
 
-With multi-label enabled, several new traversal steps become available:
+The following examples demonstrate how Gremlin behaves with multi-label 
enabled:
 
 ```text
 gremlin> g.addV('person','employee').property('name','marko')
@@ -334,49 +157,6 @@ gremlin> gml.V().has('name','marko').valueMap(true)
 ==>{id=0, label=[manager, person, employee], name=[marko]}
 ```
 
-Note the following behavioral details:
-
-* The deprecated `label()` step returns a single, non-deterministic label when 
multiple labels are present. Always
-  use `labels()` to retrieve the full set reliably.
-* When a source is configured with `with("multilabel")`, the label output can 
be forced back to a single string
-  per-traversal using `with("singlelabel")`. This is useful for providers that 
enable multi-label output by default
-  but need an escape hatch for backward compatibility. Note that when both 
options are present on the same source,
-  `"singlelabel"` always takes precedence regardless of the order in which 
they were applied.
-* Adding a label that already exists on the vertex is a no-op (when 
multi-label is enabled). Dropping a label that
-  does not exist is also a no-op. With cardinality `ONE`, both `addLabel()` 
and `dropLabel()` throw since labels are
-  immutable.
-* `hasLabel()` tests the predicate against each label on the element and 
returns `true` if at least one label
-  satisfies it. For multi-label vertices, this means 
`hasLabel(P.neq("person"))` returns `true` when the vertex also
-  carries other labels, a natural consequence of testing a negation predicate 
against a set of labels, where the
-  non-matching labels will satisfy `neq`. To exclude vertices that carry a 
specific label, use the negation pattern:
-  `g.V().not(__.hasLabel("person"))`. For single-label vertices the behavior 
is unchanged.
-
-==== Inconfigurable Request Serialization
-
-TinkerPop 3.x drivers used a single serializer configuration (for example, 
`serializer` in the Java driver or
-`message_serializer` in Python) that controlled both how a `RequestMessage` 
was serialized on the way to the server
-and how responses were deserialized on the way back. TinkerPop 4 splits these 
concerns. Requests are now always
-serialized as JSON (`application/json`) and that behavior is not configurable. 
The remaining serializer configuration
-is named `responseSerializer` (cased idiomatically per language) and controls 
only the `Accept` header and response
-deserialization.
-
-Applications that require a different request body encoding, such as 
GraphBinary for a server that expects it, can
-register a request interceptor that serializes the `RequestMessage` and 
replaces the body and `Content-Type` header.
-See the RequestInterceptor section for each GLV in the
-link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin
 Drivers and Variants]
-reference.
-
-See: link:https://issues.apache.org/jira/browse/TINKERPOP-3250[TINKERPOP-3250]
-
-==== Gremlator
-
-link:https://gremlator.com[Gremlator] has been rebuilt entirely in JavaScript 
as a browser-based single-page
-application and is now an official part of the Apache TinkerPop project. It 
translates Gremlin queries into
-equivalent representations in all supported language variants: Groovy, Java, 
Python, JavaScript, Go, .NET, and an
-anonymized form. The original gremlator.com was a prototype built by TinkerPop 
committer Dave Bechberger; the
-previous implementation required Java and a running Gremlin Server, whereas 
the new version runs entirely in the
-browser with no server infrastructure needed.
-
 ==== Transactions
 
 TinkerPop 4.0 brings a set of related transaction changes across the drivers 
and embedded graphs: explicit remote
@@ -454,6 +234,220 @@ partial work is discarded if the user forgets to call 
`commit()`. In Java (both
 can still be overridden via `tx.onClose(Transaction.CLOSE_BEHAVIOR.COMMIT)`. 
The non-Java GLVs do not support
 configuring close behavior and always rollback.
 
+==== Subgraph Support in GLVs
+
+All GLVs now support the `subgraph()` step. Previously, calling `subgraph()` 
from a GLV produced an unknown-type error
+because the variant could not interpret the `Graph` payload that the server 
returned. Applications can now extract a
+portion of a remote graph as part of a normal traversal and inspect its 
vertices and edges directly from the client,
+without having to re-issue queries to reconstruct the result. See: 
<<subgraph-step>>.
+
+In the GLVs, the result is a detached snapshot of the captured vertices and 
edges, not a traversable `Graph` instance.
+It cannot be passed to `traversal().with(...)`, and mutating its collections 
has no effect on the source graph. To
+re-query elements against the original graph, extract their ids and call 
`g.V(id)` or `g.E(id)` against the original
+`GraphTraversalSource`.
+
+==== Gremlator
+
+link:https://gremlator.com[Gremlator] has been rebuilt entirely in JavaScript 
as a browser-based single-page
+application and is now an official part of the Apache TinkerPop project. It 
translates Gremlin queries into
+equivalent representations in all supported language variants: Groovy, Java, 
Python, JavaScript, Go, .NET, and an
+anonymized form. The original gremlator.com was a prototype built by TinkerPop 
committer Dave Bechberger; the
+previous implementation required Java and a running Gremlin Server, whereas 
the new version runs entirely in the
+browser with no server infrastructure needed.
+
+==== Bindings are now Parameters
+
+The map of named values substituted into a Gremlin query was previously called 
"bindings" in some places and
+"parameters" in others. TinkerPop now refers to these consistently as *query 
parameters*, reserving "bindings" for
+the distinct concept of `ScriptEngine` variable bindings. As a result, the 
driver methods for supplying query
+parameters have been renamed. Update any code that sets them:
+
+- Java: `RequestMessage.Builder.addBindings(...)` is now `addParameters(...)`.
+- Python: `client.submit(..., bindings=...)` is now `submit(..., 
parameters=...)`, and the `request_options`
+key `bindings` is now `parameters`.
+- Go: `SetBindings`/`SetBindingsString`/`AddBinding` are now
+`SetParameters`/`SetParametersString`/`AddParameter`.
+- .NET: `AddBinding`/`AddBindings`/`AddBindingsString` are now
+`AddParameter`/`AddParameters`/`AddParametersString`.
+- JavaScript: `addBinding`/`addBindings`/`addBindingsString` are now
+`addParameter`/`addParameters`/`addParametersString`, and the 
`RequestOptions.bindings` field is now `parameters`.
+
+For example, in the Java driver:
+
+[source,java]
+----
+final Map<String, Object> params = new HashMap<>();
+params.put("x", 1);
+
+// 3.x
+RequestMessage.build("g.V(x)").addBindings(params).create();
+
+// 4.x
+RequestMessage.build("g.V(x)").addParameters(params).create();
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3262[TINKERPOP-3262]
+
+==== Standardizing GLV Connection Options
+
+TinkerPop 4.x standardizes connection option names and defaults across all 
five Gremlin Language Variants (Java, Python,
+.NET, Go, and JavaScript). Each driver using its language-idiomatic casing 
(`camelCase`, `PascalCase`, or `snake_case`).
+These renames are breaking, the old option names have been removed.
+
+NOTE: Timeouts use a millisecond-suffixed canonical name 
(`connectTimeoutMillis`, `readTimeoutMillis`,
+`idleTimeoutMillis`, `keepAliveTimeMillis`, and the `_millis` form in Python). 
Java, Go, .NET, and Python also accept an
+idiomatic duration companion for the same setting (Java `Duration`, Go 
`time.Duration`, .NET `TimeSpan`, Python seconds);
+set only one form per option. JavaScript exposes only the millisecond form.
+
+===== Standardized options (cross-GLV)
+
+The table lists each standardized option by driver. Defaults are shown in 
parentheses; "n/a" means the driver does not
+expose that option.
+
+[width="100%",cols="2,2,2,2,2,2",options="header"]
+|=========================================================
+|Concept |Java (`Cluster.Builder`) |Python (kwarg) |.NET 
(`ConnectionSettings`) |Go (settings field) |JavaScript (option)
+|Max pooled connections |`maxConnections` (128) |`max_connections` (128) 
|`MaxConnections` (128) |`MaxConnections` (128) |`maxConnections` (128)
+|Connect timeout |`connectTimeoutMillis` (5000) |`connect_timeout_millis` 
(5000) |`ConnectTimeoutMillis` (5000) |`ConnectTimeoutMillis` (5000) |n/a
+|Idle-read timeout |`readTimeoutMillis` (0/off) |`read_timeout_millis` (off) 
|`ReadTimeoutMillis` (0/off) |`ReadTimeoutMillis` (0/off) |`readTimeoutMillis` 
(undici default)
+|Pool idle timeout |`idleTimeoutMillis` (180000) |`idle_timeout_millis` 
(180000) |`IdleTimeout` (180s) |`IdleTimeoutMillis` (180000) |n/a
+|TCP keep-alive idle |`keepAliveTimeMillis` (30000) |`keep_alive_time_millis` 
(30000) |`KeepAliveTime` (30s) |`KeepAliveTimeMillis` (30000) 
|`keepAliveTimeMillis` (30000)
+|Compression |`compression` (`DEFLATE`) |`compression` (`'deflate'`) 
|`Compression` (`Deflate`) |`Compression` (`CompressionDeflate`) |`compression` 
(`'deflate'`)
+|Connection-level batch size |`batchSize` (64) |`batch_size` (64) |`BatchSize` 
(64) |`BatchSize` (64) |`batchSize` (64)
+|Connection-level bulkResults |`bulkResults` (false) |`bulk_results` (false) 
|`BulkResults` (false) |`BulkResults` (false) |`bulkResults` (false)
+|Max response header bytes |`maxResponseHeaderBytes` (8192) |n/a 
|`MaxResponseHeaderBytes` (handler default) |`MaxResponseHeaderBytes` (net/http 
default) |`maxResponseHeaderBytes` (undici default)
+|TLS configuration |`ssl(SslContext)` + keystore builders |`ssl` 
(`SSLContext`) |`Ssl` (`SslClientAuthenticationOptions`) |`Ssl` (`*tls.Config`) 
|runtime (`NODE_EXTRA_CA_CERTS`, etc.)
+|HTTP proxy |`proxy(ProxyOptions)` |`proxy` |`Proxy` (`IWebProxy`) |`Proxy` 
(env default) |`proxy` (undici `ProxyAgent`)
+|Request interceptors |`interceptors` |`interceptors` |interceptor delegates 
|`Interceptors` |`interceptors`
+|=========================================================
+
+===== Behavior changes (all drivers)
+
+These change runtime behavior on upgrade even if you do not change your 
configuration:
+
+- *Compression defaults to on.* Every driver now defaults compression to 
`deflate` and sends `Accept-Encoding: deflate`.
+  Disable it with the compression option's "none" value (`Compression.NONE`, 
`'none'`, `Compression.None`,
+  `CompressionNone`).
+- *Connect timeout lowered to 5s* (from 15s, where applicable) and is now 
actually applied to transport establishment
+  (TCP connect plus TLS handshake), not the whole request.
+- *`readTimeout` is an idle-read timeout*, reset on each inbound response 
chunk, so it is streaming-safe and bounds only
+  the gap between chunks, not total response duration. It is off by default.
+- *`idleTimeout` reaps only pooled connections* that are idle between 
requests; it no longer bounds an in-flight
+  response (that is `readTimeout`'s job).
+
+===== Driver-specific notes
+
+- *Java* (`gremlin-driver`): Renamed: 
`maxConnectionPoolSize`->`maxConnections`, 
`connectionSetupTimeoutMillis`->`connectTimeoutMillis`,
+  `idleConnectionTimeoutMillis`->`idleTimeoutMillis`, 
`resultIterationBatchSize`->`batchSize`, `serializer`->`responseSerializer`,
+  and `RequestOptions` `addG`->`traversalSource`. New: `readTimeoutMillis`, 
`keepAliveTimeMillis`,
+  `maxResponseHeaderBytes`, `proxy(ProxyOptions)`, `url(String)`, 
`ssl(SslContext)`. Removed: `maxResponseContentLength`.
+  `validationRequest` default reconciled to `g.inject(0)`.
+- *Python* (`gremlin-python`): Renamed: `pool_size`->`max_connections` 
(default 8->128) and `ssl_options`->`ssl`.
+  New: `connect_timeout_millis`, `read_timeout_millis`, `idle_timeout_millis`, 
`keep_alive_time_millis`, `compression`,
+  `batch_size`, `proxy`, `trust_env`. `auth.sigv4` gained an optional 
credentials provider. Removed: `headers` (use interceptors)
+  and `max_content_length`.
+- *.NET* (`gremlin-dotnet`): Renamed: 
`MaxConnectionsPerServer`->`MaxConnections`, 
`ConnectionTimeout`->`ConnectTimeout`,
+  `IdleConnectionTimeout`->`IdleTimeout`, 
`KeepAliveInterval`->`KeepAliveTime`, `EnableCompression`->`Compression`, and
+  `Auth.BasicAuth`/`Auth.SigV4Auth`->`Auth.Basic`/`Auth.Sigv4`. New: 
`ReadTimeout`, `MaxResponseHeaderBytes`,
+  `Proxy`, `Ssl`, `BulkResults`.
+- *Go* (`gremlin-go`): Renamed: 
`MaximumConcurrentConnections`->`MaxConnections`, 
`IdleConnectionTimeout`->`IdleTimeout`,
+  `KeepAliveInterval`->`KeepAliveTime`, `ConnectionTimeout`->`ConnectTimeout`, 
`TlsConfig`->`Ssl`, `RequestInterceptors`->`Interceptors`,
+  `EnableCompression`->`Compression`. Auth helpers moved from package 
`gremlingo`
+  (`BasicAuth`/`SigV4Auth`/`SigV4AuthWithCredentials`) into a new `auth` 
sub-package (`auth.Basic`/`auth.SigV4`/`auth.SigV4WithCredentials`).
+  New: `ReadTimeout(Millis)`, `MaxResponseHeaderBytes`, `Proxy` (defaults to 
`http.ProxyFromEnvironment`), `BatchSize`, `BulkResults`.
+- *JavaScript* (`gremlin-javascript`): adopted `undici` as a pinned dependency 
providing the default dispatcher built from the
+  options above. Renamed: `reader`->`responseSerializer`. New: 
`readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`,
+  `proxy`, `compression`, `batchSize`, `bulkResults`, `logger`. Removed 
`headers` (use interceptors) and the
+  `ca`/`cert`/`pfx`/`rejectUnauthorized`/`agent` options (TLS is configured 
through the Node/undici runtime). undici is swapped
+  out in browser bundles, where these connection-pool options are managed by 
the browser.
+
+See: 
link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] 
Standardizing GLV connection options in TinkerPop 4].
+
+==== Renaming `evaluationTimeout` to `timeoutMillis`
+
+The per-request execution timeout is now referred to by a single name, 
`timeoutMillis`, everywhere. `timeoutMillis` is
+the maximum time in milliseconds that a request is allowed to execute on the 
server before it times out. It can be
+configured server-wide and overridden on a per-request basis. Previously the 
same concept was called
+`evaluationTimeout` in the server configuration, the `with()` script token, 
and several driver APIs. The `Millis`
+suffix aligns it with the driver connection options such as 
`connectTimeoutMillis` and `readTimeoutMillis`.
+
+This is a breaking change with no backward-compatible alias. The old 
`evaluationTimeout` name (and the long-deprecated
+`scriptEvaluationTimeout`) are no longer recognized anywhere. Update each 
surface as follows:
+
+- *Server config*: the `gremlin-server.yaml` key `evaluationTimeout` becomes 
`timeoutMillis` (default still 30000).
+- *Script token*: `g.with('evaluationTimeout', 500)` becomes 
`g.with('timeoutMillis', 500)`.
+- *Java driver*: `RequestOptions.Builder.timeout(long)` becomes 
`timeoutMillis(long)` and `getTimeout()` becomes `getTimeoutMillis()`.
+- *Go driver*: `RequestOptionsBuilder.SetEvaluationTimeout(int)` becomes 
`SetTimeoutMillis(int)`.
+- *.NET driver*: `Tokens.ArgsEvalTimeout` becomes `Tokens.ArgsTimeoutMillis` 
and `RequestMessage.Builder.AddEvaluationTimeout(...)`
+  becomes `AddTimeoutMillis(...)`.
+- *JavaScript driver*: the request option `{ evaluationTimeout: N }` becomes 
`{ timeoutMillis: N }`.
+- *Python driver*: use the token `timeoutMillis` (e.g. 
`g.with_('timeoutMillis', 500)` or `request_options={'timeoutMillis': 500}`).
+
+Driver and server should be upgraded together. A driver sending the old 
`evaluationTimeout` field to a 4.x server has
+that field silently ignored and falls back to the server's default timeout, as 
with any unrecognized request argument.
+
+==== Java Runtime Upgrade
+
+TinkerPop 4.0 raises the minimum Java version from 11 to 17 for both building 
and running, and adds support for running
+on Java 21 and Java 25. Supporting Java 25 required upgrading Groovy to 
4.0.32, Hadoop to 3.4.3, Spark to 4.1.x, and
+Netty to 4.2.x. These upgrades collectively prohibit building on Java 11, but 
enable support in Java 21 and 25.
+
+As with the earlier JDK 17 support, some libraries still rely on deep 
reflection (most notably the Kryo serialization
+library used with OLAP), so it may be necessary to `--add-opens` or 
`--add-exports` certain modules at runtime. The set
+of options used by TinkerPop's own tests is unchanged.
+
+==== .NET Runtime Upgrade
+
+The minimum target framework is now `net8.0` (previously 
`netstandard2.0;net6.0`).
+
+==== Request Interceptors
+
+When TinkerPop supported WebSockets prior to 4.0.0, the Java driver offered a 
`RequestInterceptor` interface (and
+its predecessor, `HandshakeInterceptor`) that allowed modification of the raw 
Netty `FullHttpRequest`. For WebSocket
+connections, the interceptor only ran on the initial HTTP upgrade handshake. 
With the move to HTTP, the notion of
+the "interceptor" has shifted to a per-request concern that is now 
standardized across all GLVs.
+
+All GLVs now support request interceptors, which allow modification of the 
HTTP request before it is sent to the
+server. An interceptor is a function that receives the mutable HTTP request 
object and can modify headers, the
+request body, the URI, or the HTTP method. Interceptors are run in the order 
they are registered.
+
+The most common use case for interceptors is authentication (e.g., SigV4 
signing), but they can also be used to add
+provider-specific fields, inject custom headers, or transform the request body.
+
+Here is a simple Java example that adds a custom header:
+
+[source,java]
+----
+Cluster cluster = Cluster.build("localhost")
+    .interceptors(request -> request.headers().put("X-Custom-Header", "value"))
+    .create();
+----
+
+Authentication is also an interceptor. Each GLV provides convenience methods 
(e.g., `Auth.basic()`, `Auth.sigv4()`)
+that return interceptors and can be registered alongside custom ones.
+
+For full details on the interceptor API for each language variant, refer to 
the RequestInterceptor section in
+each GLV's documentation in the
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin
 Drivers and Variants]
+reference.
+
+==== Inconfigurable Request Serialization
+
+TinkerPop 3.x drivers used a single serializer configuration (for example, 
`serializer` in the Java driver or
+`message_serializer` in Python) that controlled both how a `RequestMessage` 
was serialized on the way to the server
+and how responses were deserialized on the way back. TinkerPop 4 splits these 
concerns. Requests are now always
+serialized as JSON (`application/json`) and that behavior is not configurable. 
The remaining serializer configuration
+is named `responseSerializer` (cased idiomatically per language) and controls 
only the `Accept` header and response
+deserialization.
+
+Applications that require a different request body encoding, such as 
GraphBinary for a server that expects it, can
+register a request interceptor that serializes the `RequestMessage` and 
replaces the body and `Content-Type` header.
+See the RequestInterceptor section for each GLV in the
+link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Gremlin
 Drivers and Variants]
+reference.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3250[TINKERPOP-3250]
+
 ==== Expanding Dynamic Arguments to Additional Steps
 
 Prior to 4.0, comparing a traverser's value against a dynamically computed 
reference required several coordinated
@@ -948,18 +942,6 @@ unwrap(toInt(29));  // 29
 unwrap('hello');    // 'hello'
 ----
 
-==== Subgraph Support in GLVs
-
-All GLVs now support the `subgraph()` step. Previously, calling `subgraph()` 
from a GLV produced an unknown-type error
-because the variant could not interpret the `Graph` payload that the server 
returned. Applications can now extract a
-portion of a remote graph as part of a normal traversal and inspect its 
vertices and edges directly from the client,
-without having to re-issue queries to reconstruct the result. See: 
<<subgraph-step>>.
-
-In the GLVs, the result is a detached snapshot of the captured vertices and 
edges, not a traversable `Graph` instance.
-It cannot be passed to `traversal().with(...)`, and mutating its collections 
has no effect on the source graph. To
-re-query elements against the original graph, extract their ids and call 
`g.V(id)` or `g.E(id)` against the original
-`GraphTraversalSource`.
-
 ==== Provider Defined Types
 
 Graph providers may now expose custom types as Provider Defined Types (PDT) 
(replacing the old `CustomTypeSerializer`


Reply via email to