All jobs

Find Slow Database Queries in a Django App

Yes. An agent runs your Django app on seeded Postgres, logs every query over 50 ms and reports the ORM code behind each with EXPLAIN ANALYZE and a fix.

Run this on RushIn a Project that has your repository.

What the agent does

The agent clones the repository into its own cloud sandbox and brings the app up for real. In a Django app like NetBox, it reads requirements.txt and finds Django==6.1.2, psycopg[c,pool]==3.3.6, django-debug-toolbar==8.0.0 and django-filter==26.2, and pyproject.toml asks for Python 3.12 or newer. netbox/netbox/configuration_example.py shows the DATABASES and REDIS settings it needs, so it starts Postgres and Redis in the sandbox, writes a configuration.py from the example, runs manage.py migrate and creates a superuser to browse with.

Then it seeds data at a size that shows problems. Empty tables make every query fast, so the agent writes a seeding script that creates sites, racks, devices with interfaces, cables between them, prefixes and IP addresses in the tens of thousands, using the models' own create and bulk_create. It sets Postgres log_min_duration_statement = 50 and turns on pg_stat_statements, so every query over 50 ms is logged with its duration, and exercises the main list and detail pages with the test client or a browser while logged in.

For each slow query it finds the code that issues it. NetBox list pages are ObjectListView subclasses with a base queryset: DeviceListView in netbox/dcim/views.py starts from Device.objects.select_related('virtual_chassis'), CableListView from Cable.objects.prefetch_related(...) over terminations__termination and the cached _device, _rack, _location and _site relations, and PrefixListView and IPAddressListView in netbox/ipam/views.py from .objects.all(), each filtered by a filterset and rendered by a table. The agent matches the logged SQL to the view, the filterset or the table column that adds it, and separates one slow query from many fast ones repeated per row.

It runs EXPLAIN (ANALYZE, BUFFERS) on each slow query, reads the plan for sequential scans, misestimated rows and sorts that spill to disk, and tries the change it would recommend on the seeded data: an index created in the sandbox, a select_related or prefetch_related added to the view, or a rewritten filter. It measures the time again after each change and keeps both numbers. The repository itself is left as it was; the result is a report, not a pull request.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Load this Django app in the sandbox with Postgres and Redis and a realistic amount of seed data (tens of thousands of devices, interfaces, cables, prefixes and IP addresses). Set log_min_duration_statement to 50 ms, exercise its main list and detail pages while logged in, and write slow-queries.md: each query over 50 ms, the view, filterset or table that issues it with file and line, its EXPLAIN ANALYZE, and the index or rewrite you tried with the time before and after on the same data. Report N+1 patterns separately from single slow queries. Do not open a pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is slow-queries.md as an artifact on the session, with a download link the session read returns. It opens with how the data was seeded, how many rows of each model, and which pages were exercised, so the numbers can be reproduced. Then each slow query gets a section: the SQL, the page that ran it, the Django code behind it with file and line, the EXPLAIN ANALYZE plan, and the change that was tried with the time before and after.

N+1 patterns are listed apart, with the number of queries a page made and the related field that caused them, because the fix is in the ORM call rather than an index. The session's transcript keeps the seeding script, every command, the Postgres log excerpts and every plan, so a reviewer can rerun any measurement.

Before taking a change, a reviewer checks that the seeded data resembles production's shape, that a recommended index is worth its cost on writes, and that a new prefetch_related does not load more than the page shows. A follow-up message to the same session can then ask the agent to write the chosen changes and a migration and open a pull request.

Run it on every release candidate

Slow queries arrive with new features, so run it on each release branch. A CI job or a small script can send this request when a release branch is cut, and the report lands before the release does.

Set a webhook URL on the API key and Rush POSTs run.completed when the turn finishes. Your handler reads the session, takes slow-queries.md from its artifacts and compares it with the last release's report, so a page that got slower is visible as a diff.

The use case this job follows, with its automation and trigger: Investigate an incident.

Why run it on Rush?

Finding slow queries needs a database with real volume, a running app and a way to see every query. On a laptop that means a local Postgres full of seed data, a Redis, logging changes and a long afternoon. In a Rush sandbox the agent installs all of it on its own machine, seeds as much data as the job needs and throws it away afterwards; nothing touches your production database.

Each finding in the report comes with a plan and a measured before and after on the same data, and the transcript keeps every command, so the numbers can be checked. Different parts of a large app, such as DCIM and IPAM in NetBox, can be profiled in separate runs at once, up to the key's concurrent run limit.

Frequently asked questions

Does the agent connect to my production database?

No. It runs Postgres in its own sandbox and seeds it there. If you want it to analyse your real slow queries, attach a pg_stat_statements export to the request and it starts from those.

How realistic is the seed data?

It uses your models and their relations, at the volume the prompt asks for. It cannot know your data's skew, such as one site with most of the devices, so say so in the prompt if production is uneven.

Will it add indexes to my repository?

Not in this run. It creates indexes in the sandbox database to measure them and reports the result. A follow-up message can ask for a migration and a pull request with the ones you choose.

Does it find N+1 queries too?

Yes. It counts queries per page and lists repeated queries per row separately, with the related field that causes them and the select_related or prefetch_related that removes them.