getsentry / getsentry/sentry-python

Memory leak: DedupeIntegration pins exception tracebacks in asyncio apps

Đang mở
#6,094 7 bình luận 1 reaction 1 người được giao Được @ericapisani nhận Xem trên GitHub
Bug Python Waiting for: Product Owner
Ngôn ngữ chính
Python
Star
2.2k
Fork
669
Merge trung bình
1 ngày 40 phút
Pull request đã merge (30 ngày)
212

Mô tả

### How do you use Sentry?

Self-hosted/on-premise

### Version

2.56.0 (Python 3.12.6)

### Steps to Reproduce

1. `sentry_sdk.init` with only `LoggingIntegration` + `DedupeIntegration` enabled.
2. In a long-lived asyncio task: build a local variable of ~1 MB so the frame carries that
much data, raise `ValueError`, log with `logger.exception(...)`, keep the task running.
3. The exception, its traceback, and the 1 MB local stay alive for the lifetime of the task (and maybe after its death)

### Expected Result

After `logger.exception(...)` returns and the `except` block exits, the exception, its
traceback, and all its frame locals become unreachable and memory are freed

### Actual Result

The exception stays alive for the whole lifetime of the task. Memory grows with every new
captured exception on a new task, and the 1 MB local (plus the whole traceback chain)
stays pinned until the task itself ends.

RAM usage growth:
Image

## What I found

`DedupeIntegration._last_seen` is a `ContextVar` that stores the last captured exception.
The SDK tries to store it as `weakref.ref(exc)`, but Python builtins (`ValueError`,
`TypeError`, `KeyError`, ...) do not support weakref. On `TypeError` the SDK falls back to
a strong reference

Under asyncio every long-lived task keeps its own `ContextVar` state, so the strong
reference lives as long as the task. Through that reference the SDK pins:
- the exception,
- `exc.__traceback__`,
- every frame local in the traceback.

In our web crawler frame locals held response payloads in the 500 KB-1 MB range, which is where
the RSS growth came from

## Fix (what I did)

Replace the dedupe processor with one that stores `weakref.ref(exc)` when possible and
silently skips on `TypeError`. Dedupe is lost for builtins, leak is gone

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.