Search code examples
djangorediscelerydigital-oceancelerybeat

Django Celery with Redis Issues on Digital Ocean App Platform


After quite a bit of trial and error and a step by step attempt to find solutions I thought I share the problems here and answer them myself according to what I've found. There is not too much documentation on this anywhere except small bits and pieces and this will hopefully help others in the future.

Please note that this is specific to Django, Celery, Redis and the Digital Ocean App Platform.

This is mostly about the below errors and further resulting implications:

OSError: [Errno 38] Function not implemented

and

Cannot connect to redis://......

The first error happens when you try run the celery command celery -A your_app worker --beat -l info or similar on the App Platform. It appears that this is currently not supported on digital ocean. The second error occurs when you make a number of potential mistakes.


Solution

  • PART 1:

    While Digital Ocean might remedy this in the future here is an approach that will offer a workaround. The problem is the not supported execution pool. Google "celery execution pools" if you want to know more and how they work. The default one is prefork. But what you need is either gevent or eventlet. I went with the former for my purposes.

    Whichever you pick you will have to install it as it doesn't come with celery by default. In my case it was: pip install gevent (and don't forget adding it to your requirements as well).

    Once you have that you can re-run the celery command but note that gevent and beat are not supported within a single command (will result in an error). Instead do the following:

    celery -A your_app worker --pool=gevent -l info and then separately (if you want to run beat that is) in another terminal/console celery -A your_app beat -l info

    In the first line you can also specify the concurrency like so: --concurrency=100. This is not required but useful. Read up on it what it does as that goes beyond the solution here.

    PART 2: In my specific case I've tested the above locally (development) first to make sure they work. The next issue was getting this into production. I use Redis as the db/broker.

    In my specific setup I have most of my celery configuration in the_main_app/celery/__init__.py file but sometimes people put it directly into the_main_app/celery.py. Whichever it is you do make sure that the REDIS_URL is set correctly. For development it usually looks something like this:

    YOUR_VAR_NAME = os.environ.get('REDIS_URL', 'redis://localhost:6379') where YOUR_VAR_NAME is then set to the broker with everything as below:

    YOUR_VAR_NAME = os.environ.get('REDIS_URL', 'redis://localhost:6379')
    app = Celery('the_main_app')
    app.conf.broker_url = YOUR_VAR_NAME
    

    The remaining settings are all documented on the "celery first steps with django" help page but are not relevant for what I am showing here.

    PART 3:

    When you setup your Redis Database on the App Platform (which is very simple) you will see the connection details as 'public network' and 'VPC network'.

    The celery documentation says to use the following URL format for production: redis://:password@hostname:port/db_number. This didn't work. If you are not using a yaml file then you can simply copy paste the entire connection string (select from the dropdown!) from the Redis DB connection details and then setup an App-Level environment variable in your Digital Ocean project named REDIS_URL and paste in that entire string (and also encrypt it!).

    The string should look like something like this (redis with 2 s!) rediss://USER:[email protected]:PORT.

    You are almost done. The last step is to setup the workers. It was fine for me to run the PART 1 commands as console commands on the App Platform to test them but eventually I've setup a small worker (+ Add Component) for each line pasted them into the Run Command.

    That is basically the process step by step. Good luck!