Skip to content

Making Transport class thread safe and fix leaking connection#132

Closed
alxtkr77 wants to merge 3 commits intov3io:developmentfrom
alxtkr77:ML-9429
Closed

Making Transport class thread safe and fix leaking connection#132
alxtkr77 wants to merge 3 commits intov3io:developmentfrom
alxtkr77:ML-9429

Conversation

@alxtkr77
Copy link
Contributor

@alxtkr77 alxtkr77 commented Mar 3, 2025

@assaf758 assaf758 requested a review from gtopper March 3, 2025 10:36
with self._lock:
if self._closed:
raise RuntimeError("Transport closed during response handling") from e
connection = self._create_connection(self._host, self._ssl_context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem like this line belongs in the locked section.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be and it does not affect the performance. Eventually it calls http.client.HTTPConnection, which should be thread safe, but since all it does is initializing some structures without actually doing any connecting, I prefer to have it under the lock.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why should it be under the lock if it doesn't require locking?


starting_offset = request.body.tell() if is_body_seekable else 0
retries_left = self._request_max_retries
current_connection = connection # Track the current connection for thread safety
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but aliasing connection as current_connection doesn't seem to have any effect in this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aliasing of current_connection = connection is useful because it creates a mutable reference that can be updated during retry attempts while preserving the original parameter. This allows you to track which connection is active at any point in the retry loop, especially when you need to create new connections after failures.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the original connection variable is never used after aliasing.

with self._lock:
if self._closed:
raise RuntimeError("Transport closed during request retry") from e
current_connection = self._create_connection(self._host, self._ssl_context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, it doesn't seem like creating the connection belongs in a locked section.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above

Comment on lines +78 to +79
with self._lock:
if self._closed:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using double-checked locking, especially here since it's on the hot path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since all the locked sections are not performing any time consuming operations - even on the fast path - I don't see a reason to use double-checked locking

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double-checked locking is always only to avoid locking unnecessarily, unrelated to the content of the block.

Comment on lines +145 to +154
# Return connection to pool even on HTTP errors, as the connection is still valid
with self._lock:
if not self._closed:
try:
self._free_connections.put(connection, block=False)
except Exception as e:
self._logger.warn_with(
"Failed to return connection to pool", exception=str(e), connection_id=id(connection)
)
connection.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, this change would introduce a connection leak rather than fixing one.

@alxtkr77 alxtkr77 closed this Mar 17, 2025
@gtopper
Copy link
Member

gtopper commented Mar 18, 2025

Replaced by #133.

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.

2 participants