Measure before optimizing, on TicketFlow. No solutions.md before submitting.
Exercise 1 — The baseline
- Write the local profiling test: 500 reservations of one event with
cProfileand the top 12 by cumulative. Paste the complete output. - The hypothesis: read the output and write ONE hypothesis of the form "X% of the time is in Y because of Z". Is it the function you suspected or another one?
- The minimal improvement your hypothesis suggests (without touching the DB) and the re-measurement with the SAME test: paste the total time's and the top 3's before/after.
Exercise 2 — The N+1 in disguise
- Implement the "my reservations" view with the DELIBERATE N+1: a serializer with a
SerializerMethodFieldquerying the tariff per row. Measure with django-debug-toolbar (or silk): how many queries for 40 reservations? - Fix it with
select_related/prefetch_relatedand re-measure. How many queries and how much time? - The second disguise: the same serializer accessing
reserva.seats.all()inside the loop (the related manager's lazy evaluation). Why doesprefetch_relatedcure it and what happens if you ONLY do.iterator()? (hint: the N+1 is per row, not per batch).
Exercise 3 — The honest micro-profile
- With
timeit: comparestr(uuid)vsf"{uuid}"vsuuid.hexover 100k iterations. Does it matter? Note the real number (expected: it doesn't; the lesson is learning to say no). line_profileroncalcular_total()(08): which line concentrates the time? Is it the Decimal or the tariff query? (if it's the query: it's the N+1 again, not a CPU problem).- The snapshot finding: apply §5's denormalization (precompute
total_amountat confirmation) and verify with 08's test that the price snapshot holds (the tariff's price may change afterwards; the reservation's total does NOT).
Exercise 4 — py-spy on the worker
- Launch the local stack (gunicorn with 4 workers + 36's k6 suite in smoke mode) and
py-spy dumpa worker DURING the plateau: what is it doing? Paste the 3 workers' dumps. - The flamegraph:
py-spy recordfor 60 s during the plateau and paste the SVG (or the stack top if you export it to text). Which function dominates? Does it match 36's DB bottleneck or did it discover something new? - The stuck case: simulate a stuck worker (a Celery task sleeping 120 s) and use
py-spy dumpto diagnose WITHOUT restarting: what would you tell the on-call with only the dump?
Exercise 5 — The profiling report
- Write the
docs/perf/2027-09-checkout.mdreport with: baseline (k6), findings per tool (cProfile/silk/py-spy), applied improvements and re-measurement, and the decided NON-improvements (what did you see slow and is NOT worth fixing?). - The project's rule: every performance improvement in a PR carries ITS measured before/after. Write the PR template (5 lines) and paste it into CONTRIBUTING.md.
Submit
Paste the cProfile output, the N+1's before/after, the py-spy dump and the report. Next: Lesson 38 — Caching strategies.