From 32db9ff5387244f3ce47627ff8142983e5325566 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 29 Apr 2026 13:50:17 +0100 Subject: [PATCH] fix(discord): prune remapped rest buckets --- .../discord/src/internal/rest-scheduler.ts | 7 ++++++- extensions/discord/src/internal/rest.test.ts | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/extensions/discord/src/internal/rest-scheduler.ts b/extensions/discord/src/internal/rest-scheduler.ts index 5f061ed6608..2bc8c86505f 100644 --- a/extensions/discord/src/internal/rest-scheduler.ts +++ b/extensions/discord/src/internal/rest-scheduler.ts @@ -144,6 +144,11 @@ export class RestScheduler { return false; } + private shouldPruneIdleBucket(key: string): boolean { + const mappedBucketKey = this.routeBuckets.get(key); + return mappedBucketKey !== key && !this.hasBucketReference(key); + } + private bindRouteToBucket(routeKey: string, bucketKey: string): BucketState { const target = this.getBucket(bucketKey); target.routeKeys.add(routeKey); @@ -262,7 +267,7 @@ export class RestScheduler { break; } if (bucket.pending.length === 0) { - if (bucket.active === 0 && !this.routeBuckets.has(key) && !this.hasBucketReference(key)) { + if (bucket.active === 0 && this.shouldPruneIdleBucket(key)) { this.buckets.delete(key); } continue; diff --git a/extensions/discord/src/internal/rest.test.ts b/extensions/discord/src/internal/rest.test.ts index 14188ce1ef3..b03db6fe48f 100644 --- a/extensions/discord/src/internal/rest.test.ts +++ b/extensions/discord/src/internal/rest.test.ts @@ -83,6 +83,22 @@ describe("RequestClient", () => { ]); }); + it("prunes idle route buckets after Discord bucket remapping", async () => { + const client = new RequestClient("test-token", { + fetch: async () => + new Response(JSON.stringify({ id: "first" }), { + status: 200, + headers: { "X-RateLimit-Bucket": "channel-messages" }, + }), + }); + + await expect(client.get("/channels/c1/messages")).resolves.toEqual({ id: "first" }); + + const metrics = client.getSchedulerMetrics(); + expect(metrics.activeBuckets).toBe(1); + expect(metrics.buckets.map((bucket) => bucket.key)).toEqual(["channel-messages:channels/c1"]); + }); + it("waits for a learned bucket reset before dispatching the next request", async () => { vi.useFakeTimers(); vi.setSystemTime(0);