☰
treg Idempotent-Key 多租户隔离原理:你的请求为何不会拿到别人的结果
2026/9/30 16:43:25 网站建设 项目流程

treg Idempotent-Key 多租户隔离原理:你的请求为何不会拿到别人的结果

【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg

treg 是一个面向 Agent 的"工具版 OpenRouter"——它把成百上千家第三方数据 API 统一成一个调用入口,按团队(租户)计费。Agent 天然爱重试,而 treg 的 Idempotency-Key 幂等键机制保证:重试免费、结果不变、且绝不会把 A 团队的数据回放给 B 团队。本文讲清楚这套多租户隔离是怎么做到的。

为什么 Agent 场景特别需要幂等键?

先说清楚问题:Agent 的重试远比人类频繁,而其中一种重试最"贵"——

  1. treg 调用上游供应商,供应商成功且已扣费;
  2. 响应在回传途中丢失(网络抖动、客户端超时);
  3. Agent 发起重试,treg 又调用了一次供应商,又扣了一次费。

从账本上看这是两笔各自成功的调用。更棘手的是:仅仅"记住这个 key 已扣费、跳过第二次收费"是不够的——第二次上游调用照样发生,钱照样付给供应商,只是把成本从用户转嫁到了平台。所以真正的幂等必须做到:第二次请求根本不出去,直接回放第一次的应答。

这套设计在 docs/IDEMPOTENT-CALLS-PLAN.md 中有完整推演。

只存"成功且已计费"的响应,窗口 24 小时

实现规则非常克制(源码见 idempotency.py):

  • 只有带 key 才生效:不传Idempotency-Key的请求行为与从前逐字节一致,服务端绝不"替你想 key";
  • 只存已计费的成功响应:失败本来就是免费的,没必要保护,存储因此有界;
  • 24 小时窗口:重试发生在秒级,一天足够宽裕,过期行直接消失。

核心隔离:key 按"调用者"作用域,而不是全局唯一

这是整篇文章的重点,也是"你的请求不会拿到别人的结果"的根本保证。

IdempotentCall表上的唯一约束是(membership_id, key)复合键,而不是 key 单独唯一(定义在 models.py):

uq_idem_caller_key → UNIQUE (membership_id, key)

这里的membership是 treg 的统一"调用者身份":一个人类成员的 token、一个 Agent token、一个 OAuth 授权,最终都解析为同一个 Membership。于是只有一条规则覆盖所有情形:这个 key 归"发它的人"所有。

为什么这个设计如此重要?

  • key 是客户端自己起的。两个团队迟早都会起出retry-1这样的名字。如果只按 key 隔离,先到的那个团队的响应体就会被回放给后到的团队——这是该功能里唯一会造成数据泄露(而不只是多扣钱)的失败模式;
  • 为什么不是"按团队"隔离?因为同一个团队里两个写得懒的 Agent 都会抢着用retry-1。按团队隔离会让第二个 Agent 收到莫名其妙的拒绝;按调用者隔离,它们压根碰不到彼此。作用域越小越安全,Stripe 也是按 API key 划分的,同理。

再进一步:转售场景的"标签分区"

还有一个更隐蔽的跨租户漏洞:一个转售型开发者会把所有用户都挂在自己的一个 token 下调用 treg。此时两个不同用户的请求都带着同一个调用者身份,如果都用retry-1作 key,第二个用户就会拿到第一个用户的缓存结果——跨租户泄露在"下一层"重新出现。

解法在 _scoped_idempotency_key:把调用者声明的主标签(primary tag)折叠进存储的 key 里,相当于给每个终端用户切出一个独立命名空间,且不需要任何数据库迁移;如果调用还带强制的 pin,pin 也会并入命名空间,改 pin 绝不可能暴露旧缓存。多租户边界细节可参见 docs/context/architecture/multi-tenancy.md。

三道防线:拿错结果会被"大声拒绝"

即便有了隔离,还有两种误用会被当场拦下,而不是静默返回错误数据:

情况行为说明
同一个 key 用于不同的请求返回422请求指纹(方法+路径+query+body 的哈希)不匹配时拒绝,避免把旧答案塞给新问题
同一个 key 的请求还在飞返回409第一次调用尚未完成,告知"稍后再试",阻止第二次重复扣费
并发重试同时到达数据库裁决先写入pending占位行,输者靠唯一约束插入失败而等待赢家,绝不发起第二次上游调用

占位行即锁:两个重试同时到达、同时查不到缓存时,谁先插入(membership_id, key)谁赢,另一个只能等——"读后写"的竞态窗口由数据库唯一约束关闭,这与账本ledger.reserve的条件更新是同一套思路。

回放响应如何被识别?

回放不是静默的。命中的重试会带上明确的响应头(详见 docs/context/interface/api.md):

  • X-Treg-Idempotent-Replay: true—— 告诉调用者"这是回放的";
  • X-Treg-Cost-Micro: 0—— 回放不再产生新费用,客户端的本地计费合计保持真实;
  • X-Treg-Original-Cost-Micro: <原费用>—— 原始调用花了多少,一并给出。

通过 MCP 协议调用时同样是可选的idempotency_key参数,回放结果带replayed: true。

小结

treg 的幂等机制可以浓缩为四句话:

  1. 只存付费成功的答案,24 小时过期——重试免费,但绝不变成通用缓存;
  2. 唯一约束落在 (调用者, key) 上——key 撞车是常态,跨租户回放才是事故;
  3. 转售场景再按标签分区一层——共享 token 下每个终端用户各有独立命名空间;
  4. 指纹不符报 422、在途冲突报 409、并发靠占位行+唯一约束——错误大声暴露,数据永不串户。

对普通用户来说这意味着:放心让 Agent 重试,账单不会重复,结果不会串门。

【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询