← 中文资源

简体中文版本

添加带安全默认值的功能开关总闸

用数据库功能开关在不重新部署的情况下关闭风险功能,配合五秒缓存、安全默认值和审计日志,控制故障影响范围。

阅读英文原文

概述

功能开关总闸需要三个属性:数据源不可用时仍能落到安全默认值;传播时间有明确上限,运维知道何时生效;每次切换都有审计记录。数据库行是事实来源,进程内缓存只是降低延迟,代码中的默认值是最后防线。默认值不一定总是 false:推荐引擎关闭是安全的,状态页关闭可能造成更大故障,因此每个开关都要单独定义并测试默认状态。

步骤

创建开关表、只追加的审计表和初始记录:

CREATE TABLE feature_flags (
  name text PRIMARY KEY,
  enabled boolean NOT NULL DEFAULT false,
  updated_by text NOT NULL,
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE feature_flag_audit (
  id bigserial PRIMARY KEY,
  name text NOT NULL,
  enabled boolean NOT NULL,
  changed_by text NOT NULL,
  changed_at timestamptz NOT NULL DEFAULT now()
);

读取函数先查五秒缓存;未命中时查询数据库。查询失败、表不存在或行缺失时返回代码中的安全默认值,并清掉缓存,避免继续使用旧值。管理端接口在同一个事务里更新 feature_flags 并插入 feature_flag_audit;审计写入失败时回滚整个切换。接口只接受布尔值,未知开关返回 404,避免拼写错误创建永久关闭的新开关。提交后清除本实例缓存,其他实例最多等待一个 TTL。

业务入口只判断布尔值:

if (await isEnabled('recommendations_v2')) {
  res.json({ engine: 'v2', items: await loadRecommendations(req.user.id, 'v2') });
  return;
}
res.json({ engine: 'v1', items: await loadRecommendations(req.user.id, 'v1') });

验证

用管理员令牌 POST 关闭开关,返回 enabled: false,审计表新增一行。再打开开关,等待六秒,把 feature_flags 重命名为 feature_flags_offline,再等待六秒并请求推荐接口;此时应返回 engine: v1,证明数据库不可用时落到默认值。验证后立即把表名改回,否则管理端无法继续切换。

注意事项

五秒缓存意味着总闸不是全舰队瞬时生效,TTL 要按事故容忍度设定;需要更快传播时用 Redis pub/sub 发送失效消息。该方案只有布尔总闸,不提供按用户比例灰度。数据库故障时所有开关都会回到默认值,因此默认值必须代表安全状态。管理端必须放在认证和网络限制之后,审计表只追加,不要当作通用日志表使用。