概述
功能开关总闸需要三个属性:数据源不可用时仍能落到安全默认值;传播时间有明确上限,运维知道何时生效;每次切换都有审计记录。数据库行是事实来源,进程内缓存只是降低延迟,代码中的默认值是最后防线。默认值不一定总是 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 发送失效消息。该方案只有布尔总闸,不提供按用户比例灰度。数据库故障时所有开关都会回到默认值,因此默认值必须代表安全状态。管理端必须放在认证和网络限制之后,审计表只追加,不要当作通用日志表使用。