Skip to content

Offload fallback DNS from event loops#2272

Closed
pavel-ptashyts wants to merge 5 commits into
AsyncHttpClient:mainfrom
maygemdev:perf/offload-event-loop-dns
Closed

Offload fallback DNS from event loops#2272
pavel-ptashyts wants to merge 5 commits into
AsyncHttpClient:mainfrom
maygemdev:perf/offload-event-loop-dns

Conversation

@pavel-ptashyts

@pavel-ptashyts pavel-ptashyts commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • offload AHC's default blocking fallback NameResolver when resolution starts on a Netty event loop
  • cover both redirect replay and SOCKS proxy bootstrap fallback resolution
  • add client settings to enable or disable offload and tune its worker count and bounded queue size
  • track pending resolutions so saturation and client shutdown complete them exceptionally instead of hanging
  • preserve the existing caller-thread contract for custom NameResolver implementations
  • document that the public resolver support types exist only for cross-package client wiring

Configuration

  • fallbackNameResolverOffloadEnabled defaults to false and must be enabled explicitly
  • fallbackNameResolverOffloadThreadsCount defaults to -1, which uses the configured I/O thread count
  • fallbackNameResolverOffloadQueueSize defaults to 0, which selects an automatic bounded capacity

Motivation

The legacy fallback path uses Netty's JDK-backed DefaultNameResolver. Redirect replays and SOCKS proxy bootstrap can enter that path from Netty event loops, so a cold or slow OS DNS lookup can block the loop. AddressResolverGroup users remain on the existing asynchronous path, off-loop calls retain their current behavior, and custom resolvers are not moved to an unexpected thread.

A bounded shared pool prevents an unbounded request backlog. Pending promises are tracked and failed when the pool rejects work or the client closes, including tasks removed from the queue by shutdown.

The resolver support classes are public only because client wiring crosses package boundaries. Applications should enable and tune the feature through AsyncHttpClientConfig.

Validation

  • ./mvnw -pl client -am -Dtest='AddressResolverGroupTest#defaultSocksProxyResolverUsesOffloadPool' -Dsurefire.failIfNoSpecifiedTests=false test
  • ./mvnw -pl client -Dtest='AddressResolverGroupTest#defaultNameResolverOnRedirectUsesOffloadPool+defaultNameResolverOnRedirectKeepsInlineBehavior,AsyncHttpClientDefaultsTest#testDefaultFallbackNameResolverOffloadEnabled,NameResolverOffloadTest' test
  • ./mvnw -pl client -DskipTests -Dgpg.skip=true verify

Attribution

Codex on behalf of Pavel Ptashyts

pavel-ptashyts and others added 4 commits July 20, 2026 17:01
The legacy per-request NameResolver path still executes the JDK
DefaultNameResolver on the caller thread. Redirects and SOCKS proxy
bootstrap can enter that path from a Netty event-loop thread, so a
cold or slow OS lookup can park the loop.

Add an internal resolver executor and use it only when
the fallback NameResolver path is invoked from an AHC event
loop. AddressResolverGroup users stay on the existing async path,
off-loop initial execute calls keep their current behavior, and
resolver results are completed back on the original event loop before
the request flow continues.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <codex@openai.com>
The fallback resolver pool was always enabled, matched the I/O
thread count, and used an unbounded queue. It also discarded queued
resolutions during client shutdown, which could leave their promises
incomplete.

Expose enable, thread-count, and queue-size settings. Bound
the queue, fail tracked work on shutdown, and reject overflow
deterministically. Limit automatic offload to AHC's default blocking
resolver so custom resolvers keep their existing caller-thread
contract.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <codex@openai.com>
Mark the new fallback resolver offload configuration methods as
introduced in 3.0.12 and clarify that the queue-size setting controls
queued work rather than active workers.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <codex@openai.com>
Keep fallback resolver offloading opt-in so existing clients preserve
inline resolution unless they explicitly enable the worker pool. Cover
both the disabled default and enabled behavior without leaking cached
test configuration into later tests.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <codex@openai.com>
@pavel-ptashyts
pavel-ptashyts force-pushed the perf/offload-event-loop-dns branch from 08a5adc to 2a112b8 Compare July 20, 2026 15:08
Exercise the enabled default resolver through the SOCKS bootstrap path
from an AHC event loop. This verifies that proxy-host fallback DNS
uses the configured resolver worker instead of blocking the event loop.

Document the resolver helper and accessor as intentional public
cross-package wiring while directing applications to the client config.

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <codex@openai.com>
@pavel-ptashyts
pavel-ptashyts force-pushed the perf/offload-event-loop-dns branch from 2a112b8 to 435e567 Compare July 20, 2026 15:55
@pavel-ptashyts

Copy link
Copy Markdown
Contributor Author

Closing by decision; performance-plan item 1.1 will be marked skipped.

@pavel-ptashyts
pavel-ptashyts deleted the perf/offload-event-loop-dns branch July 20, 2026 16:51
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.

1 participant