1

I am facing an issue with my Django webapp project. I am running a containerized environment using Django, Postgres, Redis and Celery. Mainly, I want to use Redis for caching and Celery to set up live updates. So far, I have been able to connect to redis and celery and store celery task results in the redis cache. Things get messy when I try to cache pages in django using Redis. For some reason, using django's caching system (cache_page decorator) breaks my celery container.

The Error

My Celery container encounters this error:

django.core.cache.backends.base.InvalidCacheBackendError: Could not find backend 'django_redis.cache.RedisCache': No module named 'django_redis'

Here is the full traceback:

Traceback (most recent call last):

File "/usr/local/lib/python3.9/site-packages/django/core/cache/__init__.py", line 50, in _create_cache

backend_cls = import_string(backend)

File "/usr/local/lib/python3.9/site-packages/django/utils/module_loading.py", line 17, in import_string

module = import_module(module_path)

File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module

return _bootstrap._gcd_import(name[level:], package, level)

File "<frozen importlib._bootstrap>", line 1030, in _gcd_import

File "<frozen importlib._bootstrap>", line 1007, in _find_and_load

File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked

File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed

File "<frozen importlib._bootstrap>", line 1030, in _gcd_import

File "<frozen importlib._bootstrap>", line 1007, in _find_and_load

File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked

ModuleNotFoundError: No module named 'django_redis'


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

File "/usr/local/bin/celery", line 8, in <module>

sys.exit(main())

File "/usr/local/lib/python3.9/site-packages/celery/__main__.py", line 15, in main

sys.exit(_main())

File "/usr/local/lib/python3.9/site-packages/celery/bin/celery.py", line 213, in main

return celery(auto_envvar_prefix="CELERY")

File "/usr/local/lib/python3.9/site-packages/click/core.py", line 829, in __call__

return self.main(*args, **kwargs)

File "/usr/local/lib/python3.9/site-packages/click/core.py", line 782, in main

rv = self.invoke(ctx)

File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1259, in invoke

return _process_result(sub_ctx.command.invoke(sub_ctx))

File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1066, in invoke

return ctx.invoke(self.callback, **ctx.params)

File "/usr/local/lib/python3.9/site-packages/click/core.py", line 610, in invoke

return callback(*args, **kwargs)

File "/usr/local/lib/python3.9/site-packages/click/decorators.py", line 21, in new_func

return f(get_current_context(), *args, **kwargs)

File "/usr/local/lib/python3.9/site-packages/celery/bin/base.py", line 132, in caller

return f(ctx, *args, **kwargs)

File "/usr/local/lib/python3.9/site-packages/celery/bin/worker.py", line 320, in worker

worker = app.Worker(

File "/usr/local/lib/python3.9/site-packages/celery/worker/worker.py", line 94, in __init__

self.app.loader.init_worker()

File "/usr/local/lib/python3.9/site-packages/celery/loaders/base.py", line 111, in init_worker

self.import_default_modules()

File "/usr/local/lib/python3.9/site-packages/celery/loaders/base.py", line 105, in import_default_modules

raise response

File "/usr/local/lib/python3.9/site-packages/celery/utils/dispatch/signal.py", line 276, in send

response = receiver(signal=self, sender=sender, **named)

File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 82, in on_import_modules

self.worker_fixup.validate_models()

File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 121, in validate_models

run_checks()

File "/usr/local/lib/python3.9/site-packages/django/core/checks/registry.py", line 70, in run_checks

new_errors = check(app_configs=app_configs, databases=databases)

File "/usr/local/lib/python3.9/site-packages/django/core/checks/urls.py", line 13, in check_url_config

return check_resolver(resolver)

File "/usr/local/lib/python3.9/site-packages/django/core/checks/urls.py", line 23, in check_resolver

return check_method()

File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 408, in check

for pattern in self.url_patterns:

File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__

res = instance.__dict__[self.name] = self.func(instance)

File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 589, in url_patterns

patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)

File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__

res = instance.__dict__[self.name] = self.func(instance)

File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 582, in urlconf_module

return import_module(self.urlconf_name)

File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module

return _bootstrap._gcd_import(name[level:], package, level)

File "<frozen importlib._bootstrap>", line 1030, in _gcd_import

File "<frozen importlib._bootstrap>", line 1007, in _find_and_load

File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked

File "<frozen importlib._bootstrap>", line 680, in _load_unlocked

File "<frozen importlib._bootstrap_external>", line 790, in exec_module

File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed

File "/code/footballdata/urls.py", line 23, in <module>

from api import views

File "/code/api/views.py", line 21, in <module>

from .mixins import CacheMixin

File "/code/api/mixins.py", line 10, in <module>

