Linear Rate Limiting
参考:https://linear.app/developers/rate-limiting
两类预算
Linear 按以下两种方式计费:
- 请求速率 —— 按 IP、按令牌(取决于套餐层级:免费版约 1500/小时,付费版更高)
- 复杂度点数 —— 按令牌(典型约 5000/小时)
真正会对大型查询造成影响的是复杂度预算。每个查询的成本取决于:
- 字段数量
- 连接规模(
first: 100比first: 10成本更高) - 嵌套解析
- 请求头
每个响应都包含:
X-RateLimit-Limit—— 每个窗口的额度X-RateLimit-Remaining—— 剩余额度X-RateLimit-Reset—— 额度重置的时间戳(epoch)X-Complexity—— 本次查询的成本X-Complexity-Limit、X-Complexity-Remaining、X-Complexity-Reset
lib/linear-client.mjs GraphQL 客户端将这些信息以回调形式暴露:
client.onRateLimit((info) => {
if (info.complexityRemaining < 1000) backoff(info.resetIn);
});
退避策略
当你收到 429(或 complexity_limit GraphQL 错误)时:
- 读取
X-RateLimit-Reset/X-Complexity-Reset - 休眠至该时间戳 + 1 秒抖动
- 重试一次;第二次 429 → 进入死信队列(DLQ)并告警(不要让用户在重试中被淹没)
async function withBackoff<T>(fn: () => Promise<T>, max = 3): Promise<T> {
for (let i = 0; i < max; i++) {
try { return await fn(); }
catch (e: any) {
if (e.status !== 429) throw e;
const reset = Number(e.headers["x-ratelimit-reset"]) * 1000 - Date.now();
await new Promise(r => setTimeout(r, Math.max(reset, 1000) + Math.random() * 500));
}
}
throw new Error("rate-limit retries exhausted");
}
批量操作的节流
当变更操作超过 50 个时,需要显式节流:
- 最多 10 个并发在途(令牌桶容量)
- 每 100ms 补充 1 个令牌
- 若
complexity_remaining< 30%,则暂停
实现位于 lib/rate-limit.ts(令牌桶)。
Webhook 背压
能用 Webhook 时就不要轮询 Linear。桥接智能体:
- 订阅 Webhook 以获取实时变更
- 仅在数据对账时轮询(每 6 小时一次),或在 Webhook 死信队列重放之后轮询
提示
- 优先使用一个包含所有所需字段的大查询,而非许多小查询(往返更少,通常总复杂度更低)
- 如果只需要 10 条,就不要请求
first: 250 - 在某个流程持续期间缓存变化缓慢的数据(团队列表、标签列表)
- 避免在大连接上使用
every:和none:过滤器 —— 它们开销很高