fix(api): fail fast on dead PartsCatalogs proxy ports + tunable timeouts

The PartsCatalogs ProxyAgent had no connect timeout, so a dead DataImpulse proxy
port stalled for undici's 10s default before the retry rotated to a fresh port
(prod: "ConnectTimeoutError ... 74.81.81.81:10628, timeout: 10000ms"). Three such
stalls exceed the caller's 25s decode budget, turning a transient bad port into a
hard decode failure.

Set an explicit 6s proxy connect timeout so retries rotate to a live port within
budget, and make the request timeout / retry count / connect timeout env-tunable
(PCAT_REQUEST_TIMEOUT_MS, PCAT_MAX_RETRIES, PCAT_PROXY_CONNECT_TIMEOUT_MS) with the
current values as defaults. Phase 2 of 4 on decode reliability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-25 00:09:27 +03:00
parent 257052162e
commit c331dca9fe

View File

@@ -18,7 +18,12 @@ import {
} from "./parts-catalogs.types";
const API_BASE = "https://gui.parts-catalogs.com/v3/api/proxy";
const REQUEST_TIMEOUT = 30_000;
const REQUEST_TIMEOUT = Number(process.env.PCAT_REQUEST_TIMEOUT_MS) || 30_000;
const MAX_RETRIES = Number(process.env.PCAT_MAX_RETRIES) || 2;
// A dead DataImpulse proxy port otherwise stalls for undici's 10s default connect
// timeout before the retry rotates to a fresh port — three of those blow the
// caller's 25s decode budget. Fail fast so retries reach a live port in time.
const PROXY_CONNECT_TIMEOUT = Number(process.env.PCAT_PROXY_CONNECT_TIMEOUT_MS) || 6_000;
@Injectable()
export class PartsCatalogsService {
@@ -188,7 +193,7 @@ export class PartsCatalogsService {
params?: Record<string, string>,
externalSignal?: AbortSignal,
): Promise<any> {
const maxRetries = 2;
const maxRetries = MAX_RETRIES;
let session: PcatSession | null = null;
@@ -227,7 +232,10 @@ export class PartsCatalogsService {
// Use undici ProxyAgent if proxy is configured
if (session.proxyUrl) {
const { ProxyAgent } = await import("undici");
fetchOptions.dispatcher = new ProxyAgent(session.proxyUrl);
fetchOptions.dispatcher = new ProxyAgent({
uri: session.proxyUrl,
connect: { timeout: PROXY_CONNECT_TIMEOUT },
});
}
const response = await fetch(url.toString(), fetchOptions);