Azure / Azure/azure-sdk-for-python

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

未关闭
#48,366 2 条评论 1 个 reaction 已指派 0 人 在 GitHub 查看
Client customer-reported needs-team-attention question Service Attention Service Bus
主要语言
Python
星标
5.6k
派生
3.4k
平均合并
2 天 2 小时
30 天内合并 PR
213

描述

- **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.

贡献指南

打开贡献指南

调研方向

首先定位 azure-servicebus 包中的 async AutoLockRenewer 实现,并跟踪 register(...) 如何存储和完成续期 futures。重现 issue 中的长时间运行 consumer 场景,然后验证已完成的 futures 会被移除,而活动中的续期仍会被跟踪,并且在消息 settled 后 collection 仍保持有界。

由索引模型根据 Issue 内容生成。

评估

技术栈
azure, python
领域
api, backend
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
冷清
描述清晰度
基本清楚
新手友好度
68/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。