From 283ba6b1083f67a9bf3f10733d312fc11f7726c5 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:26:39 -0600 Subject: [PATCH] fix: Remove VIN Cache --- .../vehicles/api/vehicles.controller.ts | 11 --- .../vehicles/domain/vehicles.service.ts | 68 +------------------ .../vehicles/domain/vehicles.types.ts | 12 ---- .../integration/vehicles.integration.test.ts | 4 +- 4 files changed, 2 insertions(+), 93 deletions(-) diff --git a/backend/src/features/vehicles/api/vehicles.controller.ts b/backend/src/features/vehicles/api/vehicles.controller.ts index 79db275..a9e7f33 100644 --- a/backend/src/features/vehicles/api/vehicles.controller.ts +++ b/backend/src/features/vehicles/api/vehicles.controller.ts @@ -406,20 +406,9 @@ export class VehiclesController { logger.info('VIN decode requested', { userId, vin: sanitizedVin.substring(0, 6) + '...' }); - // Check cache first - const cached = await this.vehiclesService.getVinCached(sanitizedVin); - if (cached) { - logger.info('VIN decode cache hit', { userId }); - const decodedData = await this.vehiclesService.mapVinDecodeResponse(cached); - return reply.code(200).send(decodedData); - } - // Call OCR service for VIN decode const response = await ocrClient.decodeVin(sanitizedVin); - // Cache the response - await this.vehiclesService.saveVinCache(sanitizedVin, response); - // Map response to decoded vehicle data with dropdown matching const decodedData = await this.vehiclesService.mapVinDecodeResponse(response); diff --git a/backend/src/features/vehicles/domain/vehicles.service.ts b/backend/src/features/vehicles/domain/vehicles.service.ts index cbce71a..f9f78c7 100644 --- a/backend/src/features/vehicles/domain/vehicles.service.ts +++ b/backend/src/features/vehicles/domain/vehicles.service.ts @@ -1,6 +1,6 @@ /** * @ai-summary Business logic for vehicles feature - * @ai-context Handles VIN decoding, caching, and business rules + * @ai-context Handles VIN decoding and business rules */ import { Pool } from 'pg'; @@ -594,72 +594,6 @@ export class VehiclesService { await cacheService.del(cacheKey); } - /** - * Check vin_cache for existing VIN data. - * Format-aware: validates raw_data has `success` field (Gemini format). - * Old NHTSA-format entries are treated as cache misses and expire via TTL. - */ - async getVinCached(vin: string): Promise { - try { - const result = await this.pool.query<{ - raw_data: any; - cached_at: Date; - }>( - `SELECT raw_data, cached_at - FROM vin_cache - WHERE vin = $1 - AND cached_at > NOW() - INTERVAL '365 days'`, - [vin] - ); - - if (result.rows.length === 0) { - return null; - } - - const rawData = result.rows[0].raw_data; - - // Format-aware check: Gemini responses have `success` field, - // old NHTSA responses do not. Treat old format as cache miss. - if (!rawData || typeof rawData !== 'object' || !('success' in rawData)) { - logger.debug('VIN cache format mismatch (legacy NHTSA entry), treating as miss', { vin }); - return null; - } - - logger.debug('VIN cache hit', { vin }); - return rawData as VinDecodeResponse; - } catch (error) { - logger.error('Failed to check VIN cache', { vin, error }); - return null; - } - } - - /** - * Save VIN decode response to cache with ON CONFLICT upsert. - */ - async saveVinCache(vin: string, response: VinDecodeResponse): Promise { - try { - await this.pool.query( - `INSERT INTO vin_cache (vin, make, model, year, engine_type, body_type, raw_data, cached_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) - ON CONFLICT (vin) DO UPDATE SET - make = EXCLUDED.make, - model = EXCLUDED.model, - year = EXCLUDED.year, - engine_type = EXCLUDED.engine_type, - body_type = EXCLUDED.body_type, - raw_data = EXCLUDED.raw_data, - cached_at = NOW() - WHERE (vin_cache.raw_data->>'confidence')::float <= $8`, - [vin, response.make, response.model, response.year, response.engine, response.bodyType, JSON.stringify(response), response.confidence ?? 1] - ); - - logger.debug('VIN cached', { vin, confidence: response.confidence }); - } catch (error) { - logger.error('Failed to cache VIN data', { vin, error }); - // Don't throw - caching failure shouldn't break the decode flow - } - } - async getDropdownMakes(year: number): Promise { const vehicleDataService = getVehicleDataService(); const pool = getPool(); diff --git a/backend/src/features/vehicles/domain/vehicles.types.ts b/backend/src/features/vehicles/domain/vehicles.types.ts index 94a69a3..2686287 100644 --- a/backend/src/features/vehicles/domain/vehicles.types.ts +++ b/backend/src/features/vehicles/domain/vehicles.types.ts @@ -242,18 +242,6 @@ export interface DecodedVehicleData { transmission: MatchedField; } -/** Cached VIN data from vin_cache table */ -export interface VinCacheEntry { - vin: string; - make: string | null; - model: string | null; - year: number | null; - engineType: string | null; - bodyType: string | null; - rawData: import('../../ocr/domain/ocr.types').VinDecodeResponse; - cachedAt: Date; -} - /** VIN decode request body */ export interface DecodeVinRequest { vin: string; diff --git a/backend/src/features/vehicles/tests/integration/vehicles.integration.test.ts b/backend/src/features/vehicles/tests/integration/vehicles.integration.test.ts index ea9062b..900280c 100644 --- a/backend/src/features/vehicles/tests/integration/vehicles.integration.test.ts +++ b/backend/src/features/vehicles/tests/integration/vehicles.integration.test.ts @@ -36,15 +36,13 @@ describe('Vehicles Integration Tests', () => { afterAll(async () => { // Clean up test database await pool.query('DROP TABLE IF EXISTS vehicles CASCADE'); - await pool.query('DROP TABLE IF EXISTS vin_cache CASCADE'); await pool.query('DROP FUNCTION IF EXISTS update_updated_at_column() CASCADE'); await pool.end(); }); beforeEach(async () => { - // Clean up test data before each test - more thorough cleanup + // Clean up test data before each test await pool.query('DELETE FROM vehicles WHERE user_id = $1', ['test-user-123']); - await pool.query('DELETE FROM vin_cache WHERE vin LIKE $1', ['1HGBH41JXMN%']); // Clear Redis cache for the test user try {