2

I've scoured the documentation and am looking for a Django setting that disables system checks (not just silences them) in production. I have a project with 20,000+ models which are autogenerated to create RESTful endpoints. These system checks take quite some time:

https://docs.djangoproject.com/en/1.11/ref/checks/#models

Having the systems check in development is necessary, even though it causes manage.py 20-30 minutes to fire up. However, any time I publish a new version to production, the first HTTP request to a production node takes 20-30 minutes to respond as well! I'd obviously like to avoid this, because after the initial request, the site is lightning fast.

While the answer in comments below references a solution to get runserver to come up more quickly, I'm looking for a solution for production, not our development environment.

I've looked around for a setting like DISABLED_SYSTEM_CHECKS but have only come across SILENCED_SYSTEM_CHECKS (see here), but that just seems to silence the output rather than not running the checks that take the time. Does such an animal exist? I'm running mod_wsgi in production. I've seen requires_system_checks for individual commands, but am looking for a project-wide solution. Thanks very much.

FlipperPA
  • 13,607
  • 4
  • 39
  • 71
  • Possible duplicate of [Skip system checks on Django server in DEBUG mode in Pycharm](https://stackoverflow.com/questions/41438593/skip-system-checks-on-django-server-in-debug-mode-in-pycharm) – Brown Bear Oct 01 '17 at 15:33
  • 1
    But the docs say - `For performance reasons, checks are not run as part of the WSGI stack that is used in deployment.`. – xyres Oct 01 '17 at 15:44
  • 1
    What makes you think the system checks are running at all? How are you starting your production server? Unless you're using manage.py for some reason, the checks shouldn't run. On the other hand, if you really have 20000 models the overhead of loading those is bound to have something to do with the time it takes. – Daniel Roseman Oct 01 '17 at 15:54
  • Huh, that is odd. If I disable the `include` statement to the `urls.py` with the data endpoints, it loads instantly. `mod_wsgi` is running as an Apache module, and during the publish, `wsgi.py` gets rewritten which causes `mod_wsgi` to reload. There may be no way around it; I was trying to avoid having to pull each node out of HA Proxy, publish, hit it locally with curl, and then put it back in the pool. – FlipperPA Oct 01 '17 at 17:19
  • If you have a project with 20,000+ models I'm pretty sure the problem comes from here. Not from things that come *after* that 20,000+ models. Maybe you should spend more time to find a clever way to have less models (search for model inheritance in Django), than all the time you're spending to be able to handle them. – Olivier Pons Oct 02 '17 at 09:14
  • 1
    This sounds like some awesome abuse of Django! :D – antonagestam Dec 04 '18 at 08:38
  • The 20,000+ models are auto-generated from a PG database storing raw data, to build endpoints for DRF. Inheritance isn't an option here. :) – FlipperPA Dec 04 '18 at 12:26

2 Answers2

1

You could create a DISABLE_CHECKS setting and force skip the check from within the check functions themselves. I noticed even if you set SILENCED_SYSTEM_CHECKS in settings.py, certain manage.py commands will still run the checks (such as migrate). Here is what I use:

import logging

from django.conf import settings
from django.core.checks import Error
from django.db import connections
from django.core.cache import caches

def check_cache_connectivity(app_configs, **kwargs):
    """
    Check cache
    :param app_configs:
    :param kwargs:
    :return:
    """
    errors = []

    # Short circuit here, checks still ran by manage.py cmds regardless of SILENCED_SYSTEM_CHECKS
    if settings.DISABLE_CHECKS:
        return errors

    cache_settings = settings.CACHES.keys()
    for cur_cache in cache_settings:
        try:
            key = 'check_cache_connectivity_{}'.format(cur_cache)
            caches[cur_cache].set(key, 'connectivity_ok', 30)
            value = caches[cur_cache].get(key)
            print("Cache '{}' connection ok, key '{}', value '{}'".format(cur_cache, key, value))
        except Exception as e:
            msg = "ERROR: Cache {} looks to be down. {}".format(cur_cache, e)
            print(msg)
            logging.exception(msg)
            errors.append(
                Error(
                    msg,
                    hint="Unable to connect to cache {}, set as {}. {}"
                         "".format(cur_cache, settings.CACHES[cur_cache], e),
                    obj='CACHES.{}'.format(cur_cache),
                    id='content_services.E002',
                )
            )
    return errors

I use this in my build environment where I want most if not all of my custom checks ignored. Hope it helps!

radtek
  • 34,210
  • 11
  • 144
  • 111
0

I posted this question long ago, and never posted the solution I ended up using. The root problem was that with that many models (we're now up to 60,000!), each one would be validated on load. urls.py contained all the end point routes, which would import the DRF ViewSets, which in turn would load the serializers and models up-front. So what I did was create a sort of lazy loading, and we've open sourced it:

https://pypi.org/project/automagic-rest/

The key element was modifying the ViewSet to load the model on-demand in __init__, rather than up front:

self.model = getattr(
    import_module(f"{self.python_path_name}.models.{self.schema_name}"),
    f"{self.schema_name}_{self.table_name}_model",
)

You can view the full source in context here: https://github.com/wharton/automagic-rest/blob/master/automagic_rest/views.py#L53

This required developing a convention for DRF's basename to hold the database name, app name, schema name, and model name, but it works well. The initial load time is now about 45 seconds for 60,000 models, rather than over three hours.

FlipperPA
  • 13,607
  • 4
  • 39
  • 71