In part 2 I’ve tested Saleor with multiple WSGI servers to give you an idea of how application load profiles affect the overall performance of the entire stack. This time I’ll take a look at the profiling tools we have at our disposal.

As was mentioned in part 1 (“fix your code first”), when an application is not meeting its performance requirements, the usual suspect is our own code. But how do I determine which parts of the code?

Database queries

Since we’re working with Django, database queries are a common sight. They are mostly I/O so they should not affect concurrency much (there are exceptions though) but they contribute a lot to individual page render times.

The worst part about database-related problems is that they are often hard to spot or even reproduce locally. Certain problems will only become apparent with large data sets, others will only manifest themselves under very high concurrency. Later on we’ll look at the tools you can use to make your work easier but first let’s take a look at some common issues.

The easy part: queries you don’t need

It’s easy to involuntarily introduce this problem by just modifying your templates and forgetting to call select_related or prefetch_related before accessing a relation. If your team has separate front-end and back-end roles, it’s almost guaranteed to happen at some point in time.

Detecting can be tricky because when testing with small data sets the n+1 tends to be pretty low so it’s easy to miss even if you use tools that show you the total query count for each page view. To be safe, use a performance monitoring tool like Opbeat to track your production servers. It should be able to tell you the average number of times a query is executed as well as its average duration: