Python tracemalloc in Workers: Find Retained Caches Without Noise

Python tracemalloc in Workers: Find Retained Caches Without Noise
12 views
RSS charts tell you memory grew; they do not tell you which dict retained per-request objects again. tracemalloc can — if you sample carefully in long-running ECS workers. Turn it on continuously at full fidelity and you will DDoS yourself with allocation noise. The senior pattern is: low-overhead baseline, on-demand snapshots during suspected leaks, differential reports that highlight retained frames, and PII-safe artifact upload.
⚡ TL;DR: Start tracemalloc with few frames; take pairwise snapshots around suspect intervals; diff top allocators; rate-limit capture. Scrub dumps before S3. Prefer this for retained caches; use heap dumps sparingly. Analogous discipline: Node heap snapshots in production.

Python tracemalloc in Workers: production guidance

python
# worker/memprobe.py
import tracemalloc, os, time
from pathlib import Path

def start_probe():
 n = int(os.getenv("TRACEMALLOC_FRAMES", "8")) # ✅ small default
 if not tracemalloc.is_tracing():
 tracemalloc.start(n)

def snapshot(label: str):
 start_probe()
 snap = tracemalloc.take_snapshot()
 snap = snap.filter_traces((
 tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
 tracemalloc.Filter(False, "*site-packages/pip/*"),
 ))
 path = Path(f"/tmp/tracemalloc-{label}-{int(time.time())}.out")
 top = snap.statistics("lineno")[:30]
 path.write_text("\n".join(str(s) for s in top), encoding="utf-8")
 return path

Differential reports beat absolute lists

python
def diff_snapshots(before, after, limit=20):
 stats = after.compare_to(before, "lineno")
 # ✅ Focus on increases — ignore steady allocator chatter
 growth = [s for s in stats if s.size_diff > 0][:limit]
 return growth

# Usage in worker heartbeat when RSS delta > threshold
# before = tracemalloc.take_snapshot()
# ... process N jobs ...
# after = tracemalloc.take_snapshot()
# report(diff_snapshots(before, after))
❌ Printing top allocators once at idle — you will “fix” the JSON library forever and miss the cache that only grows on traffic.

Rate limits and privacy

Control Default
Frames 5–10
Snapshot pair interval On RSS slope alarm, not every request
Concurrent captures 1 per task
Artifact destination Scrubbed S3 prefix, CMK, short TTL
Filter Drop importlib / known noisy sites
python
# ✅ Gate on RSS slope, not every message
if rss_mb - last_rss_mb > 64 and time.time() - last_capture > 300:
 run_diff_capture()
 last_capture = time.time()
Look specifically for unbounded module-level dicts, lru_cache without bounds, and memoization keyed by raw request payloads. Fix with maxsize, WeakValue dictionaries, or request-scoped caches that die with the context.

Closing checklist

✅ Dos – ✅ Use few frames and filtered diffs – ✅ Trigger on RSS growth alarms, not every job – ✅ Compare snapshot pairs over a work interval – ✅ Scrub and TTL snapshot artifacts in S3 – ✅ Hunt unbounded caches and memo maps first ❌ Don’ts – ❌ Don’t leave full-fidelity tracemalloc on 24/7 in huge fleets – ❌ Don’t upload raw snapshots with customer payloads – ❌ Don’t trust a single idle top-N list – ❌ Don’t ignore lru_cache without maxsize – ❌ Don’t capture many workers simultaneously during an incident

Related reading

Last updated on September 11, 2026 Related: Python Deadlock Debugging: faulthandler Plus gdb for Wedged Workers; Python’s slots Secret: Cut Memory 60% and Speed Up Attribute Access by 35%; Python’s GIL Explained: Why Your Threads Aren’t Actually Parallel (And the Fix).

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.