Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speeding Up the First Page Load in django

When I update the code on my website I (naturally) restart my apache instance so that the changes will take effect.

Unfortunately the first page served by each apache instance is quite slow while it loads everything into RAM for the first time (5-7 sec for this particular site).

Subsequent requests only take 0.5 - 1.5 seconds so I would like to eliminate this effect for my users.

Is there a better way to get everything loaded into RAM than to do a wget x times (where x is the number of apache instances defined by ServerLimit in my http.conf)

Writing a restart script that restarts apache and runs wget 5 times seems kind of hacky to me.

Thanks!

like image 872
Jiaaro Avatar asked Nov 09 '09 17:11

Jiaaro


2 Answers

The default for Apache/mod_wsgi is to only load application code on first request to a process which requires that applications. So, first step is to configure mod_wsgi to preload your code when the process starts and not only the first request. This can be done in mod_wsgi 2.X using the WSGIImportScript directive.

Presuming daemon mode, which is better option anyway, this means you would have something like:

# Define process group.

WSGIDaemonProcess django display-name=%{GROUP}

# Mount application.

WSGIScriptAlias / /usr/local/django/mysite/apache/django.wsgi

# Ensure application preloaded on process start. Must specify the
# process group and application group (Python interpreter) to use.

WSGIImportScript /usr/local/django/mysite/apache/django.wsgi \
  process-group=django application-group=%{GLOBAL}

<Directory /usr/local/django/mysite/apache>

    # Ensure application runs in same process group and application
    # group as was preloaded into on process start.

    WSGIProcessGroup django
    WSGIApplicationGroup %{GLOBAL}

    Order deny,allow
    Allow from all
</Directory>

When you have made a code change, instead of touch the WSGI script file, which is only checked on the next request, send a SIGINT signal to the processes in the daemon process group instead.

With the 'display-name' option to WSGIDaemonProcess you can identify which processes by using BSD style 'ps' program. With 'display-name' set to '%{GROUP}', the 'ps' output should show '(wsgi:django)' as process name. Identify the process ID and do:

kill -SIGINT pid

Swap 'pid' with actual process ID. If more than one process in daemon process group, send signal to all of them.

Not sure if 'killall' can be used to do this in one step. I had problem with doing it on MacOS X.

In mod_wsgi 3.X the configuration can be simpler and can use instead:

# Define process group.

WSGIDaemonProcess django display-name=%{GROUP}

# Mount application and designate which process group and
# application group (Python interpreter) to run it in. As
# process group and application group named, this will have
# side effect of preloading application on process start.

WSGIScriptAlias / /usr/local/django/mysite/apache/django.wsgi \
  process-group=django application-group=%{GLOBAL}

<Directory /usr/local/django/mysite/apache>
    Order deny,allow
    Allow from all
</Directory>

That is, no need to use separate WSGIImportScript directive as can specific process group and application group as arguments to WSGIScriptAlias instead with side effect that it will preload application.

like image 54
Graham Dumpleton Avatar answered Oct 21 '22 01:10

Graham Dumpleton


How are you running Django (mod_python vs mod_wsgi)?

If you're running mod_wsgi (in daemon mode), restarting Apache isn't necessary to reload your application. All you need to do is update the mtime of your wsgi script (which is done easily with touch).

mod_wsgi's documentation has a pretty thorough explanation of the process:

ReloadingSourceCode

like image 3
Josh Wright Avatar answered Oct 21 '22 02:10

Josh Wright