Skip to content

Latest commit

 

History

History
101 lines (72 loc) · 3.9 KB

File metadata and controls

101 lines (72 loc) · 3.9 KB

Memray Profiling for Native Python Modules on Render

This example shows a realistic way to profile memory use from native Python extensions on Render:

  • A FastAPI service exposes an authenticated endpoint that captures a Memray profile for a single request.
  • The profiled workload uses NumPy and pandas, which exercise native code paths and temporary allocations that are hard to spot with Python-only tracers.
  • Render stores the .bin capture and generated flamegraph on a persistent disk so the artifacts survive restarts and redeploys.
  • Dependencies are managed with uv, using pyproject.toml and a checked-in uv.lock.

Files

  • app.py: FastAPI app with the authenticated profiling endpoints.
  • workload.py: A customer analytics workload that allocates heavily in NumPy and pandas.
  • offline_profile.py: A CLI entrypoint for one-off memray run --native captures.
  • pyproject.toml / uv.lock: Project metadata and locked dependencies for uv.
  • render.yaml: Render Blueprint for deploying the example with a persistent disk.

Why this pattern works well on Render

Using memray.Tracker(..., native_traces=True) around a single request lets you capture one expensive path without running the entire service under a profiler all day. That is a better fit for a production-like Render service than starting the server itself with memray run.

The service also writes captures to /var/data/memray, which is on a persistent disk in render.yaml. That matters because Memray captures can be large and you usually want them available after the request completes.

Local development

Create a virtual environment, install dependencies, and start the service:

uv sync
export MEMRAY_PROFILE_TOKEN=local-dev-token
uv run uvicorn app:app --reload

Trigger a profile:

curl -X POST http://127.0.0.1:8000/profile/native \
  -H 'Content-Type: application/json' \
  -H 'X-Profile-Token: local-dev-token' \
  -d '{"rows": 400000, "customers": 35000, "top_n": 12}'

List available captures:

curl http://127.0.0.1:8000/profiles \
  -H 'X-Profile-Token: local-dev-token'

Open the generated flamegraph from the output path returned by the API, or download it directly:

curl -OJ http://127.0.0.1:8000/profiles/<run-id>/flamegraph \
  -H 'X-Profile-Token: local-dev-token'

One-off CLI capture

If you want the simplest possible Memray command on a Render shell, use the shared workload directly:

uv run memray run --native -o /var/data/memray/manual-run.bin \
  python offline_profile.py --rows 400000 --customers 35000 --top-n 12

Then generate the HTML report on the same machine:

uv run memray flamegraph -o /var/data/memray/manual-run.html /var/data/memray/manual-run.bin

Deploying on Render

  1. Create a new Blueprint service from this repository.
  2. Render provisions the MEMRAY_PROFILE_TOKEN environment variable automatically.
  3. After the service is live, copy the token value from the dashboard.
  4. Trigger the profiling endpoint against your Render URL:
curl -X POST https://your-service.onrender.com/profile/native \
  -H 'Content-Type: application/json' \
  -H 'X-Profile-Token: <token-from-render>' \
  -d '{"rows": 400000, "customers": 35000, "top_n": 12}'
  1. Download the capture or flamegraph:
curl -OJ https://your-service.onrender.com/profiles/<run-id>/capture \
  -H 'X-Profile-Token: <token-from-render>'

curl -OJ https://your-service.onrender.com/profiles/<run-id>/flamegraph \
  -H 'X-Profile-Token: <token-from-render>'

Notes

  • MEMRAY_MAX_ROWS limits how large a single capture request can be.
  • Only one profile runs at a time. The service returns 409 if another capture is already in progress.
  • Native symbol resolution is best when you generate the report on the same Render instance that created the capture.
  • Render automatically makes uv available for Python services when uv.lock is present in the repo root.