-
Notifications
You must be signed in to change notification settings - Fork 26
INTPYTHON-574 Add support for connection pooling #290
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,6 +89,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |
"endswith": "LIKE '%%' || {}", | ||
"iendswith": "LIKE '%%' || UPPER({})", | ||
} | ||
_connection_pools = {} | ||
|
||
def _isnull_operator(a, b): | ||
is_null = { | ||
|
@@ -176,7 +177,12 @@ def get_connection_params(self): | |
|
||
@async_unsafe | ||
def get_new_connection(self, conn_params): | ||
return MongoClient(**conn_params, driver=self._driver_info()) | ||
if self.alias not in self._connection_pools: | ||
conn = MongoClient(**conn_params, driver=self._driver_info()) | ||
# setdefault() ensures that multiple threads don't set this in | ||
# parallel. | ||
self._connection_pools.setdefault(self.alias, conn) | ||
return self._connection_pools[self.alias] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it ever possible for this cache to become out of sync? Does the user ever have raw access to the MongoClient (where they could call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the |
||
|
||
def _driver_info(self): | ||
if not os.environ.get("RUNNING_DJANGOS_TEST_SUITE"): | ||
|
@@ -192,11 +198,28 @@ def _rollback(self): | |
def set_autocommit(self, autocommit, force_begin_transaction_with_broken_autocommit=False): | ||
self.autocommit = autocommit | ||
|
||
def _close(self): | ||
# Normally called by close(), this method is also called by some tests. | ||
pass | ||
|
||
@async_unsafe | ||
def close(self): | ||
super().close() | ||
self.validate_thread_sharing() | ||
# MongoClient is a connection pool and, unlike database drivers that | ||
# implement PEP 249, shouldn't be closed by connection.close(). | ||
|
||
def close_pool(self): | ||
"""Close the MongoClient.""" | ||
connection = self.connection | ||
if connection is None: | ||
return | ||
# Remove all references to the connection. | ||
self.connection = None | ||
with contextlib.suppress(AttributeError): | ||
del self.database | ||
del self._connection_pools[self.alias] | ||
# Then close it. | ||
connection.close() | ||
|
||
@async_unsafe | ||
def cursor(self): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
================== | ||
Database reference | ||
================== | ||
|
||
This document supplements :doc:`Django's documentation on databases | ||
<django:ref/databases>`. | ||
|
||
Persistent connections | ||
====================== | ||
|
||
Persistent connections avoid the overhead of reestablishing a connection to | ||
the database in each HTTP request. They're normally controlled by the | ||
:setting:`CONN_MAX_AGE` parameter which defines the maximum lifetime of a | ||
connection. However, this parameter is unnecessary and has no effect with | ||
Django MongoDB Backend because Django's API for connection-closing | ||
(``django.db.connection.close()``) has no effect. In other words, persistent | ||
connections are enabled by default. | ||
|
||
.. versionadded:: 5.2.0b0 | ||
|
||
Support for connection pooling was added. In older versions, use | ||
:setting:`CONN_MAX_AGE` to enable persistent connections. | ||
|
||
.. _connection-management: | ||
|
||
Connection management | ||
===================== | ||
|
||
Django uses this backend to open a connection pool to the database when it | ||
first makes a database query. It keeps this pool open and reuses it in | ||
subsequent requests. | ||
|
||
The underlying :class:`~pymongo.mongo_client.MongoClient` takes care connection | ||
management, so the :setting:`CONN_HEALTH_CHECKS` setting is unnecessary and has | ||
no effect. | ||
|
||
Django's API for connection-closing (``django.db.connection.close()``) has no | ||
effect. Rather, if you need to close the connection pool, use | ||
``django.db.connection.close_pool()``. | ||
|
||
.. versionadded:: 5.2.0b0 | ||
|
||
Support for connection pooling and ``connection.close_pool()`` were added. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,5 +7,6 @@ API reference | |
|
||
models/index | ||
forms | ||
database | ||
django-admin | ||
utils |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Django (or Django apps) commonly use fork() or multiprocessing? If so we should consider clearing this cache in the child process. Perhaps using
https://docs.python.org/3/library/os.html#os.register_at_fork:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not something I've had experience with. Google AI overview says:
Django, when deployed using WSGI servers like Gunicorn or uWSGI, operates in a prefork model. This means the server forks multiple worker processes to handle incoming requests concurrently. Each worker process runs independently, allowing Django to manage multiple requests simultaneously. However, directly using os.fork within a Django application is generally discouraged due to potential conflicts with Django's request handling and database connections. For managing background tasks or parallel processing, libraries like multiprocessing or task queues such as Celery are recommended instead of direct forking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay sounds good to me. We can reevaluate if someone reports it as a problem.