Zsens Admin 插件配置页:我把 `saveConfig` 拆成"三阶段提交"后,终于治好了配置丢失与缓存脏读
之前写后台配置页,总觉得 `saveConfig()` 是个黑盒——表单 POST 过去,成功就刷新页面,失败就弹个红字。直到有次运营同事反馈:刚保存的阈值配置,刷新页面又变回老数值,清浏览器缓存没用,过两分钟自己好了。
排查下来是并发下的缓存穿透 + 异步刷新 race condition。两个管理员同时改配置,A 先写库、B 后写库,但 B 的缓存刷新请求先到达,A 的缓存刷新后到达,最终数据库是 A 的值,缓存却是 B 的值。更坑的是,某些框架的缓存组件默认用 `file` 驱动时,`mtime` 精度只有秒级,1 秒内多次写入直接判定"无需更新"。
后来我干脆把配置保存拆成三阶段提交,把"写库"和"刷缓存"解耦成显式步骤,而不是依赖框架的魔法方法:
// 阶段一:预校验 + 版本锁生成
public function prepareSave(array $input): ConfigDraft
{
$draft = new ConfigDraft();
$draft->payload = $this->validator->stripTags($input); // 先清洗
$draft->version = $this->meta->bumpVersion(); // 原子自增
$draft->lockKey = 'config:lock:' . $draft->version;
// 5 秒排他锁,防止同一管理员连点
if (!$this->cache->add($draft->lockKey, 1, 5)) {
throw new ConcurrentEditException('操作太频繁,请稍后再试');
}
return $draft;
}
// 阶段二:事务写库,带乐观锁校验
public function commitSave(ConfigDraft $draft): bool
{
return $this->db->transaction(function () use ($draft) {
$affected = $this->configModel
->where('group', $draft->payload['group'])
->where('version', '
最新打赏

