代表性面试主题

系统设计面试:如何保证 Feature Flag 百分比分流稳定?

系统设计困难
Offer.cc 编辑团队发布 更新

题干

同一用户会从多个区域和服务实例访问系统。Feature Flag 配置传播存在秒级延迟时,如何保证百分比分流稳定,并避免用户在新旧变体之间来回跳转?

题干与适用场景

这道题只讨论 Feature Flag 的评估热路径。控制台、审批和完整平台建设已有另一套设计题;这里要解决的是:配置会异步传播,调用方可能来自不同语言的 SDK,同一用户仍应得到可解释、可复现的变体。假设评估发生在服务端进程内,规则支持属性匹配和百分比分流。

面试官考察点

  • 能否把“配置最终一致”与“单个用户分桶稳定”拆开处理。
  • 是否定义唯一的 targetingKey、规范化规则和跨 SDK 一致的哈希算法。
  • 是否用不可变快照和单调版本切换配置,避免一次请求读到半份新配置。
  • 是否处理缺少上下文、类型错误、未知旗标和配置过期时的默认值。
  • 能否用黄金测试向量、影子评估和分布指标证明结果一致。

回答前需要澄清的问题

先确认稳定性的边界:同一用户是否要求跨设备、跨区域和跨语言 SDK 命中同一变体;匿名用户用什么稳定标识;扩大灰度比例时是否要求原 10% 用户继续留在新版本。还要确认规则属性是否包含敏感信息、配置可容忍多长时间的不一致,以及紧急关闭能否覆盖普通百分比分流。

30 秒回答框架

我会把规则编译成带版本的不可变快照,在 SDK 本地评估。百分比分流先把 targetingKeyflagKey 和种子编码成带长度边界的规范元组,再做确定性哈希并映射到固定桶区间。普通变更先分发未生效快照,等所有服务区域确认就绪后,由控制面推进唯一的分配代次;会话或持久身份始终携带该代次,实例在有限重叠期内同时保留新旧快照,因此本地时钟偏差不会让用户切换规则版本。上下文缺失、规则类型错误或配置过期时返回调用方默认值及原因码,上线前用跨语言黄金向量和影子评估验证一致性。

分步骤深入解答

先固定输入契约。每次服务端评估都要提供非空 targetingKey;匿名流量可以用持久化匿名 ID,但不能每次请求重新生成。字符串编码、大小写、空白、数字和时间格式都要写入协议。哈希输入使用带版本号和长度前缀的 UTF-8 元组,不能直接拼接字符串或只依赖分隔符,否则字段边界和值中的特殊字符会在不同 SDK 中产生歧义。

分桶函数可写成 bucket = hash(encodeTuple(v, seed, flagKey, targetingKey)) mod 100000。每个变体占用不重叠的连续区间。10% 扩到 20% 时只扩大目标区间,因此原先命中的用户继续命中。flagKey 防止不同旗标产生完全相关的样本;需要重新洗牌时显式更换 seed,并把它视为有审计记录的破坏性操作。

规则匹配和分桶必须针对同一个不可变快照。分发器先把带校验和与单调版本的未生效快照送达所有服务区域。区域全部校验并确认就绪后,控制面推进唯一的分配代次;会话令牌或持久身份记录携带该代次,每个区域按请求引用的版本评估,并在分配有效期结束前保留旧快照。缺少引用版本的实例停止承接该请求。这套协议依赖版本传递,不依赖各节点时钟同步。紧急关闭可以明确覆盖粘性,以安全优先并保留版本记录。

OpenFeature 的评估接口允许调用方提供默认值,并在异常时返回默认值及错误原因。实现时区分旗标不存在、类型不符、缺少 targetingKey 和 provider 未就绪。指标只记录低基数字段,邮箱和设备标识等敏感属性不进入普通日志。

验证分三层:所有 SDK 运行相同黄金输入与期望变体,覆盖空值、分隔符、Unicode 和字段边界碰撞;发布前用生产形状的快照做影子评估,比较旧新引擎并演练区域就绪失败;上线后观察变体占比、默认值命中率、快照年龄和区域版本分布。比例偏差只是告警线索,单个决定仍应能由快照版本、规则 ID 和桶位复算。

高质量示范回答

我会让每个 SDK 只评估经过校验的完整快照。请求提供稳定的 targetingKey,SDK 把版本化、带长度边界的 seedflagKeytargetingKey 元组做统一哈希,再映射到十万个固定桶;灰度扩容只调整桶区间,所以已命中的用户不会掉出目标变体。

普通变更先送达所有区域,再由控制面推进分配代次。会话或持久身份携带该代次,每个区域在分配有效期内保留并评估请求引用的快照,因此跨区域请求无需依赖时钟同步也只读取一个规则版本。缺少该快照的实例停止承接请求。SDK 拒绝版本倒退,配置过期或上下文无效时返回调用方默认值;紧急关闭则明确覆盖普通粘性并优先收敛。

常见错误

  • 每次评估调用远程服务,把业务可用性绑定到配置网络。
  • 使用语言运行时自带哈希,导致不同进程或 SDK 结果不一致。
  • 只对用户 ID 哈希,使所有旗标样本高度相关。
  • 配置逐字段更新,让请求同时读取新规则和旧权重。
  • 缺少 targetingKey 时随机分桶,造成同一用户持续跳变。
  • 为排障记录完整评估上下文,扩大隐私暴露面。

追问及应对

配置版本不同,怎样保证跨区域完全一致?

异步分发本身无法承诺零窗口的全局强一致。普通变更先完成所有服务区域的就绪确认,再由控制面推进一个分配代次,并由会话或持久身份在每个请求中携带该代次。各区域保留被引用的快照直到分配过期,缺少该版本的实例退出服务。紧急关闭可以覆盖粘性,优先快速收敛并监控尚未更新的实例。

匿名用户没有账号 ID 怎么办?

使用客户端持久化的随机匿名 ID,并说明清除存储或换设备后会重新分桶。IP 会共享、变化,也带来额外隐私问题,不适合作为稳定身份。

如何安全更换哈希算法?

把算法版本和种子写入快照,先影子计算新旧桶位并测量迁移比例。需要保持粘性时建立过渡映射;允许重新抽样时,也要把切换作为显式发布并保留回滚版本。

如何发现某个 SDK 实现偏差?

所有 SDK 运行同一组黄金测试向量,并周期性上报低基数的算法版本、快照版本和聚合变体计数。发现偏差时冻结该 SDK 的配置升级、保留最近有效快照,再修正规范化或哈希实现。

公开来源

同类题目

相关面试工具

用 Solve 整理系统设计回答

从澄清需求开始,展开规模、架构、组件选择和取舍。

查看工具