fix(arknights): 连接失败改为指数退避并只提示一次 - #457
Conversation
`Timer.minute_task` 每秒调用一次 `scheduled_task`,其中「未连接且窗口句柄 存在」这一分支没有任何退避、次数上限或放弃条件,只要窗口在但连不上(未进 主界面、controller 拒绝连接等)就会以秒级频率无限重试。每次失败都会 `logger.error` 并向前端推送一条错误通知,用户界面也随之被刷屏。 线上单台机器 24 小时内因此产生 68,558 条上报,占全组织事件量的 80.5%。 - 新增 `connect_retry_delay`:连续失败时 2/4/8… 秒指数退避,封顶 60 秒, 保证用户随时进入游戏后仍能在一分钟内自动接上 - 窗口句柄变化视为新的连接机会,清空退避 - 前端通知只在一轮失败的首次发出,重试期间不再重复弹窗 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
审查者指南将明日方舟连接失败处理从每秒无限重试改为 2 秒起步、60 秒封顶的指数退避,并在一轮失败中仅提示用户一次;窗口变化或连接成功会重置退避状态,同时新增退避计算边界测试。 明日方舟连接重试退避时序图sequenceDiagram
participant Timer
participant Toolkit
participant Arknights
participant Publisher
loop Every second
Timer->>Toolkit: scheduled_task()
Toolkit->>Toolkit: get_connect_status()
alt Retry time reached
Toolkit->>Arknights: connect_arknights()
alt Connection succeeds
Arknights-->>Toolkit: connected
Toolkit->>Toolkit: reset_connect_backoff()
else Connection fails
Arknights-->>Toolkit: Exception
Toolkit->>Toolkit: connect_retry_delay(failures)
Toolkit->>Toolkit: Set next_connect_attempt
Toolkit->>Publisher: Publisher.send(error notice)
end
else Backoff active
Toolkit-->>Timer: Skip connection attempt
end
end
明日方舟连接退避生命周期状态图stateDiagram-v2
[*] --> Ready
Ready --> Retrying: connect_arknights() fails
Retrying --> Retrying: scheduled_task() before next_connect_attempt
Retrying --> Retrying: connect_retry_delay() 2/4/8.../60 seconds
Retrying --> Connected: connect_arknights() succeeds
Retrying --> Ready: Window handle changes
Connected --> Ready: Window handle changes
Connected --> Connected: get_connect_status()
Retrying --> [*]: Window disappears
明日方舟连接失败通知流程图flowchart TD
A["connect_arknights()"] --> B{"Connection succeeds?"}
B -->|Yes| C["reset_connect_backoff()"]
B -->|No| D[Increment connect_failures]
D --> E["connect_retry_delay() and set next_connect_attempt"]
E --> F{"First failure in this round?"}
F -->|Yes| G["Publisher.send(error notice)"]
F -->|No| H[Wait for next retry]
G --> H
H --> I{"Window changes or connection succeeds?"}
I -->|Yes| C
I -->|No| A
文件级变更
提示和命令与 Sourcery 交互
自定义使用体验访问你的控制面板以:
获取帮助Original review guide in EnglishReviewer's Guide将明日方舟连接失败处理从每秒无限重试改为 2 秒起步、60 秒封顶的指数退避,并在一轮失败中仅提示用户一次;窗口变化或连接成功会重置退避状态,同时新增退避计算边界测试。 Sequence diagram for Arknights connection retry backoffsequenceDiagram
participant Timer
participant Toolkit
participant Arknights
participant Publisher
loop Every second
Timer->>Toolkit: scheduled_task()
Toolkit->>Toolkit: get_connect_status()
alt Retry time reached
Toolkit->>Arknights: connect_arknights()
alt Connection succeeds
Arknights-->>Toolkit: connected
Toolkit->>Toolkit: reset_connect_backoff()
else Connection fails
Arknights-->>Toolkit: Exception
Toolkit->>Toolkit: connect_retry_delay(failures)
Toolkit->>Toolkit: Set next_connect_attempt
Toolkit->>Publisher: Publisher.send(error notice)
end
else Backoff active
Toolkit-->>Timer: Skip connection attempt
end
end
State diagram for Arknights connection backoff lifecyclestateDiagram-v2
[*] --> Ready
Ready --> Retrying: connect_arknights() fails
Retrying --> Retrying: scheduled_task() before next_connect_attempt
Retrying --> Retrying: connect_retry_delay() 2/4/8.../60 seconds
Retrying --> Connected: connect_arknights() succeeds
Retrying --> Ready: Window handle changes
Connected --> Ready: Window handle changes
Connected --> Connected: get_connect_status()
Retrying --> [*]: Window disappears
Flow diagram for Arknights connection failure notificationflowchart TD
A["connect_arknights()"] --> B{"Connection succeeds?"}
B -->|Yes| C["reset_connect_backoff()"]
B -->|No| D[Increment connect_failures]
D --> E["connect_retry_delay() and set next_connect_attempt"]
E --> F{"First failure in this round?"}
F -->|Yes| G["Publisher.send(error notice)"]
F -->|No| H[Wait for next retry]
G --> H
H --> I{"Window changes or connection succeeds?"}
I -->|Yes| C
I -->|No| A
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
你好——我发现了 1 个问题
AI Agent 提示词
请处理本次代码审查中的评论:
## 单独评论
### 评论 1
<location path="app/MaaFW/ArknightWin32.py" line_range="66-69" />
<code_context>
+ float: 等待秒数, 指数增长并封顶到 ``CONNECT_RETRY_MAX_SECONDS``
+ """
+
+ return min(
+ CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0),
+ CONNECT_RETRY_MAX_SECONDS,
+ )
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** 当连续失败次数达到 1025 次时,`2 ** (failures - 1)` 在与浮点数相乘时转换为 `float`,直接抛出 `OverflowError`,因此异常处理本身失败,`second_task` 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 `min` 执行前计算的。
**Triggers:** 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。
**Suggested fix:** 在指数运算前先按封顶阈值截断失败次数,或在 `failures` 达到对应阈值时直接返回 `CONNECT_RETRY_MAX_SECONDS`。
```suggestion
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5),
CONNECT_RETRY_MAX_SECONDS,
)
```
</issue_to_address>Sourcery 评估
等待批准。 请先处理 1 个发现的问题。
阻塞性发现:app/MaaFW/ArknightWin32.py:69
帮助我变得更有用!请在每条评论上点击 👍 或 👎,我会利用这些反馈来改进审查结果。
Original comment in English
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="app/MaaFW/ArknightWin32.py" line_range="66-69" />
<code_context>
+ float: 等待秒数, 指数增长并封顶到 ``CONNECT_RETRY_MAX_SECONDS``
+ """
+
+ return min(
+ CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0),
+ CONNECT_RETRY_MAX_SECONDS,
+ )
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** 当连续失败次数达到 1025 次时,`2 ** (failures - 1)` 在与浮点数相乘时转换为 `float`,直接抛出 `OverflowError`,因此异常处理本身失败,`second_task` 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 `min` 执行前计算的。
**Triggers:** 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。
**Suggested fix:** 在指数运算前先按封顶阈值截断失败次数,或在 `failures` 达到对应阈值时直接返回 `CONNECT_RETRY_MAX_SECONDS`。
```suggestion
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5),
CONNECT_RETRY_MAX_SECONDS,
)
```
</issue_to_address>Sourcery assessment
Approval pending. 1 finding to address first.
Blocking findings: app/MaaFW/ArknightWin32.py:69
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| return min( | ||
| CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0), | ||
| CONNECT_RETRY_MAX_SECONDS, | ||
| ) |
There was a problem hiding this comment.
issue (bug_risk): 当连续失败次数达到 1025 次时,2 ** (failures - 1) 在与浮点数相乘时转换为 float,直接抛出 OverflowError,因此异常处理本身失败,second_task 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 min 执行前计算的。
Triggers: 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。
Suggested fix: 在指数运算前先按封顶阈值截断失败次数,或在 failures 达到对应阈值时直接返回 CONNECT_RETRY_MAX_SECONDS。
| return min( | |
| CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0), | |
| CONNECT_RETRY_MAX_SECONDS, | |
| ) | |
| return min( | |
| CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5), | |
| CONNECT_RETRY_MAX_SECONDS, | |
| ) |
Original comment in English
issue (bug_risk): 当连续失败次数达到 1025 次时,2 ** (failures - 1) 在与浮点数相乘时转换为 float,直接抛出 OverflowError,因此异常处理本身失败,second_task 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 min 执行前计算的。
Triggers: 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。
Suggested fix: 在指数运算前先按封顶阈值截断失败次数,或在 failures 达到对应阈值时直接返回 CONNECT_RETRY_MAX_SECONDS。
| return min( | |
| CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0), | |
| CONNECT_RETRY_MAX_SECONDS, | |
| ) | |
| return min( | |
| CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5), | |
| CONNECT_RETRY_MAX_SECONDS, | |
| ) |
摘要
Timer.minute_task每秒调用一次scheduled_task(app/core/timer.py:139+asyncio.sleep(1)),其中「未连接且窗口句柄存在」这一分支没有任何退避、次数上限或放弃条件,只要窗口在但连不上(未进主界面、controller 拒绝连接等)就会以秒级频率无限重试。logger.error并向前端Publisher.send一条错误通知,用户界面同样每秒被刷屏一次。connect_retry_delay:连续失败时 2/4/8… 秒指数退避、封顶 60 秒;窗口句柄变化视为新的连接机会并清空退避;前端通知只在一轮失败的首次发出。AUTO-MAS-BACKEND-2N,单台机器 24 小时内 68,558 条,占全组织事件量的 80.5%,频率 1.25 秒一条。设计取舍
封顶 60 秒而非彻底放弃:用户可能在任意时刻进入游戏主界面,永久放弃会导致再也连不上,需要手动开关工具才能恢复。60 秒上限把最坏情况从 86,400 次/天压到 1,440 次/天,同时保留自动恢复能力。
检查
tests/task/test_arknights_connect_backoff.py:4 个用例通过(含封顶与异常入参)python -m pytest tests --collect-only -q:收集 168 个,退出码 0ruff format无残留改动Sourcery 摘要
通过添加带上限的指数重试退避机制和一次性用户通知,在保留自动恢复功能的同时,减少 Arknights 连接失败产生的噪音。
错误修复:
增强功能:
测试:
Original summary in English
Summary by Sourcery
Reduce Arknights connection failure noise by adding capped exponential retry backoff and one-time user notifications while retaining automatic recovery.
Bug Fixes:
Enhancements:
Tests: