net/docs/address-sorter-cache.md
Active Design & Implementation
Reduce DNS resolution and connection establishment latency by caching UDP
connect() results in
net::AddressSorterPosix.
On Android, UDP connect() calls incur a median latency of 817 microseconds and
a mean of 2.6 milliseconds, blocking the network IO thread. Since
AddressSorterPosix invokes connect() to determine route reachability and
source IP selection for candidate destination IPs, caching these results
significantly improves browser resolution latency.
When resolving hostnames with multiple IP addresses,
AddressSorter performs destination address sorting
(RFC 3484 / RFC 6724). On POSIX platforms,
AddressSorterPosix::Sort() evaluates
destinations by:
DatagramClientSocket.ConnectAsync() to the destination (using port 80 if port 0 is
provided).GetLocalAddress() to discover the OS-selected source
IPAddress.This process causes high socket churn and redundant connect() system calls
during page loads.
We introduce a bounded LRU cache inside
AddressSorterPosix:
std::pair<IPAddress, NetworkAnonymizationKey>.
/24 prefix for IPv4 (and IPv4-mapped IPv6) and to a /64 prefix for
IPv6. Standard operating system routing tables operate at a subnet level;
therefore, IPs within the same subnet share the same route, outgoing
interface, and selected local source IP.NetworkAnonymizationKey prevents
cross-origin timing attacks where a malicious top-level site uses the cache
speedup as a browser history oracle.4096 entries (approximately 400 KB memory
footprint) provides an excellent hit rate for multi-tab sessions.THREAD_CHECKER(thread_checker_).AddressSorter::Sort() is updated to accept
const NetworkAnonymizationKey& anonymization_key.
ConnectAsync() entirely,
populating the route reachability and source address from the cache.ConnectAsync().
ConnectAsync() completes synchronously, the result is immediately
cached inline. This allows duplicate subnets within the same Sort()
invocation to hit the cache.net::ERR_IO_PENDING, the result is cached in the
asynchronous completion callback.Sort() requests for the same subnet are not
coalesced. The minor overhead of occasional redundant local connect() calls
is preferred over the high complexity of managing pending callback queues.To prevent serving stale routing decisions after a network change (e.g., switching from Wi-Fi to Cellular, or connecting to a VPN):
AddressSorterPosix implements both
IPAddressObserver and
NetworkChangeObserver.connect_cache_.Clear()) on both
OnIPAddressChanged() and OnNetworkChanged().The cache is guarded by the net::features::kAddressSorterConnectCache feature
flag (disabled by default). When disabled, AddressSorterPosix falls back to
the original un-cached behavior.
All existing tests in
net/dns/address_sorter_posix_unittest.cc
are parameterized to run twice: with caching enabled and with caching disabled.
We verify:
OnIPAddressChanged() and OnNetworkChanged() empty the
cache.NetworkAnonymizationKeys partition cached entries./24 or /64 subnet successfully hit
the same cache entry, while differing subnets do not.