Skip to content

Comments

perf: 复用 AsyncClient 替代 ThreadPoolExecutor 避免 SSL 上下文阻塞#162

Merged
weinibuliu merged 2 commits intomainfrom
fix/block-update-check
Feb 21, 2026
Merged

perf: 复用 AsyncClient 替代 ThreadPoolExecutor 避免 SSL 上下文阻塞#162
weinibuliu merged 2 commits intomainfrom
fix/block-update-check

Conversation

@Aliothmoon
Copy link
Member

@Aliothmoon Aliothmoon commented Feb 21, 2026

fix: (#160)

Summary by Sourcery

增强功能:

  • 将基于 ThreadPoolExecutor 的同步 HTTP 调用替换为可复用的 httpx.AsyncClient,以实现对 PyPI 版本的非阻塞检查。
Original summary in English

Summary by Sourcery

Enhancements:

  • Replace ThreadPoolExecutor-based synchronous HTTP calls with a reusable httpx.AsyncClient for non-blocking PyPI version checks.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些总体反馈:

  • 全局的 _client = httpx.AsyncClient() 从未被关闭,这可能导致连接泄漏并产生警告;建议在 check_update() 中使用 async with 代码块进行管理,或添加显式的启动/关闭钩子来创建并关闭共享的客户端。
  • 由于 asyncio.gather(..., return_exceptions=True) 现在可能会直接返回来自 _get_from_pypi 的异常,需要确保后续逻辑明确过滤掉 Exception 实例,以避免将其当作有效的版本结果来处理。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The global `_client = httpx.AsyncClient()` is never closed, which can leak connections and emit warnings; consider managing it with an `async with` block in `check_update()` or adding an explicit startup/shutdown hook to create and close a shared client.
- Since `asyncio.gather(..., return_exceptions=True)` can now surface exceptions directly from `_get_from_pypi`, ensure the subsequent logic explicitly filters out `Exception` instances to avoid treating them as valid version results.

## Individual Comments

### Comment 1
<location> `src/MaaDebugger/utils/update_checker/check.py:13` </location>
<code_context>
 TSINGHUA_PYPI_API = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/json/maadebugger"

-_executor = ThreadPoolExecutor(max_workers=2)
+_client = httpx.AsyncClient()


</code_context>

<issue_to_address>
**issue (bug_risk):** The module-level AsyncClient instance is never closed, which can leak connections/resources.

Because `_client` is created at import time and never closed, its connections/files may remain open for the entire process lifetime. Either instantiate it where used via an async context manager, or add an explicit lifecycle hook (e.g. `async def shutdown()` calling `await _client.aclose()`) and ensure it runs on application shutdown to prevent leaks.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The global _client = httpx.AsyncClient() is never closed, which can leak connections and emit warnings; consider managing it with an async with block in check_update() or adding an explicit startup/shutdown hook to create and close a shared client.
  • Since asyncio.gather(..., return_exceptions=True) can now surface exceptions directly from _get_from_pypi, ensure the subsequent logic explicitly filters out Exception instances to avoid treating them as valid version results.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The global `_client = httpx.AsyncClient()` is never closed, which can leak connections and emit warnings; consider managing it with an `async with` block in `check_update()` or adding an explicit startup/shutdown hook to create and close a shared client.
- Since `asyncio.gather(..., return_exceptions=True)` can now surface exceptions directly from `_get_from_pypi`, ensure the subsequent logic explicitly filters out `Exception` instances to avoid treating them as valid version results.

## Individual Comments

### Comment 1
<location> `src/MaaDebugger/utils/update_checker/check.py:13` </location>
<code_context>
 TSINGHUA_PYPI_API = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/json/maadebugger"

-_executor = ThreadPoolExecutor(max_workers=2)
+_client = httpx.AsyncClient()


</code_context>

<issue_to_address>
**issue (bug_risk):** The module-level AsyncClient instance is never closed, which can leak connections/resources.

Because `_client` is created at import time and never closed, its connections/files may remain open for the entire process lifetime. Either instantiate it where used via an async context manager, or add an explicit lifecycle hook (e.g. `async def shutdown()` calling `await _client.aclose()`) and ensure it runs on application shutdown to prevent leaks.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the update checker to use a reusable httpx.AsyncClient instead of ThreadPoolExecutor, addressing SSL context blocking issues (#160). The change simplifies the async implementation by using native async HTTP calls rather than wrapping synchronous calls in thread pool executors.

Changes:

  • Replaced ThreadPoolExecutor with a module-level httpx.AsyncClient instance
  • Converted _sync_get_from_pypi() to async _get_from_pypi() function
  • Simplified check_update() to directly await async calls using asyncio.gather()

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@weinibuliu weinibuliu merged commit 3c46089 into main Feb 21, 2026
3 checks passed
@weinibuliu weinibuliu deleted the fix/block-update-check branch February 21, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants