From c883012b562128091bb4b8d184032be763d495f0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 6 Sep 2024 15:38:58 -0500 Subject: [PATCH] PYTHON-4703 MongoClient should default to connect=False on FaaS environments (#1844) --- doc/changelog.rst | 7 +++++++ pymongo/asynchronous/mongo_client.py | 9 ++++++++- pymongo/synchronous/mongo_client.py | 12 ++++++++++-- test/lambda/mongodb/app.py | 4 ++++ tools/synchro.py | 3 ++- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index c5a4f47d7..6fffcdf69 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -42,6 +42,12 @@ PyMongo 4.9 brings a number of improvements including: - Fixed a bug where PyMongo would raise ``InvalidBSON: date value out of range`` when using :attr:`~bson.codec_options.DatetimeConversion.DATETIME_CLAMP` or :attr:`~bson.codec_options.DatetimeConversion.DATETIME_AUTO` with a non-UTC timezone. +- The default value for ``connect`` in ``MongoClient`` is changed to ``False`` when running on + unction-as-a-service (FaaS) like AWS Lambda, Google Cloud Functions, and Microsoft Azure Functions. + On some FaaS systems, there is a ``fork()`` operation at function + startup. By delaying the connection to the first operation, we avoid a deadlock. See + `Is PyMongo Fork-Safe`_ for more information. + Issues Resolved ............... @@ -49,6 +55,7 @@ Issues Resolved See the `PyMongo 4.9 release notes in JIRA`_ for the list of resolved issues in this release. +.. _Is PyMongo Fork-Safe : https://www.mongodb.com/docs/languages/python/pymongo-driver/current/faq/#is-pymongo-fork-safe- .. _PyMongo 4.9 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=39940 diff --git a/pymongo/asynchronous/mongo_client.py b/pymongo/asynchronous/mongo_client.py index 2af773c44..b5e73e8de 100644 --- a/pymongo/asynchronous/mongo_client.py +++ b/pymongo/asynchronous/mongo_client.py @@ -720,6 +720,10 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): .. versionchanged:: 4.7 Deprecated parameter ``wTimeoutMS``, use :meth:`~pymongo.timeout`. + + .. versionchanged:: 4.9 + The default value of ``connect`` is changed to ``False`` when running in a + Function-as-a-service environment. """ doc_class = document_class or dict self._init_kwargs: dict[str, Any] = { @@ -803,7 +807,10 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]): if tz_aware is None: tz_aware = opts.get("tz_aware", False) if connect is None: - connect = opts.get("connect", True) + # Default to connect=True unless on a FaaS system, which might use fork. + from pymongo.pool_options import _is_faas + + connect = opts.get("connect", not _is_faas()) keyword_opts["tz_aware"] = tz_aware keyword_opts["connect"] = connect diff --git a/pymongo/synchronous/mongo_client.py b/pymongo/synchronous/mongo_client.py index 6c5f68b7e..26af488ac 100644 --- a/pymongo/synchronous/mongo_client.py +++ b/pymongo/synchronous/mongo_client.py @@ -263,7 +263,8 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): aware (otherwise they will be naive) :param connect: If ``True`` (the default), immediately begin connecting to MongoDB in the background. Otherwise connect - on the first operation. + on the first operation. The default value is ``False`` when + running in a Function-as-a-service environment. :param type_registry: instance of :class:`~bson.codec_options.TypeRegistry` to enable encoding and decoding of custom types. @@ -719,6 +720,10 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): .. versionchanged:: 4.7 Deprecated parameter ``wTimeoutMS``, use :meth:`~pymongo.timeout`. + + .. versionchanged:: 4.9 + The default value of ``connect`` is changed to ``False`` when running in a + Function-as-a-service environment. """ doc_class = document_class or dict self._init_kwargs: dict[str, Any] = { @@ -802,7 +807,10 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]): if tz_aware is None: tz_aware = opts.get("tz_aware", False) if connect is None: - connect = opts.get("connect", True) + # Default to connect=True unless on a FaaS system, which might use fork. + from pymongo.pool_options import _is_faas + + connect = opts.get("connect", not _is_faas()) keyword_opts["tz_aware"] = tz_aware keyword_opts["connect"] = connect diff --git a/test/lambda/mongodb/app.py b/test/lambda/mongodb/app.py index 5840347d9..274990d3b 100644 --- a/test/lambda/mongodb/app.py +++ b/test/lambda/mongodb/app.py @@ -8,6 +8,7 @@ from __future__ import annotations import json import os +import warnings from bson import has_c as has_bson_c from pymongo import MongoClient @@ -18,6 +19,9 @@ from pymongo.monitoring import ( ServerHeartbeatListener, ) +# Ensure there are no warnings raised in normal operation. +warnings.simplefilter("error") + open_connections = 0 heartbeat_count = 0 streaming_heartbeat_count = 0 diff --git a/tools/synchro.py b/tools/synchro.py index e49405ccb..e79cfce40 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -108,7 +108,8 @@ replacements = { docstring_replacements: dict[tuple[str, str], str] = { ("MongoClient", "connect"): """If ``True`` (the default), immediately begin connecting to MongoDB in the background. Otherwise connect - on the first operation.""", + on the first operation. The default value is ``False`` when + running in a Function-as-a-service environment.""", ("Collection", "create"): """If ``True``, force collection creation even without options being set.""", ("Collection", "session"): """A