#34914: Postgres connection is not restarted after it's dropped by the server, 
even
if we use CONN_HEALTH_CHECKS=True
-------------------------------------+-------------------------------------
               Reporter:  Vitor      |          Owner:  nobody
  Menegat                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  4.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 I'm using a Django management command to start a container that executes
 DB queries (without any transaction scope): `python manage.py <mycommand>`

 These are the relevant database configs I'm using:
 {{{
 DATABASES = {
     "default": env.db("DATABASE_URL"),
 }

 DATABASES["default"]["ATOMIC_REQUESTS"] = True
 DATABASES["default"]["CONN_HEALTH_CHECKS"] = True
 DATABASES["default"]["CONN_MAX_AGE"] = None                  ### I've also
 tried setting this to 0. Same issue
 }}}

 When Django loses the DB connection for whatever reason, I see the error
 `server closed the connection unexpectedly`, but then the error
 `psycopg2.InterfaceError: connection already closed` happens over and over
 again, every time it tries to execute a SQL query. This is very annoying
 because the container needs to be re-created in other for the app to
 function properly.

 Investigating this I found here that the connection health check is
 SKIPPED if
 
(https://github.com/django/django/blob/main/django/db/backends/base/base.py#L576-L580):
 * self.connection is None; OR
 * self.health_checks_enabled = False; OR
 * self.health_check_done = True

 I created a test command to understand what was happening with the
 connection, added some logs to see the connection attributes, and found
 that the connection health check is always skipped because
 `self.health_check_done` is never set to False.

 **My question is:** shouldn't the connection be health checked when
 `self.errors_occurred` flips to True?

 Here's the code I used to test:

 {{{
 import logging
 import time

 from django.core.management import BaseCommand
 from django.db import connection


 logger = logging.getLogger(__name__)


 class Command(BaseCommand):
     help = ""

     def handle(self, *args, **kwargs):
         while True:
             time.sleep(1)
             try:
                 logger.info(f"health_check_enabled:
 {connection.health_check_enabled}")
                 logger.info(f"health_check_done:
 {connection.health_check_done}")
                 logger.info(f"errors_occurred:
 {connection.errors_occurred}")
                 with connection.cursor() as cursor:
                     cursor.execute("SELECT 1")
                 logger.info("query executed")
             except Exception as e:
                 logger.exception(e)
 }}}

 Here's the relevant part of the logs:

 {{{
 health_check_enabled: False   ## the connect() method was not yet called
 health_check_done: False      ## the connect() method was not yet called
 errors_occurred: False

 query executed

 health_check_enabled: True
 health_check_done: True
 errors_occurred: False

 server closed the connection unexpectedly
         This probably means the server terminated abnormally
         before or while processing the request.
 Traceback (most recent call last):
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/utils.py", line 87, in _execute
     return self.cursor.execute(sql)
            ^^^^^^^^^^^^^^^^^^^^^^^^
 psycopg2.OperationalError: server closed the connection unexpectedly
     This probably means the server terminated abnormally
     before or while processing the request.
 [... Traceback omitted ...]

 health_check_enabled: True
 health_check_done: True
 errors_occurred: True         ## This is set to True now, as expected

 Traceback (most recent call last):
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/base/base.py", line 308, in _cursor
     return self._prepare_cursor(self.create_cursor(name))
                                 ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py",
 line 26, in inner
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/postgresql/base.py", line 330, in
 create_cursor
     cursor = self.connection.cursor()
              ^^^^^^^^^^^^^^^^^^^^^^^^
 psycopg2.InterfaceError: connection already closed
 [...Traceback omitted ...]

 health_check_enabled: True
 health_check_done: True
 errors_occurred: True

 Traceback (most recent call last):
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/base/base.py", line 308, in _cursor
     return self._prepare_cursor(self.create_cursor(name))
                                 ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py",
 line 26, in inner
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/postgresql/base.py", line 330, in
 create_cursor
     cursor = self.connection.cursor()
              ^^^^^^^^^^^^^^^^^^^^^^^^
 psycopg2.InterfaceError: connection already closed
 [... Traceback omitted ...]

 health_check_enabled: True
 health_check_done: True
 errors_occurred: True

 Traceback (most recent call last):
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/base/base.py", line 308, in _cursor
     return self._prepare_cursor(self.create_cursor(name))
                                 ^^^^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-packages/django/utils/asyncio.py",
 line 26, in inner
     return func(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^
   File "/usr/local/lib/python3.11/site-
 packages/django/db/backends/postgresql/base.py", line 330, in
 create_cursor
     cursor = self.connection.cursor()
              ^^^^^^^^^^^^^^^^^^^^^^^^
 psycopg2.InterfaceError: connection already closed
 [... Traceback omitted ...]
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34914>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018b49aeb9eb-baff4e05-6438-44e2-aafc-38056258ad9c-000000%40eu-central-1.amazonses.com.

Reply via email to