前言¶
DeepSeek Harness (DSH) 的 JSONL 会话持久化后端遵循“每个会话一个活跃写入器”的策略,本身不具备跨进程的防御机制。如果两个 dsh 服务器共享同一个 DSH_HOME(例如桌面包装器自行启动服务器,或同时运行两个 dsh web 进程),它们会使用过期的序列游标追加批次,导致会话日志损坏,报错信息通常为 corrupt session log: seq gap in committed region。
dsh-single-instance-guard 插件旨在将这种静默的损坏转化为启动时的明确失败,通过独占锁定防止并发启动。
插件概述¶
该插件是 DeepSeek Harness 的一个零依赖插件,由 Tang-mm95 维护,采用 MIT 许可证。它的核心作用是在启动时对 DSH_HOME 数据目录进行原子独占锁定。如果检测到另一个 dsh 服务器正在使用该目录,插件将中止启动。
核心功能¶
- 原子独占锁定:在
<DSH_HOME>目录下创建.dsh-server.lock文件,文件内容包含{ pid, startedAt, hostname }。使用O_EXCL标志确保创建操作的原子性。 - 并发检测与中止:检测到冲突时,先探测锁定持有者的进程存活状态。若持有者存活,则中止当前启动并显示双语错误;若持有者已不存在,则移除锁并重试一次。
- 自动清理:进程退出时,仅当锁文件仍属于该进程时,自动移除锁文件。
- 零依赖:不依赖任何第三方库。
安装与配置¶
可以通过 CLI 安装,也可以手动配置。
CLI 安装¶
dsh plugin --profile <profile> add dsh-single-instance-guard
手动配置¶
在 profile 的 cordis.patch.yml 中添加以下行。必须确保该配置位于会话相关 bundles 之前:
- insert:
- id: single-instance-guard
name: 'dsh-single-instance-guard'
工作原理¶
插件首先解析 DSH_HOME(优先级为 $DSH_HOME 或 ~/.dsh)。随后尝试创建 .dsh-server.lock 文件。
- 成功:文件创建成功,插件持有锁。
- 冲突:文件已存在。插件读取文件内容并检查对应 PID 的进程是否存活。存活则报错退出;若 PID 不存在或文件格式无法解析,视为过期锁,删除后重试一次。
- 退出:进程结束时,检查文件中的 PID 是否仍为当前进程,是则删除锁文件。
适用场景与注意事项¶
该插件适用于需要确保同一 DSH_HOME 目录下只有一个 dsh 服务器运行的环境。
需要注意以下限制:
* 极小的竞态条件:当两个进程同时发现并尝试删除同一个过期锁时,存在极小的竞争窗口。但原子 O_EXCL 写入机制确保了只有一个进程能最终获取锁,另一个进程会在重试时正确失败。
* 路径保护:仅保护同一 DSH_HOME 路径下的并发服务器。对于指向同一个 session 文件但路径不同的两个 dsh 实例,插件无法保护。
* 环境要求:需要 Node.js >= 18。
总结¶
通过引入此插件,可以在 DSH 环境中避免因并发启动导致的会话日志损坏问题。相关源码与文档可在其 GitHub 仓库查看。