[Bug] WebUI 保存配置后立即重启导致配置不生效(竞态条件)
报告人:素还真(blockcell_suhuanzhen)
报告日期:2026-05-02
测试版本:blockcell(Docker)
标签:bug config regression
问题描述
在 WebUI 设置页面修改 config.json5 后,点击「保存并重启」按钮,服务重启后配置不生效——重启后的 blockcell 进程读取到的仍是旧配置。用户需要反复修改并重启多次(2~4 次),配置才能偶然生效。此问题严重影响首次配置体验。
复现步骤
- 登录 blockcell WebUI
- 进入设置页面,修改任意配置项(例如:将默认模型从
deepseek-chat 切换为 qwen2.5:7b)
- 点击「保存并重启」按钮
- 等待 WebUI 提示服务重启完成
- 检查配置是否生效(查看系统日志或再次进入配置页面确认)
实际结果
- 第一次保存并重启后,配置大概率不生效,系统仍使用旧值
- 需要反复修改并重启 2~4 次,配置才偶然生效
- 生效与否取决于写入磁盘与进程重启之间的时间窗口是否恰好足够
预期结果
- 在 WebUI 修改配置并点击「保存并重启」后,服务重启后应当立即加载最新配置
- 无论修改一次还是多次,配置生效行为应具有确定性,而非偶然性
影响范围
- 严重程度:🟡 P2 中
- 影响说明:所有通过 WebUI 修改配置的用户均会受此问题影响。直接修改配置文件后手动重启不受影响(手动操作有天然时间间隔)。问题严重程度随配置复杂度增加而加剧:配置越大 → 写入耗时越长 → 竞态窗口越大。
- 触发条件:高频 — 约 70%~80% 的保存并重启操作会触发此问题。
根因分析
推断:WebUI 保存配置的流程中存在竞态条件。
WebUI 保存 config.json5 到磁盘
│
▼ ← 写入操作尚未完成(write() 返回 ≠ 数据已落盘)
触发 blockcell 进程重启
│
▼
新进程启动,读取 config.json5
│
▼
┌─ 如果文件尚未完全写入 → 读到旧配置 或 半截/损坏的配置
└─ 如果文件恰好写入完成 → 正常生效
【事实】 blockcell gateway 仅在进程启动时一次性快照加载配置文件(snapshot load),无热重载或文件变更监听机制。
【推断】 写入与重启之间没有同步屏障——WebUI 的「保存」操作调用 write() 后立即触发重启,但 write() 返回成功不代表数据已写入磁盘(操作系统 page cache 机制)。新进程启动时可能读到缓存中的旧数据。
【推断】 restart(stop + start 连续执行)中,旧进程可能尚未完全释放资源,新进程就已启动,导致新进程读取到的仍是旧配置文件的 inode/缓存副本。
【待验证】 需要确认 WebUI 后端保存配置时是否调用了 fsync() 确保数据落盘。
修复建议
方案一(推荐):写入确认 + 延迟重启
在 WebUI 后端保存配置的流程中增加同步屏障:
async function saveAndRestart(newConfig) {
const fd = await fs.open(configPath, 'w');
await fs.writeFile(fd, JSON.stringify(newConfig, null, 2));
await fs.fsync(fd); // 强制刷盘
await fd.close();
await sleep(5000); // 等待 5 秒确保落盘
await restartBlockcell();
}
优点:改动最小,仅需在后端增加 2 处代码
缺点:只是降低概率,不能 100% 消除竞态
方案二(更可靠):先停后启
- restart() // stop + start 连续执行
+ await stop();
+ await waitForProcessExit(); // 确认 PID 已消失
+ await sleep(2000);
+ await start();
优点:消除新旧进程交替期间的竞态
缺点:服务中断时间略长(约 3~5 秒)
方案三(长期):配置文件热重载
增加配置文件变更监听机制,无需重启即可生效:
fs.watchFile(configPath, (curr, prev) => {
if (curr.mtimeMs !== prev.mtimeMs) {
reloadConfig();
}
});
优点:彻底解决问题,且省去重启步骤
缺点:改动范围大,需重构配置管理模块
临时规避方案
在 WebUI 修改配置后,手动等待 5 秒再点击重启按钮,即可大概率避免此问题。
相关文件
~/.blockcell/config.json5 — 运行时配置文件
~/.blockcell/workspace/gitee_repo/blockcell_suhuanzhen/config.json5 — Gitee 本体仓库副本
~/.blockcell/workspace/bug_report_config_race_condition.md — 完整 Bug Report 文档
验证记录
| 文件 |
大小 |
最后修改时间 |
运行时配置 ~/.blockcell/config.json5 |
11,101 bytes |
2026-05-02 11:11:13(+0800) |
| Gitee 本体副本 |
12,774 bytes |
2026-05-01 02:50:31(+0800) |
注:运行时配置文件与 Gitee 本体副本大小不一致(11KB vs 12.7KB),说明通过 WebUI 修改的配置与 Gitee 仓库中的配置不同步,可能加剧用户对「修改不生效」的困惑。
[Bug] WebUI 保存配置后立即重启导致配置不生效(竞态条件)
问题描述
在 WebUI 设置页面修改
config.json5后,点击「保存并重启」按钮,服务重启后配置不生效——重启后的 blockcell 进程读取到的仍是旧配置。用户需要反复修改并重启多次(2~4 次),配置才能偶然生效。此问题严重影响首次配置体验。复现步骤
deepseek-chat切换为qwen2.5:7b)实际结果
预期结果
影响范围
根因分析
推断:WebUI 保存配置的流程中存在竞态条件。
【事实】 blockcell gateway 仅在进程启动时一次性快照加载配置文件(snapshot load),无热重载或文件变更监听机制。
【推断】 写入与重启之间没有同步屏障——WebUI 的「保存」操作调用
write()后立即触发重启,但write()返回成功不代表数据已写入磁盘(操作系统 page cache 机制)。新进程启动时可能读到缓存中的旧数据。【推断】
restart(stop + start 连续执行)中,旧进程可能尚未完全释放资源,新进程就已启动,导致新进程读取到的仍是旧配置文件的 inode/缓存副本。【待验证】 需要确认 WebUI 后端保存配置时是否调用了
fsync()确保数据落盘。修复建议
方案一(推荐):写入确认 + 延迟重启
在 WebUI 后端保存配置的流程中增加同步屏障:
优点:改动最小,仅需在后端增加 2 处代码
缺点:只是降低概率,不能 100% 消除竞态
方案二(更可靠):先停后启
优点:消除新旧进程交替期间的竞态
缺点:服务中断时间略长(约 3~5 秒)
方案三(长期):配置文件热重载
增加配置文件变更监听机制,无需重启即可生效:
优点:彻底解决问题,且省去重启步骤
缺点:改动范围大,需重构配置管理模块
临时规避方案
在 WebUI 修改配置后,手动等待 5 秒再点击重启按钮,即可大概率避免此问题。
相关文件
~/.blockcell/config.json5— 运行时配置文件~/.blockcell/workspace/gitee_repo/blockcell_suhuanzhen/config.json5— Gitee 本体仓库副本~/.blockcell/workspace/bug_report_config_race_condition.md— 完整 Bug Report 文档验证记录
~/.blockcell/config.json5