Skip to content

Duplicate DNS records cause repeated connection attempts to the same address #989

Description

@nikagra

Problem

ChannelFactory.resolveCandidates() hands the address list returned by Netty's
AddressResolverGroup straight to reattachHostnames() and rotate() without collapsing
duplicates. If a name maps to the same address more than once, that address is attempted more than
once within a single logical connect().

Each redundant attempt costs a full advanced.connection.connect-timeout when the address is not
answering, and because rotate() sorts candidates by toString() the duplicates end up adjacent,
so the stalls are consecutive. ChannelFactory#tryNextCandidate already documents a worst case of
N × connect-timeout for a name with N addresses; duplicates inflate that N with attempts that
cannot tell the driver anything it does not already know from the first one.

Where duplicates come from in practice: a zone file with a repeated A record, or a custom
AddressResolverGroup that stitches results together from more than one source.

Found while reviewing #890 (DRIVER-201, multi-address DNS resolution), which introduced this code
path. Deliberately left out of that PR to keep it scoped — duplicate A records are unusual, no
reviewer raised it, and #890 was close to being signed off. This issue is that follow-up.

Fix sketch

Collapse after reattachHostnames() and before rotate():

List<SocketAddress> unique = new ArrayList<>(new LinkedHashSet<>(reattachHostnames(address, addresses)));
result.complete(rotate(address, unique));

Two things make this safe:

  • LinkedHashSet collapses what we want it to. InetSocketAddress.equals compares the address
    bytes and the port and ignores the attached host name, so two candidates for the same IP and port
    are equal even if the resolver labelled them differently. Doing it after reattachHostnames()
    also means the surviving entry carries the queried name, as every other candidate does.
  • Retrying the same address is not this loop's job. The candidate loop exists to try
    different addresses; recovering from a transient failure at the same address is what the
    reconnection schedule is for. So there is no behaviour worth preserving here.

Order is preserved, so nothing about rotation determinism changes.

Test

ChannelFactoryMultiAddressTest already covers the candidate loop with a stub resolver
(TestAddressResolverGroup) that answers with a fixed list, so a case that answers with the same
address twice and asserts a single connection attempt fits alongside the existing tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions