Skip to content
Open
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
4 changes: 4 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ Bug fixes
:class:`~pymongo.asynchronous.cursor.AsyncCursor` to be
``Optional[Mapping[str, Any]]`` instead of ``Optional[bool]``, matching what
the argument accepts at runtime (`PYTHON-6145`_).
- Disabled TLS session reuse on Python 3.11
due to a CPython memory leak bug (`python/cpython#116810 <https://github.com/python/cpython/issues/116810>`_).
The bug was fixed in Python 3.12.7 and Python 3.13.1 (`PYTHON-6182`_).

.. _PYTHON-5929: https://jira.mongodb.org/browse/PYTHON-5929
.. _PYTHON-6172: https://jira.mongodb.org/browse/PYTHON-6172
.. _PYTHON-6149: https://jira.mongodb.org/browse/PYTHON-6149
.. _PYTHON-5873: https://jira.mongodb.org/browse/PYTHON-5873
.. _PYTHON-6145: https://jira.mongodb.org/browse/PYTHON-6145
.. _PYTHON-6182: https://jira.mongodb.org/browse/PYTHON-6182

Changes in Version 4.19.0 (2026/10/14)
--------------------------------------
Expand Down
6 changes: 5 additions & 1 deletion pymongo/pool_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
_ConnT = TypeVar("_ConnT", bound="_AgnosticBaseConnection[Any]")
_PoolT = TypeVar("_PoolT", bound="_AgnosticBasePool[Any]")

# Reading SSLSocket.session / SSLObject.session leaks a copy of the session on
# every call in CPython 3.11, so disable TLS session reuse on that version.
_TLS_SESSION_REUSE_LEAKS = sys.version_info < (3, 12)
Comment on lines +90 to +92

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agree

@NoahStapp NoahStapp Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We specifically said in our meeting that we do not distinguish between patch versions of CPython. If users want to avoid this bug on Python 3.12 or 3.13, they will have to upgrade patch versions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In that case, we should include that info in the changelog e.g.:

Disabled TLS session reuse on Python 3.11 due to a CPython memory leak bug (python/cpython#116810 <https://github.com/python/cpython/issues/116810>). The bug was fixed in Python 3.12.7 and Python 3.13.1. Python 3.12.0 through 3.12.6 and 3.13.0 still contain the bug; users on those versions should upgrade Python. (PYTHON-6182)

@NoahStapp NoahStapp Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The changelog already says "The bug was fixed in Python 3.12.7 and Python 3.13.1", which implies that Python 3.12.0 through 3.12.6 and 3.13.0 contain the bug.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The changelog already says "The bug was fixed in Python 3.12.7 and Python 3.13.1", which implies that Python 3.12.0 through 3.12.6 and 3.13.0 contain the bug.

The changelog should explicitly state that we are not addressing the bug on 3.12 - 3.12.6 and 3.13 and that "Python 3.12.0 through 3.12.6 and 3.13.0 still contain the bug; users on those versions should upgrade Python." (PYTHON-6182)" if for no other reason than Copilot may not have flagged this had we done that to begin with 😂

In other words, "You can use 3.12 with PyMongo just make sure you upgrade your Python." Otherwise, the implicit warning to not use PyMongo with those versions is more likely to go unnoticed.

Ideally we would track patch versions of Python but that's a different issue and the least we can do here is be super-clear about what's going on. 🙏



class _ConnectionTelemetryInfo(Protocol):
"""Protocol for connection fields consumed by :class:`~pymongo._telemetry._CommandTelemetry`."""
Expand Down Expand Up @@ -308,7 +312,7 @@ def __init__(
self._pending = 0
self._max_connecting = self.opts.max_connecting
self._ssl_session_cache: Optional[list[Any]] = (
[None] if self.opts._ssl_context is not None else None
[None] if self.opts._ssl_context is not None and not _TLS_SESSION_REUSE_LEAKS else None
)
self._telemetry = _CmapTelemetry(
client_id, address, options._event_listeners, publish=not is_sdam, log=not is_sdam
Expand Down
9 changes: 9 additions & 0 deletions test/asynchronous/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pymongo.errors import ConfigurationError, ConnectionFailure, OperationFailure
from pymongo.hello import HelloCompat
from pymongo.pool_shared import (
_TLS_SESSION_REUSE_LEAKS,
_configured_protocol_interface,
_configured_socket_interface,
_get_ssl_session,
Expand Down Expand Up @@ -923,11 +924,19 @@ async def test_pyopenssl_ignored_in_async(self):
await client.close()

@async_client_context.require_tls
@unittest.skipIf(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is not supported on Python 3.11")
async def test_pool_has_ssl_session_cache(self):
pool = list(self.client._topology._servers.values())[0].pool
self.assertIsInstance(pool._ssl_session_cache, list)

@async_client_context.require_tls
@unittest.skipUnless(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is enabled on Python 3.12+")
async def test_pool_has_no_ssl_session_cache_on_python_311(self):
pool = list(self.client._topology._servers.values())[0].pool
self.assertIsNone(pool._ssl_session_cache)

@async_client_context.require_tls
@unittest.skipIf(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is not supported on Python 3.11")
@unittest.skipUnless(
_IS_SYNC and _HAVE_PYOPENSSL,
"Sync stdlib ssl may return None for session on TLS 1.3; test limited to PyOpenSSL",
Expand Down
9 changes: 9 additions & 0 deletions test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pymongo.errors import ConfigurationError, ConnectionFailure, OperationFailure
from pymongo.hello import HelloCompat
from pymongo.pool_shared import (
_TLS_SESSION_REUSE_LEAKS,
_configured_socket_interface,
_get_ssl_session,
)
Expand Down Expand Up @@ -912,11 +913,19 @@ def test_pyopenssl_ignored_in_async(self):
client.close()

@client_context.require_tls
@unittest.skipIf(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is not supported on Python 3.11")
def test_pool_has_ssl_session_cache(self):
pool = list(self.client._topology._servers.values())[0].pool
self.assertIsInstance(pool._ssl_session_cache, list)

@client_context.require_tls
@unittest.skipUnless(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is enabled on Python 3.12+")
def test_pool_has_no_ssl_session_cache_on_python_311(self):
pool = list(self.client._topology._servers.values())[0].pool
self.assertIsNone(pool._ssl_session_cache)

@client_context.require_tls
@unittest.skipIf(_TLS_SESSION_REUSE_LEAKS, "TLS session reuse is not supported on Python 3.11")
@unittest.skipUnless(
_IS_SYNC and _HAVE_PYOPENSSL,
"Sync stdlib ssl may return None for session on TLS 1.3; test limited to PyOpenSSL",
Expand Down
Loading