Files
sase.tr/apps/api/src/vehicles/vin-cache-keys.ts
Semih Yesilyurt c2dfa5cd2e fix(decode): recognize VXK WMI as Opel (PSA/Stellantis-platform Corsa F, Mokka B)
Customer entered VXKUPHNKSP4032040 (2023 Corsa F) and got a bare
'Marka desteklenmiyor' — VXK was missing from all three WMI maps, so
identifyBasic could not even name the brand. PL24's opel_parts catalog
ends at CORSA-E (2015-2019, verified via the model-picker page), so the
catalog itself stays a gap, but the user now gets the honest
'Opel 2023 olarak tanındı, katalog henüz mevcut değil' message and the
miss is logged for the coverage backlog.

- WMI_BRAND_MAP + PL24_WMI_SERVICE_MAP + EMEX CATALOG_MAP: VXK -> Opel
- DECODE_CHAIN_VERSION 2 -> 3 (brand-mapping change; invalidates the
  stale negative cache for the customer's VIN)
- vin-cache-keys.spec: lock-key assertion checked the bare version char,
  which now appears inside the test VIN — assert the ✌️ segment instead

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:36:51 +03:00

43 lines
1.7 KiB
TypeScript

/**
* Redis key builders for the VIN resolve cache, namespaced by decode-chain version.
*
* Bump DECODE_CHAIN_VERSION whenever a change to the decode chain (sources,
* routing, dispatch, gating, brand mapping) could turn a previously-cached
* "unknown" into a hit. Bumping instantly invalidates every stale positive and
* negative entry on deploy: without it, the negative cache keeps serving the old
* miss for its full TTL and a real fix looks like it "didn't work" until the
* cache ages out. This was the single biggest source of phantom-undecoded VINs
* (see undecoded-vin-rca.md §2/§6 — ~17 of 124 historical misses already
* decoded but were masked by a stale 6h negative cache).
*
* Both the decode path (VehiclesService) and the admin cache-buster
* (InternalVehiclesService) MUST build keys through here so they stay in sync.
*/
export const DECODE_CHAIN_VERSION = "3";
export function vinResolveCacheKeys(vin: string): {
cacheKey: string;
negKey: string;
lockKey: string;
} {
const v = DECODE_CHAIN_VERSION;
return {
cacheKey: `vin:resolve:${v}:${vin}`,
negKey: `vin:resolve:neg:${v}:${vin}`,
// The in-flight lock is transient (60s) and version-independent — keep it
// un-namespaced so concurrent decoders still dedupe across a deploy boundary.
lockKey: `vin:lock:${vin}`,
};
}
/**
* Server-side stash for a multi-candidate decode: maps the opaque candidate
* keys returned to the client back to the provider-specific selection
* (parts-catalogs car id / EMEX list index). Exists so the client never has to
* carry decode-source details between the decode and the candidate-pick
* requests.
*/
export function vinCandidateStashKey(vin: string): string {
return `vin:candidates:${DECODE_CHAIN_VERSION}:${vin}`;
}