fix(export): 为函数图像与导出产物增加资源上限 #30

Closed
yxx wants to merge 0 commits from feat/export-service into main
Collaborator

修复内容(PR #29 审阅 P1)

针对「函数数量没有限制,可能生成数百 MB 的 SVG」,为导出链路增加资源上限:

代码

  • app/plot/parser.py:单块 function-plot 表达式数量上限 _MAX_EXPRESSIONS = 16,超限整块回退并记 FUNCTION_PLOT_TOO_MANY_EXPRESSIONS 诊断。
  • app/export/exporters/html.py:单篇文档函数图像数量上限 _MAX_FUNCTION_PLOTS = 16,超出部分回退为源码占位并记 warning。
  • app/export/service.py:输入源 MAX_MARKDOWN_CHARS(200k)与产物 MAX_EXPORT_BYTES(20MB)上限,超限分别拒绝创建(EXPORT_OPTIONS_INVALID)或标记 failed(EXPORT_OUTPUT_TOO_LARGE)。

测试

  • 新增 4 条回归测试(表达式数量上限、文档级图像数量上限、note 源大小上限、产物大小上限),全量 625 通过。

文档

  • 同步更新 Export开发说明.md 资源上限章节与错误码表、契约 §10.5。

🤖 Generated with Claude Code

## 修复内容(PR #29 审阅 P1) 针对「函数数量没有限制,可能生成数百 MB 的 SVG」,为导出链路增加资源上限: ### 代码 - `app/plot/parser.py`:单块 function-plot 表达式数量上限 `_MAX_EXPRESSIONS = 16`,超限整块回退并记 `FUNCTION_PLOT_TOO_MANY_EXPRESSIONS` 诊断。 - `app/export/exporters/html.py`:单篇文档函数图像数量上限 `_MAX_FUNCTION_PLOTS = 16`,超出部分回退为源码占位并记 warning。 - `app/export/service.py`:输入源 `MAX_MARKDOWN_CHARS`(200k)与产物 `MAX_EXPORT_BYTES`(20MB)上限,超限分别拒绝创建(`EXPORT_OPTIONS_INVALID`)或标记 failed(`EXPORT_OUTPUT_TOO_LARGE`)。 ### 测试 - 新增 4 条回归测试(表达式数量上限、文档级图像数量上限、note 源大小上限、产物大小上限),全量 625 通过。 ### 文档 - 同步更新 `Export开发说明.md` 资源上限章节与错误码表、契约 §10.5。 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Owner

审阅结论:请求修改,暂不建议合并

审阅提交:124024a

上一轮提出的函数数量、输入大小和产物大小问题已经分别增加限制。本轮复审发现 1 个 P1、1 个 P3。

[P1] 组合复杂度仍可长时间占满导出线程

位置:backend/app/plot/parser.py:365–374

当前分别限制了:

  • 单条表达式的 AST 深度和节点数;
  • 单个图块最多 16 条表达式;
  • 单篇文档最多 16 个函数图像;
  • 输入最多 200,000 个字符;
  • 最终产物最多 20 MB。

但是没有限制整篇文档的累计 AST 节点数或采样工作量,因此这些上限组合后仍然允许高开销输入。

实测一个包含 16 条表达式、每条由 150 项加法组成的图块:

  • 源码约 4.8 KB;
  • 渲染约 0.86 秒。

包含 16 个此类图块的文档源码约 77 KB,仍低于输入上限,预计渲染约 13.7 秒,最终 SVG 也不会触发 20 MB 产物限制。

渲染运行在 asyncio.to_thread 中,期间无法响应取消信号。最多 100 个活动任务可同时进入队列,可能持续占满工作线程和 CPU。

建议:

  • 增加文档级累计表达式数量或 AST 节点预算;
  • 或直接降低文档级函数图像与表达式组合上限;
  • 为实际执行中的导出任务增加并发限制;
  • 在采样过程中支持取消或超检查;
  • 补充组合上限和并发资源保护测试。

[P3] `EXPORT_OUTPUT_TOO_LARGE`` 不会对应 HTTP 413

位置:docs/development/Export开发说明.md:68–79

产物超限时:

  1. POST /api/exports 已经返回 202;
  2. 后台任务将状态改为 failed;
  3. EXPORT_OUTPUT_TOO_LARGE`` 写入 ExportJob.error_code`;
  4. 前端通过后续 GET 请求取得任务状态,该请求仍返回 200。

因此该错误不会通过 HTTP 413 返回。文档将它标记为 413,可能导致前端错误地按 HTTP 状态码处理。

建议将同步 HTTP 错误和异步任务错误分开记录,并注明该错误通过:

{
  "status": "failed",
  "error_code": "EXPORT_OUTPUT_TOO_LARGE"
}

返回,而不是 HTTP 413。

验证结果

  • 后端全量测试:625 passed
  • git diff --check:通过
  • 与 main:无冲突
  • 远程状态:可合并

请修复组合资源预算问题并更新文档后再提交复审。

## 审阅结论:请求修改,暂不建议合并 审阅提交:`124024a` 上一轮提出的函数数量、输入大小和产物大小问题已经分别增加限制。本轮复审发现 **1 个 P1、1 个 P3**。 ### [P1] 组合复杂度仍可长时间占满导出线程 位置:`backend/app/plot/parser.py:365–374` 当前分别限制了: - 单条表达式的 AST 深度和节点数; - 单个图块最多 16 条表达式; - 单篇文档最多 16 个函数图像; - 输入最多 200,000 个字符; - 最终产物最多 20 MB。 但是没有限制整篇文档的累计 AST 节点数或采样工作量,因此这些上限组合后仍然允许高开销输入。 实测一个包含 16 条表达式、每条由 150 项加法组成的图块: - 源码约 4.8 KB; - 渲染约 0.86 秒。 包含 16 个此类图块的文档源码约 77 KB,仍低于输入上限,预计渲染约 13.7 秒,最终 SVG 也不会触发 20 MB 产物限制。 渲染运行在 `asyncio.to_thread` 中,期间无法响应取消信号。最多 100 个活动任务可同时进入队列,可能持续占满工作线程和 CPU。 建议: - 增加文档级累计表达式数量或 AST 节点预算; - 或直接降低文档级函数图像与表达式组合上限; - 为实际执行中的导出任务增加并发限制; - 在采样过程中支持取消或超检查; - 补充组合上限和并发资源保护测试。 ### [P3] `EXPORT_OUTPUT_TOO_LARGE`` 不会对应 HTTP 413 位置:`docs/development/Export开发说明.md:68–79` 产物超限时: 1. `POST /api/exports` 已经返回 `202`; 2. 后台任务将状态改为 `failed`; 3. `EXPORT_OUTPUT_TOO_LARGE`` 写入 `ExportJob.error_code`; 4. 前端通过后续 `GET` 请求取得任务状态,该请求仍返回 `200`。 因此该错误不会通过 HTTP `413` 返回。文档将它标记为 `413`,可能导致前端错误地按 HTTP 状态码处理。 建议将同步 HTTP 错误和异步任务错误分开记录,并注明该错误通过: ```json { "status": "failed", "error_code": "EXPORT_OUTPUT_TOO_LARGE" } ``` 返回,而不是 HTTP 413。 ### 验证结果 - 后端全量测试:**625 passed** - `git diff --check`:通过 - 与 `main`:无冲突 - 远程状态:可合并 请修复组合资源预算问题并更新文档后再提交复审。
Kronecker closed this pull request 2026-09-06 00:26:51 +08:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Kronecker/NotesAgentic#30