fix: expand contact point hostnames to all DNS IPs at connection time (DRIVER-201) — Part 1/2#889
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to address DRIVER-201 by ensuring a hostname contact point can be tried via all DNS-resolved IPs (instead of only the first), and begins deprecating RESOLVE_CONTACT_POINTS as a no-op to preserve unresolved hostnames until connection time.
Changes:
- Deprecates
RESOLVE_CONTACT_POINTSinDefaultDriverOption/TypedDriverOptionand stops reading it inSessionBuilder(always merges contact points withresolve=false). - Adds
MetadataManager#getResolvedContactPoints()plus unit tests that validate DNS expansion / passthrough behavior. - Updates comments/tests around contact-point handling in load-balancing wrapper tests.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| core/src/main/java/com/datastax/oss/driver/internal/core/metadata/MetadataManager.java | Adds getResolvedContactPoints() that expands unresolved hostname contact points to all DNS IPs. |
| core/src/main/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapper.java | Adjusts comments around contact point handling (but currently still uses getContactPoints() in query plan construction). |
| core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java | Stops honoring RESOLVE_CONTACT_POINTS; always stores contact points as unresolved. |
| core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java | Marks RESOLVE_CONTACT_POINTS deprecated with updated Javadoc. |
| core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java | Marks RESOLVE_CONTACT_POINTS deprecated with updated Javadoc. |
| core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java | Adds tests for getResolvedContactPoints() behavior (null state, passthrough, DNS expansion). |
| core/src/test/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapperTest.java | Updates an assertion comment related to appended contact points. |
Comments suppressed due to low confidence (1)
core/src/main/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapper.java:176
- The new comment says DNS expansion is handled by ChannelFactory at connection time, but ChannelFactory.connect() currently just calls endPoint.resolve() once and bootstrap.connect(resolvedAddress), which won’t iterate over all A/AAAA records for an unresolved InetSocketAddress. Either update this logic to actually use getResolvedContactPoints() here, or implement/point to the connection-time DNS expansion in ChannelFactory; as-is, the comment (and intended fallback behavior) is incorrect.
// Use the original (potentially unresolved) contact-point endpoints so that the control
// connection channel retains the hostname, preserving hostname-based node identity in
// metadata. DNS expansion to all IPs for each hostname is handled by ChannelFactory at
// actual connection time.
Set<DefaultNode> originalNodes = context.getMetadataManager().getContactPoints();
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
🚧 Files skipped from review as they are similar to previous changes (10)
📝 WalkthroughWalkthroughThis PR deprecates the RESOLVE_CONTACT_POINTS option and defers DNS expansion of contact-point hostnames until query-plan time. It adds MetadataManager.getResolvedContactPoints(), stops address resolution during session initialization, updates LoadBalancingPolicyWrapper and other consumers to use resolved contact points, simplifies OptionalLocalDcHelper logic, and adjusts unit and integration tests to reflect DNS-expanded endpoints. Sequence DiagramsequenceDiagram
participant SessionBuilder
participant LoadBalancingPolicyWrapper
participant MetadataManager
participant DNS
SessionBuilder->>LoadBalancingPolicyWrapper: initialize with unresolved contact points
LoadBalancingPolicyWrapper->>MetadataManager: getResolvedContactPoints()
MetadataManager->>MetadataManager: iterate contactPoints
MetadataManager->>DNS: InetAddress.getAllByName(hostname)
DNS-->>MetadataManager: resolved IPs
MetadataManager-->>LoadBalancingPolicyWrapper: expanded list of nodes with resolved addresses
LoadBalancingPolicyWrapper->>LoadBalancingPolicyWrapper: build query plan with resolved nodes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
68e37e8 to
317c73c
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java (3)
493-551: ⚡ Quick winConsider adding negative test cases for DNS resolution.
The current tests cover happy-path scenarios but don't verify error handling for:
- DNS resolution failures (e.g., unknown hostname triggering
UnknownHostException)- Null or empty hostname handling
- Non-
InetSocketAddressendpoint types (if supported by the API)Adding these cases would improve robustness validation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java` around lines 493 - 551, Add negative tests to MetadataManagerTest covering DNS failures and invalid endpoints: create a test that adds an unresolved hostname that is guaranteed to fail resolution (e.g., "nonexistent.invalid") via metadataManager.addContactPoints(new DefaultEndPoint(InetSocketAddress.createUnresolved(...))) and assert getResolvedContactPoints() either returns empty or handles the UnknownHostException path as the implementation expects; add a test that passes a null/empty hostname using InetSocketAddress.createUnresolved("", 9042) and assert proper handling; and add a test that supplies a non-InetSocketAddress EndPoint (if supported by EndPoint implementations) and assert getResolvedContactPoints() rejects or skips it. Ensure tests reference getResolvedContactPoints, metadataManager.addContactPoints, DefaultEndPoint, and Node to locate behavior under test.
531-551: 💤 Low valueTest assertion doesn't verify independent expansion.
The test verifies
size >= 2(line 543), which only confirms at least one node from each contact point. However, iflocalhostresolves to multiple IPs (e.g.,127.0.0.1and::1on IPv6 systems), the test doesn't verify that all those IPs are included.This means the "independently" aspect of the test name isn't fully validated—if expansion silently dropped some IPs, the test would still pass.
Consider asserting a more specific count based on known localhost resolution behavior, or document that the test only validates minimum expansion.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java` around lines 531 - 551, The test should explicitly verify that the unresolved contact point (unresolvedEndPoint) is expanded into all of its resolved addresses rather than only asserting a minimum total size; update should_expand_multiple_contact_points_independently to resolve unresolvedEndPoint (via unresolvedEndPoint.resolve()), collect all resulting InetSocketAddress instances, and assert that for each resolved address there is a corresponding Node in metadataManager.getResolvedContactPoints() whose resolved EndPoint equals that InetSocketAddress, while still asserting that resolvedEndPoint is present (use the existing resolvedEndPoint, metadataManager.getResolvedContactPoints(), and node.getEndPoint()/resolve() to locate matches).
512-529: 💤 Low valueTest doesn't verify expansion to ALL IPs.
The test name claims
should_expand_unresolved_hostname_to_all_ips, but the assertion only checksisNotEmpty()(line 523). This doesn't verify thatInetAddress.getAllByName()actually returned multiple IPs or all available IPs for the hostname.Consider either:
- Using a hostname known to resolve to multiple IPs and asserting the exact count, or
- Renaming the test to
should_expand_unresolved_hostname_to_resolved_ipsto match what's actually verified.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java` around lines 512 - 529, The test method should either assert multiple resolved addresses or be renamed to match its current assertions: update the test in MetadataManagerTest.should_expand_unresolved_hostname_to_all_ips to either (a) use a hostname known to resolve to multiple IPs and assert the expected count returned by metadataManager.getResolvedContactPoints (ensuring each Node from getResolvedContactPoints has a resolved InetSocketAddress with the expected port), or (b) rename the method to should_expand_unresolved_hostname_to_resolved_ips and keep the current assertions that each resolved endpoint is not unresolved and uses port 9042; adjust any assertions or test data around metadataManager.addContactPoints and getResolvedContactPoints accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java`:
- Around line 938-943: Update the Javadoc for the addContactPoints method in
SessionBuilder to reflect that contact points are now stored as unresolved
hostnames and expanded to all DNS IPs at query-plan time (via
MetadataManager.getResolvedContactPoints) instead of being manually expanded
with InetAddress#getAllByName or controlled by the deprecated
advanced.resolve-contact-points option; remove or rephrase the lines that
instruct users to call InetAddress#getAllByName or reference
RESOLVE_CONTACT_POINTS/advanced.resolve-contact-points and instead document the
new resolution behavior and where users can find configuration guidance.
---
Nitpick comments:
In
`@core/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java`:
- Around line 493-551: Add negative tests to MetadataManagerTest covering DNS
failures and invalid endpoints: create a test that adds an unresolved hostname
that is guaranteed to fail resolution (e.g., "nonexistent.invalid") via
metadataManager.addContactPoints(new
DefaultEndPoint(InetSocketAddress.createUnresolved(...))) and assert
getResolvedContactPoints() either returns empty or handles the
UnknownHostException path as the implementation expects; add a test that passes
a null/empty hostname using InetSocketAddress.createUnresolved("", 9042) and
assert proper handling; and add a test that supplies a non-InetSocketAddress
EndPoint (if supported by EndPoint implementations) and assert
getResolvedContactPoints() rejects or skips it. Ensure tests reference
getResolvedContactPoints, metadataManager.addContactPoints, DefaultEndPoint, and
Node to locate behavior under test.
- Around line 531-551: The test should explicitly verify that the unresolved
contact point (unresolvedEndPoint) is expanded into all of its resolved
addresses rather than only asserting a minimum total size; update
should_expand_multiple_contact_points_independently to resolve
unresolvedEndPoint (via unresolvedEndPoint.resolve()), collect all resulting
InetSocketAddress instances, and assert that for each resolved address there is
a corresponding Node in metadataManager.getResolvedContactPoints() whose
resolved EndPoint equals that InetSocketAddress, while still asserting that
resolvedEndPoint is present (use the existing resolvedEndPoint,
metadataManager.getResolvedContactPoints(), and node.getEndPoint()/resolve() to
locate matches).
- Around line 512-529: The test method should either assert multiple resolved
addresses or be renamed to match its current assertions: update the test in
MetadataManagerTest.should_expand_unresolved_hostname_to_all_ips to either (a)
use a hostname known to resolve to multiple IPs and assert the expected count
returned by metadataManager.getResolvedContactPoints (ensuring each Node from
getResolvedContactPoints has a resolved InetSocketAddress with the expected
port), or (b) rename the method to
should_expand_unresolved_hostname_to_resolved_ips and keep the current
assertions that each resolved endpoint is not unresolved and uses port 9042;
adjust any assertions or test data around metadataManager.addContactPoints and
getResolvedContactPoints accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1d3e1e1e-4557-4536-ab87-dac1f64c0d44
📒 Files selected for processing (7)
core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.javacore/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.javacore/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.javacore/src/main/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapper.javacore/src/main/java/com/datastax/oss/driver/internal/core/metadata/MetadataManager.javacore/src/test/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapperTest.javacore/src/test/java/com/datastax/oss/driver/internal/core/metadata/MetadataManagerTest.java
4f07446 to
36e7af3
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@core/src/main/java/com/datastax/oss/driver/internal/core/loadbalancing/helper/OptionalLocalDcHelper.java`:
- Around line 85-86: The warning is triggered by comparing localDc in
OptionalLocalDcHelper.checkLocalDatacenterCompatibility against per-IP synthetic
nodes from MetadataManager.getResolvedContactPoints() whose
DefaultNode.newContactPoint() leaves datacenter null; update the compatibility
check to either iterate over MetadataManager.getContactPoints() instead of
getResolvedContactPoints(), or skip nodes where node.getDatacenter() == null
when comparing to localDc (and avoid logging those as mismatches). Locate
checkLocalDatacenterCompatibility and replace the resolved-contact-points loop
with one using getContactPoints(), or add a null-check on node.getDatacenter()
before comparing and logging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4f2c96b7-838d-4718-8fb1-41be6a1e3299
📒 Files selected for processing (7)
core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsClient.javacore/src/main/java/com/datastax/oss/driver/internal/core/loadbalancing/helper/OptionalLocalDcHelper.javacore/src/main/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapper.javacore/src/main/java/com/datastax/oss/driver/internal/core/metadata/MetadataManager.javacore/src/test/java/com/datastax/dse/driver/internal/core/insights/InsightsClientTest.javacore/src/test/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapperTest.javaintegration-tests/src/test/java/com/datastax/oss/driver/core/resolver/MockResolverIT.java
🚧 Files skipped from review as they are similar to previous changes (2)
- core/src/main/java/com/datastax/oss/driver/internal/core/metadata/MetadataManager.java
- core/src/test/java/com/datastax/oss/driver/internal/core/metadata/LoadBalancingPolicyWrapperTest.java
9c9cba9 to
4a5d3a0
Compare
… (DRIVER-201) Problem: with RESOLVE_CONTACT_POINTS=false (the default), a contact point hostname was stored as a single unresolved InetSocketAddress. The LBP query plan contained exactly one Node per hostname, so only the first IP returned by DNS was ever tried. If that IP was non-responsive the driver raised AllNodesFailedException with no fallback to other IPs. Solution (per dkropachev's architectural direction, Part 1/2): - Deprecate DefaultDriverOption.RESOLVE_CONTACT_POINTS and TypedDriverOption.RESOLVE_CONTACT_POINTS. SessionBuilder now hardcodes resolve=false so contact points are always kept as unresolved hostnames. - Add MetadataManager.getResolvedContactPoints(): calls InetAddress.getAllByName() per hostname and creates a synthetic DefaultNode per IP; already-resolved endpoints pass through unchanged. - LoadBalancingPolicyWrapper BEFORE_INIT/DURING_INIT query plans and newControlReconnectionQueryPlan() fallback both use getResolvedContactPoints() so all DNS-mapped IPs are included in the query plan. - OptionalLocalDcHelper, InsightsClient: switch to getResolvedContactPoints() for consistency. - MockResolverIT: assert address.isUnresolved() == false since endpoints are now resolved at query-plan time (hostname preserved in InetAddress). - Unit tests for MetadataManager (23) and LoadBalancingPolicyWrapper.
4a5d3a0 to
4b04062
Compare
Problem
When
RESOLVE_CONTACT_POINTS=false(the default), a contact point hostname was stored as a single unresolvedInetSocketAddress. At connection time the load-balancing query plan contained exactly oneNodeper hostname, so only the first IP returned by DNS was ever tried. If that IP was non-responsive the driver raisedAllNodesFailedExceptionwith no fallback to other IPs the hostname might resolve to.This is particularly impactful in dynamic DNS environments where a hostname maps to multiple nodes and the first one may be temporarily unavailable.
Fixes DRIVER-201.
Changes
DefaultDriverOption/TypedDriverOptionRESOLVE_CONTACT_POINTSis now@Deprecated. Contact points are always kept as unresolved hostnames; the option is a no-op.SessionBuilderRESOLVE_CONTACT_POINTSread;ContactPoints.merge()is now always called withresolve=false, deferring all DNS expansion to connection time.MetadataManagergetResolvedContactPoints()method: for each contact point backed by an unresolved hostname, callsInetAddress.getAllByName()to obtain all IPs and creates a syntheticDefaultNodefor each. Already-resolved and non-InetSocketAddressendpoints are returned as-is.LoadBalancingPolicyWrappernewQueryPlan()(inBEFORE_INIT/DURING_INITstates) andnewControlReconnectionQueryPlan()now callgetResolvedContactPoints()instead ofgetContactPoints(), so the query plan contains one entry per resolved IP. The driver naturally falls back to the next candidate if one is unreachable.Tests
MetadataManagerTestcases covering: null state, already-resolved passthrough, single-hostname DNS expansion, multi-endpoint expansion.LoadBalancingPolicyWrapperTestupdated to stubgetResolvedContactPoints().MockResolverIT.should_connect_when_first_dns_entry_is_non_responsive: 2-node CCM cluster on127.0.1.x; first DNS entry points to a non-existent IP (127.0.1.11); asserts session opens successfully against the real nodes.