How Sentry uncovered an N+1 issue in djangoproject.com
Sentry recently launched Performance Issues, a feature to help developers discover and fix common performance problems in their projects. We tested this project internally and with alpha users, so when we finally turned it on for all Sentry users, we were delighted (and dismayed) to hear from Carlton Gibson, current Django fellow and great human, that Sentry had:
Detected an N+1 problem in the code for http://djangoproject.com/
Annoyed the heck out of Carlton and other Django maintainers with duplicate Sentry Issues for that same N+1 problem (sorry about that!)
djangoproject.com’s N+1 problem
The site’s N+1 problem was in their docs version switcher, and the fix was pretty simple.
Version with N+1 problem:
@register.simple_tag(takes_context=True)
def get_all_doc_versions(context, url=None):
"""
Get a list of all versions of this document to link to.
Usage: {% get_all_doc_versions <url> as "varname" %}
"""
lang = context.get('lang', 'en')
versions = []
# This causes an N+1 query!
for release in DocumentRelease.objects.filter(lang=lang):
version_root = get_doc_root(release.lang, release.version)
if version_root.exists():
doc_path = get_doc_path(version_root, url)
if doc_path:
versions.append(release.version)
# Save the versions into the context
versions = sorted(StrictVersion(x) for x in versions if x != 'dev')
return [str(x) for x in versions] + ['dev']
Fixed version:
@register.simple_tag(takes_context=True)
def get_all_doc_versions(context, url=None):
"""
Get a list of all versions of this document to link to.
Usage: {% get_all_doc_versions <url> as "varname" %}
"""
lang = context.get('lang', 'en')
versions = []
# This is fixed :D
for release in DocumentRelease.objects.select_related('release').filter(lang=lang):
version_root = get_doc_root(release.lang, release.version)
if version_root.exists():
doc_path = get_doc_path(version_root, url)
if doc_path:
versions.append(release.version)
# Save the versions into the context
versions = sorted(StrictVersion(x) for x in versions if x != 'dev')
return [str(x) for x in versions] + ['dev']
Our duplicate issues bug
All developers write buggy code - even devs at Sentry. Carlton was able to resolve djangoproject.com’s N+1 issue, and with his feedback, we were also able to fix the bug in Sentry that was duplicating N+1 performance issues. You can learn more about our fix in the PR.
Performance issues
Sentry’s Performance Issues feature can currently detect N+1 issues in all Sentry-supported backend languages. If you’re already using Sentry for backend performance monitoring, to detect N+1 issues all you have to do is… nothing. Sentry will automatically detect N+1 problems by default.
Beyond N+1 issues, Sentry performance monitoring will identify slow transactions, spans that could be the bottleneck and how a slowdown in one part of your stack is affecting other parts of your application. Check out our docs to learn more about performance monitoring with Sentry.