happy_eyeballs: sort upstream address list once per host#46240
happy_eyeballs: sort upstream address list once per host#46240davidvo-lyft wants to merge 1 commit into
Conversation
Previously every upstream connection attempt to a multi-address host re-ran the RFC 8305 address interleave in HappyEyeballsConnectionProvider's constructor, allocating a fresh sorted vector, per family buckets, and a family order vector each time. The address list and the cluster happy eyeballs config are immutable between address list updates, so the sort now runs once: HostDescriptionImpl caches the sorted list at construction and LogicalHost recomputes it under its existing address lock when addresses are refreshed. The provider receives the pre-sorted shared vector and no longer sorts or copies it, and addressListOrNull() still returns the original raw order for its existing consumers. Connection attempt order is unchanged because the same sortAddresses function runs, just earlier and once. Fixes envoyproxy#32916 Signed-off-by: David Vo <davidvo@lyft.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
RyanTheOptimist
left a comment
There was a problem hiding this comment.
Looks good. Are the failing tests real?
|
/retest |
|
Thanks for taking a look. That failure was not related to this change: the one red test was contrib/peak_ewma's BasicLoadBalancing, which asserts power-of-two-choices spreads requests across at least 2 upstreams and that run happened to see only 1 (a randomized load-distribution check). This PR only changes how a single multi-address host's addresses are ordered for happy eyeballs and keeps the connection attempt order byte-identical; it does not touch contrib/peak_ewma or cross-host load balancing. I re-triggered CI and it is green now. |
Every upstream connection attempt to a multi-address host (logical DNS, EDS additional addresses) re-ran the RFC 8305 address interleave in the connection path:
HappyEyeballsConnectionProvider's constructor calledsortAddresses(), which per connection allocated a fresh result vector, a map of per-family buckets, and a family-order vector, then stored its own copy of the sorted list. The address list and the cluster happy_eyeballs config are immutable between address-list updates, so this was pure repeated work per connection. This change computes the sorted list once, where the address list is created or refreshed, and hands the pre-sorted shared vector to the provider. Issue #32916 asks for exactly this, and the header TODO endorsed sorting at resolution time.HostDescriptionImplcaches the sorted list as a const member at construction;LogicalHostcomputes it in its constructor and insidesetNewAddressesunder the existingaddress_lock_, so the raw and sorted lists update atomically.HostImplBasegains asortedAddressListOrNull()accessor implemented byHostImplandLogicalHost(the only two subclasses), and the staticcreateConnectionnow receives the pre-sorted vector; the provider no longer sorts or copies.addressListOrNull()still returns the original raw order for its existing consumers (admin config_dump, the HTTP/3 pool grid, and http3 conn_pool). Connection attempt order is byte-identical: the samesortAddressesfunction runs on the same input with the same config, just once and earlier.sortAddressescost (dual-family list)Commit Message: happy_eyeballs: sort upstream address list once per host
Additional Description: Tradeoffs disclosed for transparency: multi-address hosts now cache a second vector of shared_ptrs (roughly 16 bytes per address plus one control block), and a host that always dials through an HTTP/1.1 proxy redirect sorts once per address-list refresh it never uses. Both are small and bounded versus the per-connection savings.
Risk Level: Low. Behavior preserving: the identical sort function runs on identical input with the identical config, only earlier and once; the existing
sortAddressesordering tests pass unchanged.Testing:
HostImplTest.HappyEyeballsSortsAddressListOncePerHostcounts the existingsort address with happy_eyeballs configtrace event: host creation sorts exactly once and twocreateConnectioncalls sort zero times. Red before the fix (creation 0, two connections 2), green after (1, 0). Verified in the CI clang docker image.LogicalHostSortedAddressListTest.SortedListFollowsAddressUpdates: the sorted list is interleaved at construction, recomputed bysetNewAddresses, and cleared when there is no list, while the raw list order is preserved.sortAddressesordering tests inhappy_eyeballs_connection_provider_testpass unchanged (same static function), corroborating identical attempt order. Also run green:upstream_impl_test,multi_connection_base_impl_test,logical_host_test,logical_dns_cluster_test,eds_test.//test/common/network:happy_eyeballs_speed_testbenchmark reports thesortAddressescost that moves out of the connection path (2 addresses: 114 ns, 8: 250 ns, 16: 394 ns).Docs Changes: N/A
Release Notes: changelogs/current/minor_behavior_changes/happy_eyeballs__sort-address-list-once.rst
Platform Specific Features: N/A
Fixes #32916
Generative AI disclosure: Developed with AI assistance (Claude Code); I reviewed and understand every line and take full ownership of the submission.