Skip to content

fix: release pre-acquired stream IDs - #964

Closed
arnabnandy7 wants to merge 1 commit into
scylladb:scylla-4.xfrom
arnabnandy7:fix/pre-acquired-stream-id-leak
Closed

fix: release pre-acquired stream IDs#964
arnabnandy7 wants to merge 1 commit into
scylladb:scylla-4.xfrom
arnabnandy7:fix/pre-acquired-stream-id-leak

Conversation

@arnabnandy7

Copy link
Copy Markdown

Description

What changed

  • Release pre-acquired stream IDs when DriverChannel.write() rejects a request before submitting it to the Netty pipeline.
  • Release reservations when synchronous request construction fails before write() is called.
  • Apply the same handling to:
    • CQL requests
    • prepare requests and secondary prepares
    • graph requests
    • continuous paging requests
  • Release the reservation when a continuous paging callback completes after channel acquisition but before submission.
  • Add regression coverage and update the changelog.

Root cause

Channel selection calls preAcquireId() before returning a channel. Request handlers then perform additional work before the resulting RequestMessage reaches InFlightHandler.

If that work throws, or DriverChannel.write() rejects the request before pipeline submission, InFlightHandler never sees the request and therefore cannot release the reservation. Repeated failures can cause available stream-ID accounting to drift and eventually make healthy channels appear saturated.

Impact

Every successful stream-ID pre-acquisition now ends in one of two outcomes:

  • the request is submitted to InFlightHandler; or
  • the reservation is explicitly released.

Fixes #947.

Validation

mvn -pl core -Dtest=DriverChannelTest,CqlRequestHandlerTest,CqlPrepareHandlerTest test
  • 18 tests passed
  • Formatter and Error Prone checks passed under JDK 17
  • git diff --check passed

Signed-off-by: Arnab Nandy <arnab_nandy7@yahoo.com>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@arnabnandy7, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd67cdf2-932d-40fe-be27-c20c265e9794

📥 Commits

Reviewing files that changed from the base of the PR and between 3cafb2a and 5900e56.

📒 Files selected for processing (8)
  • changelog/README.md
  • core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousRequestHandlerBase.java
  • core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestHandler.java
  • core/src/main/java/com/datastax/oss/driver/internal/core/channel/DriverChannel.java
  • core/src/main/java/com/datastax/oss/driver/internal/core/channel/InFlightHandler.java
  • core/src/main/java/com/datastax/oss/driver/internal/core/cql/CqlPrepareHandler.java
  • core/src/main/java/com/datastax/oss/driver/internal/core/cql/CqlRequestHandler.java
  • core/src/test/java/com/datastax/oss/driver/internal/core/channel/DriverChannelTest.java

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arnabnandy7

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

@dkropachev

Copy link
Copy Markdown

Closing in favor of #965. It covers the same #947 stream-id leak on scylla-4.x, includes the additional pre-acquired-id cleanup paths, and has focused test coverage plus the current CI compatibility fixes.

@dkropachev dkropachev closed this Jul 20, 2026
nikagra added a commit to nikagra/java-driver that referenced this pull request Aug 3, 2026
Fills in the full DRIVER_CONFIG JSON report in the approved v2
cross-driver schema shape, replacing the stage-1 {"version":1}
placeholder. All groups are populated from Configuration and Policies
when the report is built, i.e. once per Cluster as it initializes.

Adds public getters to DCAwareRoundRobinPolicy and
RackAwareRoundRobinPolicy needed to report node-location-preference
and dc-failover, and makes PagingOptimizingLoadBalancingPolicy
implement ChainableLoadBalancingPolicy so the reporter can unwrap the
LB policy Cluster.Manager wraps at runtime.

Caps the report at 32KiB of UTF-8 (MAX_DRIVER_CONFIG_LENGTH), matching
the 4.x sibling PR scylladb#968, gocql scylladb#964 and csharp-driver scylladb#262. Beyond
cross-driver parity this is a correctness fix: CBUtil.writeString
writes each STARTUP value with a 16-bit length prefix and no bounds
check, so a value over 65535 bytes truncates the prefix modulo 65536
while still appending the whole body -- a corrupt frame and a failed
handshake, and not something the fail-safe try/catch can contain since
nothing throws. Parts of the report are user-supplied and unbounded
(DC/rack names, consistency levels, custom policy class names). Over
the limit means WARN and no DRIVER_CONFIG.

