fix(pcat-auth): two-armed post-goto poll — fail-fast on dead sites
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
The post-goto token poll ran a blind 20s wait regardless of whether page.goto succeeded or threw. On a healthy goto the widget API call fires within ~1-2s; on a failed goto the request either already went through (rare) or never will (common). The 20s cap was the dominant cost on failed-site attempts — verified tonight as a 28s "No token after ..." log on auto-komplekt after page.goto ERR_TIMED_OUT. * CAPTURE_POLL_AFTER_OK = 10 (5s) — token usually arrives in <2s * CAPTURE_POLL_AFTER_FAIL = 4 (2s) — brief grace then bail Per-attempt worst case on a dead site: 10s goto + 2s grace = 12s (was 10s + 20s = 30s). On a healthy site, well-known capture times (3-5s) stay comfortably inside the 5s post-goto cap.
This commit is contained in:
@@ -32,7 +32,14 @@ const REDIS_SAFETY_BUFFER_S = 60; // don't serve from Redis if <60s of life left
|
|||||||
const TOKEN_TTL = 600; // seconds — TWS- has no built-in expiry, refresh aggressively
|
const TOKEN_TTL = 600; // seconds — TWS- has no built-in expiry, refresh aggressively
|
||||||
const REFRESH_BUFFER = 90; // Refresh 90s before expiry
|
const REFRESH_BUFFER = 90; // Refresh 90s before expiry
|
||||||
const CAPTURE_POLL_INTERVAL = 500; // ms
|
const CAPTURE_POLL_INTERVAL = 500; // ms
|
||||||
const CAPTURE_POLL_MAX = 40; // 40 × 500ms = 20s max wait
|
// Two-armed post-goto poll: a healthy goto means the widget JS already loaded
|
||||||
|
// and the API call typically fires within ~1-2s — short cap is enough. A
|
||||||
|
// failed goto means we lost the page, but the widget request may still have
|
||||||
|
// gone through before the navigation timeout — give a brief grace window
|
||||||
|
// and bail. Old single-armed 20s cap was the dominant cost on failed-site
|
||||||
|
// attempts (e.g. 10s page timeout + 20s blind poll = 30s wasted).
|
||||||
|
const CAPTURE_POLL_AFTER_OK = 10; // 10 × 500ms = 5s after a healthy goto
|
||||||
|
const CAPTURE_POLL_AFTER_FAIL = 4; // 4 × 500ms = 2s grace after goto failure
|
||||||
const PAGE_TIMEOUT = 10_000; // 10s — healthy partner sites load in <5s through
|
const PAGE_TIMEOUT = 10_000; // 10s — healthy partner sites load in <5s through
|
||||||
// the proxy; a longer wait only prolongs dead-port connects in capture retries.
|
// the proxy; a longer wait only prolongs dead-port connects in capture retries.
|
||||||
const CONTEXT_CLOSE_TIMEOUT = 5_000;
|
const CONTEXT_CLOSE_TIMEOUT = 5_000;
|
||||||
@@ -634,18 +641,23 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Navigate — networkidle waits for widget JS to load + make API calls
|
// Navigate — networkidle waits for widget JS to load + make API calls
|
||||||
|
let gotoOk = true;
|
||||||
try {
|
try {
|
||||||
await page.goto(siteUrl, {
|
await page.goto(siteUrl, {
|
||||||
timeout: PAGE_TIMEOUT,
|
timeout: PAGE_TIMEOUT,
|
||||||
waitUntil: "networkidle",
|
waitUntil: "networkidle",
|
||||||
});
|
});
|
||||||
} catch (navErr) {
|
} catch (navErr) {
|
||||||
// Navigation may timeout but JWT could still be captured
|
// Navigation may timeout but JWT could still be captured if the
|
||||||
|
// widget request fired before goto gave up — see two-armed poll
|
||||||
|
// constants above.
|
||||||
|
gotoOk = false;
|
||||||
this.logger.debug(`Navigation ended: ${(navErr as Error).message?.slice(0, 80)}`);
|
this.logger.debug(`Navigation ended: ${(navErr as Error).message?.slice(0, 80)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll for token
|
// Poll for token — short cap after healthy goto, brief grace after fail
|
||||||
for (let i = 0; i < CAPTURE_POLL_MAX; i++) {
|
const pollMax = gotoOk ? CAPTURE_POLL_AFTER_OK : CAPTURE_POLL_AFTER_FAIL;
|
||||||
|
for (let i = 0; i < pollMax; i++) {
|
||||||
if (capturedToken) break;
|
if (capturedToken) break;
|
||||||
await new Promise((r) => setTimeout(r, CAPTURE_POLL_INTERVAL));
|
await new Promise((r) => setTimeout(r, CAPTURE_POLL_INTERVAL));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user