mcpField
L
ln-654-resource-lifecycle-auditor

ln-654-resource-lifecycle-auditor

github.com/levnikolaevich测试与质量584247 次安装2 天前更新
代码仓库

关于 ln-654-resource-lifecycle-auditor

ln-654-resource-lifecycle-auditor 是用于检查代码库中资源获取、释放、作用域和连接池实践的技能。它还会检查流式端点及错误路径,并输出带严重性标记的问题和合规评分。

主要功能

  1. 01检查资源作用域和清理
  2. 02审查流式端点的资源使用
  3. 03发现错误路径中的清理缺口
  4. 04检查连接池配置
  5. 05报告问题及合规评分

适用场景

  1. 01发现长时间流期间仍持有的会话
  2. 02检查异常或提前返回时的资源清理
  3. 03审查数据库连接池设置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/levnikolaevich/claude-code-skills --skill ln-654-resource-lifecycle-auditor

安装后在 Claude Code 中输入/ln-654-resource-lifecycle-auditor确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
audit_output_schema.md
audit_scoring.md
audit_summary_contract.md
audit_worker_core_contract.md
mcp_integration_patterns.md
mcp_tool_preferences.md
two_layer_detection.md
references/templates/
audit_worker_report_template.md

详细介绍

路径: 文件路径(references/、../ln-*)相对于此技能目录。

资源生命周期审计器(L3 Worker)

类型: L3 Worker

专门审计资源获取/释放模式、作用域不匹配和连接池规范。

目的与范围

  • 审计资源生命周期(优先级:高)
  • 检查会话/连接作用域不匹配、流式端点持有资源、清理模式和连接池配置
  • 将结构化发现写入文件,包含严重性、位置、工作量和建议
  • 计算资源生命周期类别的合规分数(X/10)

输入

必须读取: 加载 references/audit_worker_core_contract.md 和 references/mcp_tool_preferences.md。 工具策略:你可能会作为独立子 Agent 运行,此时宿主的 AGENTS.md 不在作用域内,因此读取、搜索和编辑文件时默认优先使用 hex-line MCP。仅当 MCP 行为不明确时,才加载 references/mcp_integration_patterns.md。

接收包含以下内容的 contextStore:tech_stack、best_practices、db_config(数据库类型、ORM 设置、连接池配置、会话工厂)、codebase_root、output_dir。

支持领域模式: 支持 domain_mode + current_domain。

当引用链或调用路径能实质性地改进生命周期发现时,优先使用 hex-graph。当 hex-line 可用时,读取本地代码/配置优先使用 hex-line。如果 MCP 不可用、不受支持或未建立索引,则使用内置的 Read/Grep/Glob/Bash 继续操作,并在报告中说明所采用的回退方式。

工作流

检测策略:采用两层检测(候选扫描,然后上下文验证);仅当验证方法不明确时,才加载 references/two_layer_detection.md。

  1. 从 contextStore 解析上下文

    • 提取 tech_stack、best_practices、db_config、output_dir
    • 确定 scan_path
  2. 检测 DI 框架

    • FastAPI Depends()、Django 中间件、Spring @Autowired/@PersistenceContext、Express 中间件、Go wire/fx
  3. 发现资源基础设施

    • 查找会话/连接工厂模式(sessionmaker、create_engine、DataSource、连接池创建)
    • 查找 DI 注册(Depends()、@Inject、providers、中间件挂载)
    • 查找流式端点(SSE、WebSocket、长轮询、流式响应)
    • 映射:哪些端点通过 DI 接收哪些资源
  4. 扫描代码库中的违规情况(6 项检查)

    • 跟踪端点生命周期内的资源注入 -> 使用 -> 释放
    • 分析流式端点是否持有资源
    • 检查错误路径中的清理情况
  5. 收集发现,包含严重性、位置、工作量和建议

  6. 使用惩罚算法计算分数

  7. 编写报告: 根据 references/templates/audit_worker_report_template.md 在内存中构建完整的 Markdown 报告,通过单次 Write 调用将其写入 {output_dir}/ln-654--global.md

  8. 返回摘要: 向协调器返回最简摘要(参见输出格式)

审计规则(优先级:高)

1. 资源作用域不匹配

**内容:**通过 DI 注入的资源在整个请求/连接作用域内都处于活动状态,但实际只在其中一小段时间内使用。

