jazzband/django-constance

Race-condition caused by when constance registers Django checks

caioariede opened this issue · 2 comments

Describe the problem

Using django-constance with pytest causes race-condition in settings load because of a misplaced import:

from . import checks

Django documentation recommends in [link]:

Lastly, your check function must be registered explicitly with system check registry. Checks should be registered in a file that’s loaded when your application is loaded; for example, in the AppConfig.ready() method.

This is how the race-condition happens:

  1. Pytest plugin manager loads the constance/test/pytest.py file
  2. The constance/test/pytest.py file refers to constance/__init__.py in:
    from constance import config as constance_config
  3. The constance/__init__.py causes checks to be registered in:
    from . import checks
  4. Important: This happens before pytest-django can set up Django here
    1. Whenever checks are loaded, it will cascade-load Django checks as well
    2. Django checks will try to load internal stuff like caches and database
    3. Whenever Django detects cache/database isn't configured, it will configure them before knowing what the final state of settings would be
  5. Pytest plugin manager will now trigger all the hooks for plugins to load correctly (eg. pytest_configure)
  6. The pytest-django plugin finally sets settings up - this is when constance should register its checks
  7. Important: Now if you compare django.conf.settings with django.db.connections.settings they can likely be different as below:
(Pdb) from django.conf import settings
(Pdb) p settings
<dynaconf.base.LazySettings object at 0x1127de640>
(Pdb) p settings.DATABASES
<Box: {'default': {'NAME': 'local', 'USER': '', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': '', 'CONN_MAX_AGE': 500, 'ENGINE': 'django.db.backends.postgresql_psycopg2'}}>
(Pdb) from django.db import connections
(Pdb) p connections
<django.db.utils.ConnectionHandler object at 0x111fa4c40>
(Pdb) p connections.settings
{'default': {'ENGINE': 'django.db.backends.dummy', 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None, 'NAME': '', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', 'TEST': {'CHARSET': None, 'COLLATION': None, 'MIGRATE': True, 'MIRROR': None, 'NAME': None}}}

Steps to reproduce

I think this really depends on your set up. I was able to reproduce it using dynaconf probably because of the way it overrides Django settings. Nonetheless, I'm sure that if constance can load checks correctly this shouldn't be a problem.

System configuration

  • Django version: 3.2
  • Python version: 3.9
  • Django-Constance version: 2.7

@caioariede please try with the latest master if the issue was resolved.

@camilonova I'm not able to test this now but I've reviewed the PR and from the looks of it, this issue should be granted as solved. Thanks!