class CacheMixin(object):

File "/code/api/mixins.py", line 16, in CacheMixin

def list(self, request):

File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 63, in _dec

return _multi_decorate(decorator, obj)

File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 47, in _multi_decorate

_update_method_wrapper(_wrapper, dec)

File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 17, in _update_method_wrapper

def dummy(*args, **kwargs):

File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 117, in _decorator

middleware = middleware_class(view_func, *m_args, **m_kwargs)

File "/usr/local/lib/python3.9/site-packages/django/middleware/cache.py", line 174, in __init__

super().__init__(get_response)

File "/usr/local/lib/python3.9/site-packages/django/middleware/cache.py", line 67, in __init__

super().__init__(get_response)

File "/usr/local/lib/python3.9/site-packages/django/middleware/cache.py", line 133, in __init__

self.cache = caches[self.cache_alias]

File "/usr/local/lib/python3.9/site-packages/django/core/cache/__init__.py", line 79, in __getitem__

cache = _create_cache(alias)

File "/usr/local/lib/python3.9/site-packages/django/core/cache/__init__.py", line 52, in _create_cache

raise InvalidCacheBackendError(

django.core.cache.backends.base.InvalidCacheBackendError: Could not find backend 'django_redis.cache.RedisCache': No module named 'django_redis'

Setup

Using Django with django-redis==4.12.1 and redis==3.5.3. Dockerized using docker-compose.

my docker-compose.yml:

version: "3.9"
   
services:
  postgresdb:
    image: postgres
    container_name: postgresdb
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    volumes: 
      - pgdata:/code/pgdata/

  redisdb:
    image: redis
    container_name: redisdb
    volumes: 
      - redisdata:/code/redisdata/

  web:
    build: .
    container_name: django
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    environment: 
      - CELERY_BROKER=redis://redisdb:6379/0
      - CELERY_BACKEND=redis://redisdb:6379/0
    ports:
      - "8000:8000"
    depends_on:
      - postgresdb
      - redisdb

  celery:
    build: .
    container_name: celery
    command: celery -A footballdata worker -l INFO
    volumes: 
      - .:/code
    depends_on: 
      - web
      - redisdb

volumes: 
  pgdata:
  redisdata:

In my settings.py:

        CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://redisdb:6379/",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        }
    }

CACHE_TTL = 30

# Celery stuff 
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER", "redis://redisdb:6379/0")
CELERY_RESULT_BACKEND = os.environ.get("CELERY_BROKER", "redis://redisdb:6379/0")
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Warsaw'

# Redis 
REDIS_HOST = 'redisdb'
REDIS_PORT = 6379

What causes the container to crash

Using django's cache_page decorator. I have a REST API which I want to connect to the cache. I cache the pages using a simple mixin:

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)

class CacheMixin(object):
    """
    Caching for REST API views.
    Caches list and retrieve results of the view for the specified TTL.
    """
    @method_decorator(cache_page(CACHE_TTL))
    def list(self, request):
        return super().list(request)

    @method_decorator(cache_page(CACHE_TTL))
    def retrieve(self, request, pk):
        return super().retrieve(request, pk)

Using this mixin breaks my celery container. When I comment out this code the containers run just fine.

From what I see, the server tries to create or connect to the cache backend that I've specified in my settings.py but doesn't find the django_redis module. Which is weird, because I have it installed and in my requirements.txt file.

What is even weirder (at least to me) is that the other containers work just fine - and the caching mixin also works! When I connect to the redis container and cache a page with this mixin, it's stored in the cache correctly. It's just that Celery breaks for some reason.

What I have tried

  1. Look around for similar issues. The closest I've found is this question. However, this issue is with the six package. My traceback is different. Apart from that, I haven't found anything that helped me.

  2. Revert to an older version of django-redis (3.6.6) and/or redis (2.10) - I have seen that some people has compatibility issues with django and redis in the past. This did not work either for me.

Has anyone had similar issues? I would appreciate any ideas.

rniem
  • 31
  • 4
  • If you are using docker-compose you might want to include that as well. The more info the better! – Danoram Feb 17 '21 at 18:55
  • @Danoram You're right - I forgot to add that info. Just added the compose file as well. – rniem Feb 17 '21 at 19:07

1 Answers1

2

Okay, I solved it. It turned out to be more simple than I thought (as it often is). I should have just listened to the error message and been aware of the place it was coming from.

What happened was that I had made some changes in my requirements file, but instead of rebuilding the containers using docker-compose up --build, I rebuilt using docker build .. So I guess the celery container was not properly updated with the required packages.

So the error was due to my lack of understanding of docker vs docker-compose, essentially.

rniem
  • 31
  • 4