检测(Python/FastAPI):

  • 步骤 1 - 查找依赖数据库会话的端点:
    • 搜索:async def\s+\w+\(.*Depends\(get_db\)|Depends\(get_session\)|db:\s*AsyncSession|session:\s*AsyncSession
  • 步骤 2 - 衡量端点函数体内会话的使用范围:
    • 统计首次和最后一次使用 session\.|db\.|await.*repo 之间的行数
    • 统计端点函数体的总行数
  • 步骤 3 - 若 usage_lines / total_lines < 0.2,则标记(会话使用范围小于函数体的 20%)
    • 尤其要注意:会话仅在函数开头使用(用于身份验证检查、初始加载),但函数后续仍执行非数据库操作

检测(Node.js/Express):

  • 中间件在请求开始时注入 req.db 或 req.knex
  • 搜索:app\.use.*pool|app\.use.*knex|app\.use.*prisma(中间件注入)
  • 路由处理程序仅在函数体前 20% 的范围内使用 req.db

检测(Java/Spring):

  • 在包含大量非数据库处理的方法上使用 @Transactional
  • 注入了 EntityManager,但仅短暂使用
  • 搜索:@Autowired.*EntityManager|@PersistenceContext,并分析方法体

检测(Go):

  • 将 sql.DB 或 *gorm.DB 传递给处理程序,仅使用一次,之后进行长时间处理
  • 搜索:func.*Handler.*\*sql\.DB|func.*Handler.*\*gorm\.DB

严重性:

  • **严重:**流式端点(SSE、WebSocket)的会话作用域不匹配——会话被占用数分钟或数小时
  • **高:**包含外部 API 调用的端点存在会话作用域不匹配(网络延迟期间会话一直被占用)
  • **中:**包含超过 50 行非数据库处理的端点存在会话作用域不匹配

**建议:**将数据库操作提取到作用域明确的函数中;仅在需要的时段获取会话;使用 async with get_session() as session: 代码块,而不是在端点级别通过 DI 注入。

**工作量:**M(将 DI 重构为作用域内获取)

2. 流式端点占用资源

**内容:**SSE、WebSocket 或长轮询端点在整个流传输期间一直占用数据库会话/连接。

检测(Python/FastAPI):

  • 步骤 1 - 查找流式端点:
    • 搜索:StreamingResponse|EventSourceResponse|SSE|async def.*websocket|@app\.websocket
    • 搜索:yield\s+.*event|yield\s+.*data:|async for.*yield(SSE 生成器模式)
  • 步骤 2 - 检查流式函数/生成器的作用域内是否有数据库会话:
    • 端点签名中的 Depends() 会话 -> 整个流传输期间都会被占用
    • 生成器内部通过上下文管理器获取的会话 -> 作用域明确(正常)
  • 步骤 3 - 分析生成器内的会话使用情况:
    • 如果会话只在开头使用一次(用于身份验证/权限检查),之后流循环不再访问数据库,则说明作用域不匹配

检测(Node.js):

  • 搜索:res\.write\(|res\.flush\(|Server-Sent Events|new WebSocket|ws\.on\(
  • 检查是否在流循环开始前获取了连接/连接池客户端,且未释放

检测(Java/Spring):

  • 搜索:SseEmitter|WebSocketHandler|StreamingResponseBody
  • 检查 @Transactional 是否包裹了流式方法

检测(Go):

  • 搜索:Flusher|http\.Flusher|websocket\.Conn
  • 检查是否在刷新循环期间一直占用 *sql.DB 或事务

严重性:

  • **严重:**整个 SSE/WebSocket 流传输期间都占用数据库会话/连接(高负载下会耗尽连接池)
  • **高:**长轮询期间一直占用数据库连接(超时超过 30 秒)

**建议:**在流传输开始前执行身份验证/权限检查:获取会话、检查身份验证、释放会话,然后再开始流传输。对于流传输期间的数据库访问,使用单独的作用域会话。

**工作量:**M(重构端点,在流传输前释放会话)

3. 缺少资源清理模式

是什么: 获取资源后未确保清理(没有 try/finally、上下文管理器或 close())。

检测(Python):

  • Grep:session\s*=\s*Session\(\)|session\s*=\s*sessionmaker|engine\.connect\(\) 未置于 with 或 async with 中
  • Grep:connection\s*=\s*pool\.acquire\(\)|conn\s*=\s*await.*connect\(\) 后面没有 try:.*finally:.*close\(\)
  • 模式:session = get_session() 未使用上下文管理器
  • 可排除的安全模式:async with session_factory() as session:、with engine.connect() as conn:

检测(Node.js):

  • Grep:pool\.connect\(\)|knex\.client\.acquireConnection|\.getConnection\(\),且同一函数中没有对应的 .release() 或 .end()
  • Grep:createConnection\(\),且未在 try/finally 中调用 .destroy()

检测(Java):

  • Grep:getConnection\(\)|dataSource\.getConnection\(\),未使用 try-with-resources
  • 模式:Connection conn = ds.getConnection(),未使用 try (Connection conn = ...) 语法

检测(Go):

  • Grep:sql\.Open\(|db\.Begin\(\),且没有 defer.*Close\(\)|defer.*Rollback\(\)
  • 模式:tx, err := db.Begin(),但没有 defer tx.Rollback()

严重程度:

  • 高: 获取会话/连接后未确保清理(异常时会泄漏)
  • 中: 非关键路径中未清理文件句柄或游标

例外: 在开始流式传输/长轮询前获取并释放会话 -> 跳过。将 NullPool / pool_size 配置为无服务器设计并有文档说明 -> 跳过。

建议: 确保资源在所有退出路径中都能得到清理(使用上下文管理器、try-finally 或框架管理的生命周期)。

工作量: S(包装在上下文管理器中或添加 defer)

4. 连接池配置缺口

内容: 缺少连接池健康监控、连接前预检和回收机制,也没有溢出限制。

检测(Python/SQLAlchemy):

  • 搜索 create_engine\(|create_async_engine\:
    • 缺少 pool_pre_ping=True → 无法检测失效连接
    • 缺少 pool_recycle → 连接存活时间超过数据库服务器超时时间(默认值:MySQL 为 8 小时,PG 不限)
    • 缺少 pool_size → 使用默认值 5(对生产环境来说可能过小)
    • 缺少 max_overflow → 高负载下溢出连接数不受限制
    • Web 服务中使用 pool_size=0 或 NullPool → 不使用连接池(反模式)
  • 搜索连接池事件监听器:
    • 缺少 @event.listens_for(engine, "invalidate") → 无法查看连接失效情况
    • 缺少 @event.listens_for(engine, "checkout") → 无法监控连接取出情况
    • 缺少 @event.listens_for(engine, "checkin") → 无法监控连接归还情况

检测(Node.js):

  • 搜索 createPool\(|new Pool\(:
    • 缺少 min/max 配置
    • 缺少 idleTimeoutMillis 或 reapIntervalMillis
    • 缺少连接验证(validateConnection、testOnBorrow)

检测(Java/Spring):

  • 搜索:DataSource|HikariConfig|HikariDataSource:
    • 缺少 leakDetectionThreshold
    • 缺少 maximumPoolSize(默认值为 10)
    • 缺少 connectionTestQuery 或 connectionInitSql

检测(Go):

  • 搜索:sql\.Open\(:
    • 缺少 db.SetMaxOpenConns()
    • 缺少 db.SetMaxIdleConns()
    • 缺少 db.SetConnMaxLifetime()

严重程度:

  • 高: 既没有 pool_pre_ping,也没有 pool_recycle(失效连接会被悄然使用)
  • 高: Web 服务没有 max_overflow 限制(高负载下连接数会无限增长)
  • 中: 缺少连接池事件监听器(无法查看连接池健康状况)
  • 中: 缺少泄漏检测阈值(Java/HikariCP)
  • 低: 连接池大小为默认值(对小型服务可能足够)

视情况而定的例外:

  • NullPool 可用于无服务器/Lambda
  • 对于低流量服务,pool_size=5 可能足够

建议: 配置 pool_pre_ping=True、将 pool_recycle 设为小于数据库服务器超时时间的值,并根据预期并发量设置合适的 pool_size;添加连接池事件监听器以进行监控。

工作量: S(添加配置参数),M(添加事件监听器/监控)

5. 错误路径中未关闭的资源

内容: 异常/错误处理路径跳过资源清理。

检测(Python):

  • 查找包含 raise 或 return、但之前没有 session.close()、conn.close() 或 cursor.close() 的 except 代码块
  • 模式:except Exception: logger.error(...); raise(重新抛出异常但不清理)
  • 查找使用数据库会话、但未处理 GeneratorExit 的生成器函数:
    • 搜索:async def.*yield.*session|def.*yield.*session,且没有 try:.*finally:.*close\(\)

检测(Node.js):

  • 搜索会执行 throw 或 return、但没有释放连接的 catch\s*\( 代码块
  • 模式:pool.connect().then(client => { ... }),但没有 .finally(() => client.release())
  • 用于清理的 Promise 链中缺少 .finally()

检测(Java):

  • 搜索 catch\s*\( 代码块:如果在 try 中打开了连接,却没有 finally { conn.close() }
  • 对 AutoCloseable 资源未使用 try-with-resources

检测(Go):

  • 搜索:在资源清理的 defer 语句之前出现 if err != nil \{.*return
  • 模式:在 Open() 和 defer Close() 之间检查错误,并在未关闭资源的情况下返回

严重程度:

  • 严重: 高频端点的错误路径中存在会话/连接泄漏(导致连接池耗尽)
  • 高: API 处理程序的错误路径中存在资源泄漏
  • 中: 后台任务的错误路径中存在资源泄漏

建议: 在任何可能失败的代码执行之前,使用上下文管理器/try-with-resources/defer;对于生成器,在 yield 外层添加 try/finally。

工作量: S(调整结构,使资源获取发生在可能出错的路径之前)

6. 资源工厂与注入反模式

内容: 使用框架 DI 将短生命周期资源注入长生命周期上下文,而不是使用工厂模式。

检测(Python/FastAPI):

  • 步骤 1 - 查找通过 DI 注入的端点签名中的会话:
    • Grep:Depends\(get_db\)|Depends\(get_session\)|Depends\(get_async_session\)
  • 步骤 2 - 判定端点生命周期:
    • 短生命周期:常规 REST 端点(请求/响应)→ 可以使用 DI 注入
    • 长生命周期:SSE(StreamingResponse、EventSourceResponse)、WebSocket(@app.websocket)、后台任务(BackgroundTasks.add_task)
  • 步骤 3 - 标记长生命周期端点中的 DI 注入:
    • 长生命周期端点应在需要时使用工厂模式:async with session_factory() as session:
    • 不应在端点级别使用 session: AsyncSession = Depends(get_session)

检测(Node.js/Express):

  • 中间件注入的池连接(req.db)被用于 WebSocket 处理程序或 SSE 路由
  • 应在需要时使用:const conn = await pool.connect(); try { ... } finally { conn.release() }

检测(Java/Spring):

  • SSE 端点(SseEmitter)所在的 @Controller 中存在 @Autowired EntityManager
  • 应使用:通过 EntityManagerFactory 以编程方式创建 EntityManager

检测(Go):

  • 在构造处理程序时注入了 *sql.DB,但应按操作获取 *sql.Conn

严重级别:

  • CRITICAL: SSE/WebSocket 端点中通过 DI 注入的会话(会话的存续时间比预期作用域长出多个数量级)
  • HIGH: 通过 DI 注入的会话被传递给后台任务(任务的存续时间超过请求)

建议: 对于长生命周期上下文,使用工厂模式;注入工厂(sessionmaker、池),而不是会话/连接本身。

工作量: M(将 DI 注入从会话改为会话工厂,并添加作用域内的获取逻辑)

评分算法

必须阅读: 加载 references/audit_scoring.md。

输出格式

必须阅读: 加载 references/templates/audit_worker_report_template.md。

按照 references/audit_summary_contract.md 编写 JSON 摘要。在托管模式下,调用方会同时传入 runId 和 summaryArtifactPath;在独立模式下,工作器会按照共享契约生成自己的运行作用域产物路径。

将报告写入 {output_dir}/ln-654--global.md,其中 category: "Resource Lifecycle",检查项为:resource_scope_mismatch、streaming_resource_holding、missing_cleanup、pool_configuration、error_path_leak、factory_vs_injection。

按照 references/audit_summary_contract.md 返回摘要。

当 summaryArtifactPath 缺失时,将独立运行时摘要写入 .hex-skills/runtime-artifacts/runs/{run_id}/evaluation-worker/{worker}--{identifier}.json,并可选择在结构化输出中回显相同摘要。

Report written: .hex-skills/runtime-artifacts/runs/{run_id}/audit-report/ln-654--global.md
Score: X.X/10 | Issues: N (C:N H:N M:N L:N)

关键规则

遵循已加载的 references/audit_worker_core_contract.md。

  • 不要自动修复: 仅报告问题
  • 考虑 DI: 了解框架依赖注入的生命周期作用域(请求、单例、瞬态)
  • 先识别框架: 检查注入模式前,先识别 DI 框架
  • 先检测流式处理: 作用域分析前,先查找所有流式/长生命周期端点
  • 排除测试: 不要标记测试夹具、测试会话设置或模拟会话
  • 排除 CLI/脚本: 对单次运行的脚本,DI 作用域不匹配无关紧要
  • 合理评估工作量: S = <1h,M = 1-4h,L = >4h
  • 池配置取决于上下文: NullPool 对无服务器/Lambda 场景有效;对于低流量服务,pool_size=5 可能也没问题
  • 注意安全模式: 不要标记 async with、with、try-with-resources、defer 内部的资源(已进行管理)

完成定义

应用已加载的 references/audit_worker_core_contract.md。

  • contextStore 已成功解析(包括 output_dir、db_config)
  • 已确定 scan_path
  • 已检测到 DI 框架(FastAPI Depends、Django middleware、Spring @Autowired、Express middleware、Go wire)
  • 已盘点流式端点
  • 已完成全部 6 项检查:
    • 资源作用域不匹配、流式资源占用、缺少清理、连接池配置、错误路径泄漏、工厂与注入
  • 已收集发现项,并注明严重程度、位置、工作量和建议
  • 已使用惩罚算法计算分数
  • 已将报告写入 {output_dir}/ln-654--global.md(使用单次 Write 调用原子写入)
  • 已按契约编写摘要

参考文件

  • 审计输出架构: references/audit_output_schema.md

版本: 1.0.0 最后更新: 2026-03-03

评论

支持 Markdown