双轨配置中心 — 全量 + 灰度 + 一键回滚
AdminConfig 同时存 value(全量)和 gray_value(灰度),配 gray_strategy(percentage / phone_list / tier)。任何变更进 history,一键回滚到任意历史版本。
运营有个永恒矛盾:想试新值,又怕影响所有用户。常规做法是「feature flag」或「A/B 实验平台」,但那俩都是专门的工具,有自己一套世界观。我们做了个简化版叫「双轨配置」。
双轨是哪两轨
每个配置项同时存两个值:
- value:全量(默认对所有用户生效)
- gray_value:灰度(命中 gray_strategy 的用户生效)
运行时通过 resolve_from_row(config, user) 解析:命中 gray_strategy → 返 gray_value,否则返 value。
灰度策略
3 种 strategy,够覆盖 95% 场景:
- percentage:N% 用户命中(用 user.id % 100 < N,稳定哈希)
- phone_list:具体手机号白名单(beta 用户)
- tier:特定等级用户(free / pro)
# admin_configs/free.project_limit 演示
key: free.project_limit
value: 1
gray_strategy: { type: percentage, pct: 30 }
gray_value: 2
# → 70% Free 用户看到 1 个项目上限,30% 看到 2历史 + 一键回滚
每次 PATCH 配置都写一行 admin_config_history(value_before / value_after / strategy / change_note / updated_by / updated_at)。回滚就是反向跑历史 — 把当前 value 设回 history 行的 value_before。
这套设计让运营改值无压力:有问题就 admin /audit-log 点回滚,任何时候看历史都清楚。
为什么不用 LaunchDarkly / Optimizely?
因为 1) 收费贵;2) SaaS 依赖;3) 配置项每次都得拿网络请求(SDK 缓存有 stale 问题)。我们的需求小 — 几十个配置项,每个都是简单值或简单 JSON。直接放数据库,配置中心服务端解析,$0 成本。
把简单事做简单。LaunchDarkly 解决的问题是「跨产品线大规模配置」,我们一个产品一组配置,用不到那种规模。
Phase 2 演进
- 缓存策略:resolve_from_row 加 lru_cache (key, user_id) → 30s TTL,减少 db 查询
- WebSocket 推送:gray_strategy 变更时推给所有在线用户,无需刷新即可生效
- 试算:admin UI 点「试算」给一个 phone,看这个用户在当前 gray_strategy 下会拿到哪个值
试算已经做了,见 /v1/admin/configs/{key}/preview?phone=...。