From dfdfb7e4abc5ed67841649015174bcb44eb547b1 Mon Sep 17 00:00:00 2001 From: nk Date: Thu, 4 Jun 2026 09:52:39 +0300 Subject: [PATCH] =?UTF-8?q?fix(enrich):=20Discogs=20=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D1=82=D1=82=D0=BB=D0=B8=D1=82=20=D0=BF=D0=BE=20IP=20=E2=80=94?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20=D0=BB=D0=B8=D0=BC=D0=B8?= =?UTF-8?q?=D1=82=20~54/=D0=BC=D0=B8=D0=BD=20(=D1=81=D1=82=D0=BE=D0=BF=204?= =?UTF-8?q?29-=D1=88=D1=82=D0=BE=D1=80=D0=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Два токена с одного IP не помогают (Discogs лимитит по IP, не по токену) — вызывало 100% 429. Вернул общий интервал 1100мс (~54/мин). 2-й токен полезен только на втором IP (DE-воркер). Co-Authored-By: Claude Opus 4.8 --- src/enrich/discogs.service.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/enrich/discogs.service.ts b/src/enrich/discogs.service.ts index c406a33..8fc5763 100644 --- a/src/enrich/discogs.service.ts +++ b/src/enrich/discogs.service.ts @@ -38,7 +38,11 @@ export class DiscogsService { process.env.DISCOGS_TOKEN ?? '', process.env.DISCOGS_TOKEN2 ?? '', ].filter((t) => t.length > 0); - private readonly slots: number[] = this.tokens.map(() => 0); + // ВАЖНО: Discogs троттлит по IP (не по токену), поэтому с одного сервера + // общий лимит ~54/мин независимо от числа токенов. Токены ротируем вхолостую + // (выгода от 2-го токена — только на ВТОРОМ IP, напр. DE-воркер). + private nextSlot = 0; + private rr = 0; private readonly minIntervalMs = 1100; // Без токена обогащение жанрами не работает (поиск требует авторизации) @@ -46,18 +50,15 @@ export class DiscogsService { return this.tokens.length > 0; } - // Резервирует слот наименее загруженного токена, ждёт его и возвращает токен private async pickToken(): Promise { - let idx = 0; - for (let i = 1; i < this.slots.length; i++) { - if (this.slots[i] < this.slots[idx]) idx = i; - } const now = Date.now(); - const start = Math.max(now, this.slots[idx]); - this.slots[idx] = start + this.minIntervalMs; + const start = Math.max(now, this.nextSlot); + this.nextSlot = start + this.minIntervalMs; // глобальный интервал (IP-лимит) const wait = start - now; if (wait > 0) await new Promise((r) => setTimeout(r, wait)); - return this.tokens[idx]; + const token = this.tokens[this.rr % this.tokens.length]; + this.rr++; + return token; } async lookup(artist: string, song: string): Promise {