Hardens the other two ways reporting could break a connection rather
than merely fail to report:

- The fail-safe catch also covers InternalError, since customPolicy()
  calls getClass().getSimpleName() on arbitrary user policy objects
  (documented JDK edge case for certain synthetic classes). Not a bare
  Error, so OutOfMemoryError/StackOverflowError still surface.
- The load balancing policy chain walk is bounded at 16 policies and
  shared by both callers. It follows getChildPolicy() on arbitrary user
  policies, so a cyclic chain used to spin forever on the Cluster
  initialization path -- the one failure mode the try/catch cannot
  contain, because it hangs rather than throws.

Adds a JSON-Schema conformance test suite (mirroring the 4.x sibling
PR scylladb#968): the normative schema is shipped as a test resource and
validated via com.networknt:json-schema-validator (pinned to 1.5.x,
the last line still targeting Java 8), covering every discriminated-
union branch and optional group the 3.x reporter can emit, plus a
negative test proving additionalProperties=false is enforced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nikagra added a commit to nikagra/java-driver that referenced this pull request Aug 3, 2026
Fills in the full DRIVER_CONFIG JSON report in the approved v2
cross-driver schema shape, replacing the stage-1 {"version":1}
placeholder. All groups are populated from Configuration and Policies
when the report is built, i.e. once per Cluster as it initializes.

Adds public getters to DCAwareRoundRobinPolicy and
RackAwareRoundRobinPolicy needed to report node-location-preference
and dc-failover, and makes PagingOptimizingLoadBalancingPolicy
implement ChainableLoadBalancingPolicy so the reporter can unwrap the
LB policy Cluster.Manager wraps at runtime.

Caps the report at 32KiB of UTF-8 (MAX_DRIVER_CONFIG_LENGTH), matching
the 4.x sibling PR scylladb#968, gocql scylladb#964 and csharp-driver scylladb#262. Beyond
cross-driver parity this is a correctness fix: CBUtil.writeString
writes each STARTUP value with a 16-bit length prefix and no bounds
check, so a value over 65535 bytes truncates the prefix modulo 65536
while still appending the whole body -- a corrupt frame and a failed
handshake, and not something the fail-safe try/catch can contain since
nothing throws. Parts of the report are user-supplied and unbounded
(DC/rack names, consistency levels, custom policy class names). Over
the limit means WARN and no DRIVER_CONFIG.

Hardens the other two ways reporting could break a connection rather
than merely fail to report:

- The fail-safe catch also covers InternalError, since customPolicy()
  calls getClass().getSimpleName() on arbitrary user policy objects
  (documented JDK edge case for certain synthetic classes). Not a bare
  Error, so OutOfMemoryError/StackOverflowError still surface.
- The load balancing policy chain walk is bounded at 16 policies and
  shared by both callers. It follows getChildPolicy() on arbitrary user
  policies, so a cyclic chain used to spin forever on the Cluster
  initialization path -- the one failure mode the try/catch cannot
  contain, because it hangs rather than throws.

A custom load balancing policy is now named after the policy the user
configured rather than PagingOptimizingLoadBalancingPolicy. Cluster
.Manager wraps every session's policy in that internal class, and it is
the outermost element of the chain, so every custom policy was reported
as {"type":"custom","name":"PagingOptimizingLoadBalancingPolicy"}.

Adds a JSON-Schema conformance test suite (mirroring the 4.x sibling
PR scylladb#968): the normative schema is shipped as a test resource and
validated via com.networknt:json-schema-validator (pinned to 1.5.x,
the last line still targeting Java 8), covering every discriminated-
union branch and optional group the 3.x reporter can emit, plus a
negative test proving additionalProperties=false is enforced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4.x: Pre-acquired stream id can leak when request fails before InFlightHandler write

2 participants