mcpField
E
execution-lifecycle-manager

execution-lifecycle-manager

github.com/curiositechAgent 工作流25771 次安装2 天前更新
代码仓库

关于 execution-lifecycle-manager

为实现 DAG 执行管理的开发者提供生命周期管理模式,涵盖状态跟踪、取消传播与优雅关闭。内容也涉及清理孤儿进程和中止后的成本记录。

主要功能

  1. 01跟踪活动执行及其状态
  2. 02传播 AbortSignal 以取消子执行
  3. 03先发送 SIGTERM,再按需使用 SIGKILL
  4. 04关闭时停止执行并清理进程
  5. 05中止处理中的最终成本记录

适用场景

  1. 01为 DAG 执行添加开始、停止、暂停和恢复控制
  2. 02在服务关闭时终止活动执行
  3. 03处理超时或用户取消并清理子进程

安装

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

$npx skills add https://github.com/curiositech/some_claude_skills --skill execution-lifecycle-manager

安装后在 Claude Code 中输入/execution-lifecycle-manager确认已加载。

附带文件references/process-signals.md

详细介绍

执行生命周期管理器

集中管理运行中的 DAG 执行,并采用优雅关闭模式。

适用场景

✅ 适用于:

  • 实现执行的启动/停止/暂停/恢复控制
  • 优雅地终止进程(SIGTERM → SIGKILL)
  • 跟踪整个系统中的活动执行
  • 清理孤立进程
  • 实现带成本跟踪的中止处理程序

❌ 不适用于:

  • 成本估算或定价计算(使用 cost-accrual-tracker)
  • 构建或修改 DAG 结构
  • 技能匹配或选择
  • 启动进程(直接使用执行器)

核心模式

1. 优雅关闭模式

始终先使用 SIGTERM,然后再升级为 SIGKILL:

// CORRECT: Two-phase shutdown
const GRACEFUL_TIMEOUT_MS = 2000;

async function terminateProcess(proc: ChildProcess): Promise<void> {
  proc.kill('SIGTERM');

  const forceKillTimer = setTimeout(() => {
    if (!proc.killed) {
      proc.kill('SIGKILL');
    }
  }, GRACEFUL_TIMEOUT_MS);

  await waitForExit(proc);
  clearTimeout(forceKillTimer);
}
2. AbortController 模式

使用 AbortController 传播取消信号:

// Parent (DAGExecutor)
const abortController = new AbortController();

// Pass signal to child executors
await executor.execute({
  ...request,
  abortSignal: abortController.signal,
});

// To abort all children:
abortController.abort();
3. 执行注册表模式

跟踪活动执行,以便监控和清理:

interface ActiveExecution {
  executionId: string;
  abortController: AbortController;
  status: 'running' | 'stopping' | 'stopped' | 'completed' | 'failed';
  startedAt: number;
  stoppedAt?: number;
}

class ExecutionManager {
  private executions: Map<string, ActiveExecution> = new Map();

  create(id: string): ActiveExecution { /* ... */ }
  stop(id: string, reason: string): Promise<StopResult> { /* ... */ }
  listActive(): ActiveExecution[] { /* ... */ }
}

反模式

不发送 SIGTERM,直接使用 SIGKILL

新手想法:“直接杀掉进程就行。”

实际情况:SIGKILL 不允许执行清理操作。进程无法:

  • 将缓冲区刷新到磁盘
  • 正常关闭网络连接
  • 释放锁
  • 保存部分进度

时间安排:

  • 始终先使用 SIGTERM,以便优雅关闭
  • 如果进程在 2-5s 后仍无响应,再使用 SIGKILL

正确做法:始终先使用 SIGTERM,将 SIGKILL 作为后备方案。

未传播中止信号

新手想法:“只跟踪顶层执行就行。”

实际情况:如果没有传播信号,子进程就会成为孤立进程:

  • 父进程退出,子进程仍在运行
  • 资源泄漏
  • 成本持续累积

正确做法:在整个执行树中传递 AbortSignal。

同步停止处理程序

新手想法:“停止操作应该立即返回。”

实际情况:停止是异步操作,进程需要时间才能终止:

  • 网络请求需要超时
  • 文件句柄需要关闭
  • 需要进行最终成本计算

正确做法:返回 Promise,并在清理完成后提供最终状态。

状态机

         ┌──────────┐
         │  idle    │
         └────┬─────┘
              │ start()
              ▼
         ┌──────────┐
    ┌───►│ running  │◄───┐
    │    └────┬─────┘    │
    │         │          │ resume()
    │         │ pause()  │
    │         ▼          │
    │    ┌──────────┐    │
    │    │ paused   │────┘
    │    └────┬─────┘
    │         │ stop()
    │         ▼
    │    ┌──────────┐
    └────│ stopping │ (transitional - 2-10s)
         └────┬─────┘
              │
     ┌────────┴────────┐
     ▼                 ▼
┌──────────┐     ┌──────────┐
│ stopped  │     │  failed  │
└──────────┘     └──────────┘

API 设计

停止端点响应
interface StopResponse {
  status: 'stopped';
  executionId: string;
  reason: string;  // 'user_abort' | 'timeout' | 'error'
  finalCostUsd: number;
  stoppedAt: number;
  summary: {
    nodesCompleted: number;
    nodesFailed: number;
    nodesTotal: number;
    durationMs: number;
  };
}
服务关闭时的清理
// In server.ts
process.on('SIGINT', async () => {
  console.log('Shutting down...');

  // Stop all active executions gracefully
  const active = executionManager.listActive();
  await Promise.all(
    active.map(e => executionManager.stop(e.executionId, 'server_shutdown'))
  );

  server.close();
});

集成点

组件职责
ExecutionManager跟踪执行并协调停止操作
DAGExecutor管理 AbortController 并编排执行波次
ProcessExecutor启动进程并处理 SIGTERM/SIGKILL
/api/execute/stop停止请求的 HTTP 接口

参考资料

有关 Unix 信号处理的详情,请参阅 /references/process-signals.md。

评论

支持 Markdown