perf(api): pre-warm PL24 auth on boot to kill the post-deploy cold-start
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
PL24 auth tokens (account JWT + per-service tokens) are cached in-memory, so a (re)deploy clears them and the first VIN decode of each service-family pays the ~10s login + service-authorize handshake on the request path. Measured on dev: a cold opel_parts decode took 12s vs ~2s once the auth was warm. Add OnModuleInit to PL24AuthService that warms, in the background (fire-and-forget, never blocking boot), both base account logins (the shared dominant cost) plus the common legacy + top Turkish-market service tokens. allSettled throughout so a slow/down PL24 degrades gracefully; gated on credential presence; PL24_PREWARM=false disables. The pre-decode (search page) already hides this from users mid-session — this closes the one remaining gap: the first dealer right after a deploy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
* - 'de' (de-708171): DataImpulse Germany proxy, Fiat + EUR prices
|
||||
*/
|
||||
|
||||
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
||||
import { Injectable, Logger, type OnModuleInit, UnauthorizedException } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PL24_ENDPOINTS } from "./pl24.constants";
|
||||
import {
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from "./pl24.types";
|
||||
|
||||
@Injectable()
|
||||
export class PL24AuthService {
|
||||
export class PL24AuthService implements OnModuleInit {
|
||||
private readonly logger = new Logger(PL24AuthService.name);
|
||||
|
||||
// ── Account 1 (tr-903645) ──────────────────────────────────────────────────
|
||||
@@ -64,6 +64,61 @@ export class PL24AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
onModuleInit(): void {
|
||||
// In-memory auth tokens are cleared on every (re)deploy, so the first VIN
|
||||
// decode afterwards otherwise pays the ~10s PL24 login + service-authorize
|
||||
// handshake on the request path. Warm it in the background so users never do.
|
||||
// Fire-and-forget — never block or fail boot; set PL24_PREWARM=false to disable.
|
||||
if (process.env.PL24_PREWARM === "false") return;
|
||||
if (!this.companyCode || !this.username || !this.password) return;
|
||||
void this.prewarm().catch((err) =>
|
||||
this.logger.warn(`PL24 auth pre-warm error: ${(err as Error).message}`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-fill the in-memory auth caches: the base account logins (the shared,
|
||||
* dominant cost) plus the common legacy + top Turkish-market service tokens.
|
||||
* Idempotent — the underlying methods cache, so this just populates the cache.
|
||||
* Uses allSettled throughout: a slow/down PL24 degrades gracefully (logged).
|
||||
*/
|
||||
async prewarm(): Promise<void> {
|
||||
const t0 = Date.now();
|
||||
// 1) Base account JWT + session cookie. login() for "tr" (tokenData) and "de"
|
||||
// (tokenData2) are independent; the session cookie is warmed as a side effect.
|
||||
const baseResults = await Promise.allSettled([
|
||||
this.getAccessTokenForAccount("tr"),
|
||||
this.companyCode2 ? this.getAccessTokenForAccount("de") : Promise.resolve(""),
|
||||
]);
|
||||
const baseOk = baseResults.filter((r) => r.status === "fulfilled").length;
|
||||
|
||||
// 2) Common legacy (Ford-legacy JWT flow) + top Turkish P5 service tokens.
|
||||
// Only if the tr login succeeded, so we don't stampede concurrent logins.
|
||||
const services = [
|
||||
"opel_parts",
|
||||
"fordt_parts",
|
||||
"hyundai_parts",
|
||||
"nissan_parts",
|
||||
"volvo_parts",
|
||||
"vw_parts",
|
||||
"renault_parts",
|
||||
"toyota_parts",
|
||||
];
|
||||
let svcOk = 0;
|
||||
if (baseResults[0].status === "fulfilled") {
|
||||
const svcResults = await Promise.allSettled(
|
||||
services.map((svc) => this.authorizeServiceForAccount(svc, "tr")),
|
||||
);
|
||||
svcOk = svcResults.filter((r) => r.status === "fulfilled").length;
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`PL24 auth pre-warm done in ${Date.now() - t0}ms (logins ${baseOk}/2, services ${svcOk}/${services.length}${
|
||||
baseResults[0].status === "fulfilled" ? "" : " — services skipped: tr login failed"
|
||||
})`,
|
||||
);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// ── Public: per-account API ──────────────────────────────────────────────
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user