From ea3a5026ad76028562ee4aa6b70c79c94fb17d4b Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 03:03:59 +0800 Subject: fix(api): 加固行情采集子进程超时、取消与进程组清理并脱敏错误 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MarketDataService: setsid 独立会话启动采集器,硬超时覆盖子进程退出与管道 EOF 两个阶段;超时、取消与异常路径按进程组杀灭(含持有管道的后台子进程),不遗留孤儿进程 - App.ts/Search/RefreshNav 传入 ctx.RequestAborted,客户端断开即取消采集 - 对外错误仅返回统一短消息(503 MARKET_DATA_UNAVAILABLE 等),不泄露路径、stderr 或密钥;stderr 仅记录服务端日志 - 新增 9 项子进程回归(成功/stderr 洪泛/超时杀灭/取消杀灭/孤儿管道恢复/畸形载荷/非零退出/启动失败/缺脚本)与 HTTP 脱敏回归;全文套件 66 通过(Domain 19 / Web 17 / API 30) - 实机验证:挂起采集器 503 2.4s 无进程残留;真实 akshare 1.18.96 搜索 000001 返回 200,净值刷新持久化 6010 条观测 - README 与 qa/README 补充环境变量、回归命令、G1 过滤断言口径与真实建档标签辨析 --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'README.md') diff --git a/README.md b/README.md index 1fcd88b..73350e4 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,13 @@ API 持久化测试默认启动临时 Docker PostgreSQL 容器;运行 `dotnet dotnet test tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj ``` +只运行行情采集器子进程回归(成功/stderr 洪泛/超时/取消/孤儿管道/非零退出/启动失败/缺脚本/HTTP 脱敏): + +```sh +dotnet test tests/FundLab.Api.Tests/FundLab.Api.Tests.fsproj --filter "FullyQualifiedName~ProcessCollectorTests" +dotnet test tests/FundLab.Api.Tests/FundLab.Api.Tests.fsproj --filter "FullyQualifiedName~market data API keeps collector failures" +``` + 构建前端: ```sh @@ -30,8 +37,12 @@ PATH=/home/somhairle/projects/fund-lab/.tools/node-v22.23.2/bin:$PATH npm run bu API 使用环境变量 `FUND_LAB_AUTH_TOKEN` 配置 Bearer token,使用 `FUND_LAB_DATABASE_URL` 配置 PostgreSQL 连接字符串;仓库只提供 `.env.example`,不保存真实凭证。`/health` 可匿名访问,业务 API 需要 `Authorization: Bearer `。 +行情采集器子进程(AKShare)行为:`FUND_LAB_AKSHARE_PYTHON` 指定解释器(默认 `python3`),`FUND_LAB_AKSHARE_PYTHONPATH` 注入 `PYTHONPATH`(例如指向预装 akshare 的 site 目录),`FUND_LAB_COLLECTOR_TIMEOUT_SECONDS` 设定硬超时(默认 30 秒,有效范围 1–600)。超时覆盖子进程退出与管道 EOF 两个阶段;超时、取消和异常路径都会杀掉整个进程组(通过 `setsid` 建立独立会话),不会遗留持有管道的后台子进程;对外的 HTTP 错误只返回统一短消息(如 503 `MARKET_DATA_UNAVAILABLE` + "collector did not finish within N seconds"),不泄露路径、stderr 或密钥,stderr 仅记录在服务端日志。 + 基金创建接口为 `POST /api/funds`,需要 `Idempotency-Key` 和 decimal 字符串字段;基金读取接口为 `GET /api/funds/{id}`。首次启动会自动创建所需表。 -运行时数据库、行情缓存、凭证和构建产物不应进入 Git。当前 `origin` 为 `/home/somhairle/git/fund-lab.git`;基线提交已独立发布并核对,最新 3c-1 Web 建档切片修改在完成最终验证前保持在本地工作树。 +运行时数据库、行情缓存、凭证和构建产物不应进入 Git。当前 `origin` 为 `/home/somhairle/git/fund-lab.git`;基线提交已独立发布并核对,最新 3c-2 行情采集器子进程加固切片(超时/取消/进程组清理/错误脱敏)在完成最终验证后单独提交。 + +真实 AKShare 冒烟(可选):将 `FUND_LAB_AKSHARE_PYTHON` 指向 `/usr/bin/python3` 并用 `FUND_LAB_AKSHARE_PYTHONPATH` 指向装有 akshare 的 site 目录后启动 API,`GET /api/instruments/search?q=000001` 应返回 200 与真实基金名单,`POST /api/instruments/000001/nav/refresh` 应返回 200 并持久化净值观测。 3a 收口测试还会启动独立临时 PostgreSQL 和真实 Kestrel 进程,覆盖 HTTP 创建、幂等重放/冲突、认证、畸形 JSON、金额边界、停止重启后的读取,以及首次 SQL 写入后故障的完整事务回滚。 -- cgit v1.2.3