Azure / Azure/azure-sdk-for-python

[ServiceBus] aio AutoLockRenewer never prunes completed futures from _futures, leaking memory over a long-running consumer

Đang mở
#48,366 2 bình luận 1 reaction 0 người được giao Xem trên GitHub
Client customer-reported needs-team-attention question Service Attention Service Bus
Ngôn ngữ chính
Python
Star
5.6k
Fork
3.4k
Merge trung bình
2 ngày
Pull request đã merge (30 ngày)
217

Mô tả

- **Package Name**: azure-servicebus
- **Package Version**: 7.14.3
- **Operating System**: Windows 11 Pro
- **Python Version**: 3.13.5

**Describe the bug**
`azure.servicebus.aio.AutoLockRenewer` grows its internal `_futures` list by one entry on every `register(...)` and does not release those entries as the corresponding messages are settled - the list is only drained when the renewer is closed. A long-running consumer using the **documented** one-renewer-per-app pattern therefore accumulates one entry per message processed for the whole lifetime of the renewer: unbounded memory growth, reclaimed only on `close()`.

This is the pattern the docs recommend (one long-lived renewer, `register` each message in the receive loop), so idiomatic usage hits it:

```python
lock_renewal = AutoLockRenewer()
async with servicebus_receiver:
async for message in servicebus_receiver:
lock_renewal.register(servicebus_receiver, message, max_lock_renewal_duration=60)
await process_message(message)
await servicebus_receiver.complete_message(message) # renewal completes here...
# ...but its Future stays in lock_renewal._futures forever
```

**To Reproduce**
1. Create a single long-lived `AutoLockRenewer()`.
2. Receive from a queue holding a few hundred messages; for each message, `register(receiver, msg, ...)` then settle it immediately with `complete_message(msg)` (so no renewal stays active).
3. Print `len(renewer._futures)` periodically.
4. Observe it climb to the all-time processed count and never shrink, even though every registered renewal has already completed; process RSS grows in lockstep. Only `await renewer.close()` releases them.

**Expected behavior**
A completed renewal's `Future` should be dropped from `_futures` when it finishes, so the list stays bounded by the number of *active* (unsettled) renewals rather than the process's all-time message count. In the repro above `len(_futures)` should hover near 0 (each message settles immediately), not grow to `processed`.

**Screenshots**
Not applicable

**Additional context**
The growth is proportional to the total number of messages handled over the process's uptime, not to how many renewals are active at once - the accumulated entries are only released when the renewer itself is closed, so a single app-lifetime renewer (the documented pattern) that stays open until shutdown holds them for the whole run. Higher throughput or longer uptime makes it more pronounced; a short-lived script that closes the renewer promptly won't notice.

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

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

Hướng nghiên cứu

Bắt đầu bằng cách xác định phần triển khai async AutoLockRenewer trong package azure-servicebus và theo dõi cách register(...) lưu trữ cũng như hoàn tất các future gia hạn. Tái hiện kịch bản consumer chạy lâu dài từ issue, sau đó xác minh rằng các future đã hoàn tất được xóa, trong khi các lần gia hạn đang hoạt động vẫn được theo dõi và collection vẫn bị giới hạn sau khi các message được settled.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
azure, python
Lĩnh vực
api, backend
Loại issue
Lỗi
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
68/100

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.