Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up the ConnectionKey #9365

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/9365.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changed ``ClientRequest.connection_key`` to be a `NamedTuple` to improve client performance -- by :user:`bdraco`.
24 changes: 15 additions & 9 deletions aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
Iterable,
List,
Mapping,
NamedTuple,
Optional,
Tuple,
Type,
Expand Down Expand Up @@ -150,8 +151,13 @@ def check(self, transport: asyncio.Transport) -> None:
SSL_ALLOWED_TYPES = (bool,)


@dataclasses.dataclass(frozen=True)
class ConnectionKey:
_SSL_SCHEMES = frozenset(("https", "wss"))


# ConnectionKey is a NamedTuple because it is used as a key in a dict
# and a set in the connector. Since a NamedTuple is a tuple it uses
# the fast native tuple __hash__ and __eq__ implementation in CPython.
class ConnectionKey(NamedTuple):
bdraco marked this conversation as resolved.
Show resolved Hide resolved
# the key should contain an information about used proxy / TLS
# to prevent reusing wrong connections from a pool
host: str
Expand Down Expand Up @@ -287,24 +293,24 @@ def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None:
writer.add_done_callback(self.__reset_writer)

def is_ssl(self) -> bool:
return self.url.scheme in ("https", "wss")
return self.url.scheme in _SSL_SCHEMES

@property
def ssl(self) -> Union["SSLContext", bool, Fingerprint]:
return self._ssl

@property
def connection_key(self) -> ConnectionKey:
proxy_headers = self.proxy_headers
if proxy_headers:
if proxy_headers := self.proxy_headers:
h: Optional[int] = hash(tuple(proxy_headers.items()))
else:
h = None
url = self.url
return ConnectionKey(
self.host,
self.port,
self.is_ssl(),
self.ssl,
url.raw_host or "",
url.port,
url.scheme in _SSL_SCHEMES,
self._ssl,
self.proxy,
self.proxy_auth,
h,
Expand Down
5 changes: 2 additions & 3 deletions aiohttp/connector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import asyncio
import dataclasses
import functools
import logging
import random
Expand Down Expand Up @@ -1312,8 +1311,8 @@ async def _create_proxy_connection(
# asyncio handles this perfectly
proxy_req.method = hdrs.METH_CONNECT
proxy_req.url = req.url
key = dataclasses.replace(
req.connection_key, proxy=None, proxy_auth=None, proxy_headers_hash=None
key = req.connection_key._replace(
proxy=None, proxy_auth=None, proxy_headers_hash=None
)
conn = Connection(self, key, proto, self._loop)
proxy_resp = await proxy_req.send(conn)
Expand Down
Loading