Skip to content

Commit 010caab

Browse files
ojiiasvetlov
authored andcommitted
Fix #4013 _resolve_host race conditions (#4014)
1 parent 8ee3e3d commit 010caab

File tree

4 files changed

+42
-6
lines changed

4 files changed

+42
-6
lines changed

CHANGES/4013.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed race conditions in _resolve_host caching and throttling when tracing is enabled.

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Jian Zeng
134134
Jinkyu Yi
135135
Joel Watts
136136
Jon Nabozny
137+
Jonas Obrist
137138
Joongi Kim
138139
Josep Cugat
139140
Joshu Coats

aiohttp/connector.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -778,24 +778,28 @@ async def _resolve_host(self,
778778

779779
if (key in self._cached_hosts) and \
780780
(not self._cached_hosts.expired(key)):
781+
# get result early, before any await (#4014)
782+
result = self._cached_hosts.next_addrs(key)
781783

782784
if traces:
783785
for trace in traces:
784786
await trace.send_dns_cache_hit(host)
785-
786-
return self._cached_hosts.next_addrs(key)
787+
return result
787788

788789
if key in self._throttle_dns_events:
790+
# get event early, before any await (#4014)
791+
event = self._throttle_dns_events[key]
789792
if traces:
790793
for trace in traces:
791794
await trace.send_dns_cache_hit(host)
792-
await self._throttle_dns_events[key].wait()
795+
await event.wait()
793796
else:
797+
# update dict early, before any await (#4014)
798+
self._throttle_dns_events[key] = \
799+
EventResultOrError(self._loop)
794800
if traces:
795801
for trace in traces:
796802
await trace.send_dns_cache_miss(host)
797-
self._throttle_dns_events[key] = \
798-
EventResultOrError(self._loop)
799803
try:
800804

801805
if traces:

tests/test_connector.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
from aiohttp import client, web
1919
from aiohttp.client import ClientRequest, ClientTimeout
2020
from aiohttp.client_reqrep import ConnectionKey
21-
from aiohttp.connector import Connection, _DNSCacheTable
21+
from aiohttp.connector import Connection, TCPConnector, _DNSCacheTable
2222
from aiohttp.helpers import PY_37
23+
from aiohttp.locks import EventResultOrError
2324
from aiohttp.test_utils import make_mocked_coro, unused_port
2425
from aiohttp.tracing import Trace
2526
from conftest import needs_unix
@@ -2257,3 +2258,32 @@ def test_next_addrs_single(self, dns_cache_table) -> None:
22572258

22582259
addrs = dns_cache_table.next_addrs('foo')
22592260
assert addrs == ['127.0.0.1']
2261+
2262+
2263+
async def test_connector_cache_trace_race():
2264+
class DummyTracer:
2265+
async def send_dns_cache_hit(self, *args, **kwargs):
2266+
connector._cached_hosts.remove(("", 0))
2267+
2268+
token = object()
2269+
connector = TCPConnector()
2270+
connector._cached_hosts.add(("", 0), [token])
2271+
2272+
traces = [DummyTracer()]
2273+
assert await connector._resolve_host("", 0, traces) == [token]
2274+
2275+
2276+
async def test_connector_throttle_trace_race(loop):
2277+
key = ("", 0)
2278+
token = object()
2279+
2280+
class DummyTracer:
2281+
async def send_dns_cache_hit(self, *args, **kwargs):
2282+
event = connector._throttle_dns_events.pop(key)
2283+
event.set()
2284+
connector._cached_hosts.add(key, [token])
2285+
2286+
connector = TCPConnector()
2287+
connector._throttle_dns_events[key] = EventResultOrError(loop)
2288+
traces = [DummyTracer()]
2289+
assert await connector._resolve_host("", 0, traces) == [token]

0 commit comments

Comments
 (0)