fix(db): index NOCASE municipalité + index partiel ventes récentes — panne 2026-08-25
municipalityCenter/municipalityPercentile faisaient un SCAN des 3,7 M d'unités à chaque page d'estimation (COLLATE NOCASE ≠ idx_units_mun BINARY) ; recentSales scannait 750 k transactions avec tri temporaire. Sous trafic crawlers, better-sqlite3 (synchrone) gelait l'event loop : next-server à 100 % CPU pendant 10 h, port 8090 muet, site down. Index créés sur la base live + bootstrap CREATE INDEX IF NOT EXISTS dans getDb() pour survivre à une reconstruction de la base. Requêtes vérifiées : covering index, pages < 0,32 s sous 20 req parallèles (avant : plusieurs secondes chacune, empilement infini). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 changed file +12 −0
modified
src/lib/db.ts
+12 −0
@@ -10,6 +10,18 @@ export function getDb(): Database.Database { | ||
| 10 | 10 | process.env.VRAIPRIX_DB ?? path.join(process.cwd(), "data", "vraiprix.db"); |
| 11 | 11 | db = new Database(p, { fileMustExist: true }); |
| 12 | 12 | db.pragma("journal_mode = WAL"); |
| 13 | + // Index requis par les requêtes ci-dessous (COLLATE NOCASE : l'index BINARY | |
| 14 | + // idx_units_mun est inutilisable → scan de 3,7 M de lignes qui gelait | |
| 15 | + // l'event loop sous trafic, panne du 2026-08-25). No-op si déjà présents. | |
| 16 | + db.exec(` | |
| 17 | + CREATE INDEX IF NOT EXISTS idx_units_mun_nocase_geo | |
| 18 | + ON units(municipalite COLLATE NOCASE, lat, lng); | |
| 19 | + CREATE INDEX IF NOT EXISTS idx_units_mun_nocase_est | |
| 20 | + ON units(municipalite COLLATE NOCASE, est_2026); | |
| 21 | + CREATE INDEX IF NOT EXISTS idx_tx_recent | |
| 22 | + ON transactions(date DESC, amount DESC) | |
| 23 | + WHERE id_provinc IS NOT NULL AND street IS NOT NULL AND amount >= 50000; | |
| 24 | + `); | |
| 13 | 25 | } |
| 14 | 26 | return db; |
| 15 | 27 | } |
| 16 | 28 | |