1

Situation: I would like to deploy our Django project's code changes to the stage or production server. For this we have an ansible deployment script, that pulls the django project's code from gitlab, migrates the database, collects the static files, restarts the different servers, etc.

Problem: Some uwsgi processes (1 out of 10 on average) do not reload my django application when I deploy via the ansible script. I can reproduce this for example after the static assets change after a code update and the static assets bundle's file name hashes changes. The server then throws a 500 error because uwsgi still tries to load the bundle with the old hash (via https://github.com/ezhome/django-webpack-loader). When I refresh the website in the browser after a deployment where the static assets have changed, then the server returns a 500 error in about 10 - 20% of the requests because it cannot find the old static assets bundle (example: bundle.9290aAFKASE234.js).

When I run /etc/init.d/supervisor restart on the server the problem immediately goes away, all uwsgi processes seem to be reloaded correctly.

relevant nginx config

location / {
    {% if nginx_site_basic_auth %}
    auth_basic "Test Server what.digital";
    auth_basic_user_file "{{ project_root}}/nginx_passwdfile";
    {% endif %}

    uwsgi_pass {{ project_name }}_server;
    uwsgi_read_timeout 1800s;
    include /etc/nginx/uwsgi_params;
}

uwsgi config (app.ini)

# uwsgi.ini file
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = {{ project_root }}
# Django's wsgi file
module = {{ project_django_wsgi }}
plugins = python
# the virtualenv (full path)
home = {{ project_venv }}
# process-related settings
# prevents some pretty crazy erratic behaviour
lazy-apps = true
# master
master = true
# maximum number of worker processes
processes = 10
pidfile = {{ wsgi_pid_file }}
#touch-reload = {{ project_root }}/config/wsgi.py
#daemonize = {{ project_root }}/{{ project_name }}-uwsgi-daemon.log
logto = {{ project_root }}/{{ project_name }}-uwsgi-error.log
log-5xx = true
disable-logging = true
harakiri = 120
no-orphans = true
max-requests = 50
# give appropriate permissions to socket file
chmod-socket = 666
# the socket (use the full path to be safe)
socket = {{ project_root }}/{{ project_name }}.sock
# http-socket = :8000
# clear environment on exit
vacuum = true
buffer-size = 32768
# https://stackoverflow.com/questions/32452529/uwsgi-emperor-unicodeencodeerror-ascii-codec-cant-encode-character
env = LANG=en_US.UTF-8

supervisor template (populated by ansible):

[program:uwsgi]
user = {{ user_name }}
command={{ project_venv }}/bin/uwsgi --ini {{ project_root }}/{{ uwsgi_conf_file }}
environment={% for name, value in env_vars.iteritems() -%}
    {{ name }}="{{ value|replace('%', '%%') }}";
{%- endfor %}
autostart=true
autorestart=true
stderr_logfile = {{ project_logs }}/supervisor_uwsgi_err.log
stdout_logfile = {{ project_logs }}/supervisor_uwsgi_stdout.log
stopsignal=INT

relevant ansible config

- name: upload supervisor configuration
  template: src=supervisor.j2 dest=/etc/supervisor/conf.d/{{ project_name }}.conf
  become_user: root

#- name: make sure supervisord runs
#  service: name=supervisor state=started
#  become_user: root

#- name: reread supervisorctl config files
#  command: supervisorctl reread
#  become_user: root

- name: restart supervisor
  # ansible's supervisorctl module doesnt really restart and some weird caching problems emerge
  # shell: supervisorctl restart uwsgi
  shell: /etc/init.d/supervisor restart
  become_user: root

- name: make sure nginx is restarted
  service: name=nginx state=restarted
  become_user: root
Mario
  • 2,619
  • 1
  • 24
  • 22
  • Do you run `supervisorctl restart uwsgi`? It's commented out in your ansible file. – Håken Lid Feb 16 '18 at 12:44
  • The json file doesn't work like python files. As soon as the file is modified, the changes will take effect. It doesn't matter if you restart uwsgi. If you want different behaviour, you might have to change django-webpack-loader somehow. For example, it should be possible to convert the json file into a python script that exposes a dictionary. Then that will be cashed by the uwsgi workers. – Håken Lid Feb 16 '18 at 12:53
  • It might be possible to change django-webpack-loader config as well to get the same result. Have you enabled the cache? https://github.com/ezhome/django-webpack-loader#cache – Håken Lid Feb 16 '18 at 12:56
  • If calling `/etc/init.d/supervisor restart` directly on the server after you've run Ansible is fixing the issue, the problem is likely part of your Ansible config. Is what you've shown 100% of your Ansible config related to Supervisor? – YPCrumble Feb 16 '18 at 16:12
  • @HåkenLid thank you, /etc/init.d/supervisor restart also restarts all child processes AFAIK. In regards to the json file, this is intended behaviour. But as soon as the uwsgi process is restarted by supervisor it should also catch the new paths in the json file. The problem is exactly the other way round, uwsgi still 'reads' the old paths from the json file. It also 'reads' old values from the db.sqlite3 database file! It seems to somehow cache the files that the process has access to. – Mario Feb 21 '18 at 11:41
  • Seems like a cache invalidation issue. Caching could happen at any level, so it can be hard to isolate the bug. Django, uwsgi and nginx can have independent caching. Also check the http cache-control headers. The browser or some proxy server might be doing unintended caching. – Håken Lid Feb 21 '18 at 12:04
  • @HåkenLid - in regards to webpack cache - this is just a symptom, the problem exists with other files, such as the db.sqlite3 file as well. Disabling webpack cache would only make the underlying issue more invisible. – Mario Mar 16 '18 at 09:53

1 Answers1

0

We finally found the reason for this: django is using local caching per default which is not safe to use when having multiple uwsgi processes. The solution is to set up a memcached service and configure django's CACHES for it as described here: https://docs.djangoproject.com/en/4.0/topics/cache/#memcached

Mario
  • 2,619
  • 1
  • 24
  • 22