Skip to content
Flame Graph templates

Flame Graph — Django Checkout Endpoint (py-spy, wall clock)

A 2,800-sample py-spy profile of POST /checkout/ under load: 45.8% of wall time is waiting on PostgreSQL, and the single widest tower is the promotions engine re-querying the database for every basket line (23.4%) — an N+1 the fix for which is one prefetch. The payment gateway call (19.1%) is network wait the service cannot shorten. Illustrative values.

Template previewFlame Graph
POST /checkout/ — py-spy wall-clock profile, 2,800 samples (illustrative)all (2800)gunicorn:handle_requestdjango:BaseHandler.get_responsedjango:BaseHandler._get_responsecheckout.views:CheckoutView.postcheckout.services:price_basketpricing:apply_promotionsdjango.db:QuerySet.__iter__psycopg2:cursor.executepricing:PromotionR…prici…prici…checkout.services:author…requests:Session.sendssl:SSLSocket.recvcheckout.se…rest_fra…checkout.se…django.db:Q…psycopg2:cu…checkout.…django.db…psycop…

Make it your own.

title "POST /checkout/ — py-spy wall-clock profile, 2,800 samples (illustrative)"
# py-spy record --idle --rate 100 --format raw -d 30 --pid <gunicorn worker>
# Frames are module:function, root first. Sample counts sum to 2,800.
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;django:SessionMiddleware.process_request;django.db:SQLCompiler.execute_sql;psycopg2:cursor.execute 96
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;django:AuthenticationMiddleware.process_request;django.db:SQLCompiler.execute_sql;psycopg2:cursor.execute 74
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:price_basket;pricing:apply_promotions;pricing:PromotionRule.matches 412
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:price_basket;pricing:apply_promotions;django.db:QuerySet.__iter__;psycopg2:cursor.execute 655
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:price_basket;pricing:tax_for_lines;pricing:TaxRate.lookup 148
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:reserve_stock;django.db:QuerySet.select_for_update;psycopg2:cursor.execute 286
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:create_order;django.db:Model.save;psycopg2:cursor.execute 171
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:create_order;django.db:Model.save;django.db:signals.post_save;audit:write_event 63
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.services:authorise_payment;requests:Session.send;ssl:SSLSocket.recv 534
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.serializers:OrderSerializer.data;rest_framework:Serializer.to_representation 228
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;checkout.views:CheckoutView.post;checkout.serializers:OrderSerializer.data;json:dumps 61
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;django:CommonMiddleware.process_response;django:HttpResponse.content 32
gunicorn:handle_request;django:BaseHandler.get_response;django:BaseHandler._get_response;django:LoggingMiddleware.process_response;logging:Handler.emit 40