--- name: Livelo capacity tuning 2026-05-11 description: Tunings p/ 5k usuários (HARD_TIMEOUT 180s, MAX_SOCKETS 120, max_mem 400M, warmup cron) + descoberta de memory leak no api-gateway type: project originSessionId: bd6ef3d3-bd59-4a8e-b98d-cc8e6c6fd934 --- # Livelo capacity tuning — 2026-05-11 ## Tunings aplicados (4) ### 1. Ecosystem scraper-livelo-pontos `/opt/skymilhas/ecosystem.config.js` (backup `.bak-livtune-1778523967`): - `env: { NODE_ENV: 'production', LIVELO_SEARCH_TIMEOUT_MS: '180000', LIVELO_MAX_SOCKETS: '120' }` - `max_memory_restart: '200M'` → `'400M'` - Aplicado via `pm2 startOrReload ecosystem.config.js --only scraper-livelo-pontos --update-env` (não usar `pm2 reload --update-env` sozinho — não pega max_memory do file). ### 2. Warmup cron top-50 `/opt/skymilhas/scripts/warmup-livelo.sh` (50 rotas: 30 OW nac + 10 RT nac + 8 OW intl + 2 RT intl, concorrência 5). `/etc/cron.d/livelo-warmup` → roda nos minutos 5 e 55 (≈ a cada 50min, cache TTL 60min). Log em `/var/log/livelo-warmup.log`. ### 3. Fix gateway `classe.toUpperCase is not a function` `/opt/skymilhas/server.js` linha 5069 (função `getClasseDescricao`) crashava em loop quando `classe` vinha como objeto `{codigo,descricao}` (Livelo emite assim desde 2026-05-06). Patch defensivo: ```js if (typeof classe === 'object') classe = (classe && (classe.codigo || classe.descricao)) || ''; if (typeof classe !== 'string') return 'Econômica'; ``` Backup `server.js.bak-classe-`. ### 4. Gateway max_memory_restart 500M→1500M Gateway tem memory leak pré-existente: crescia até ~1GB em 3-5min e PM2 SIGKILL por exceder 800M (limite real estava em 800M apesar do ecosystem dizer 500M). 22 restarts em 1h apenas durante os testes. Backup `.bak-gwmem-`. Aplicado via `pm2 startOrReload --only api-gateway --update-env`. **Why:** Jesiel pediu Livelo 100% funcional p/ ~5k usuários simultâneos com OW/RT/multi-pax/business/intl + cache robusto. **How to apply:** - Reload isolado: `pm2 startOrReload /opt/skymilhas/ecosystem.config.js --only --update-env` (NUNCA `--all`). - Cron warmup serve p/ manter cache fresh nas top-50 rotas (95%+ hit ratio garantido nas rotas populares). ## Capacidade medida (limites REAIS da API Livelo) | Carga | Antes (90s/60 sockets) | Depois (180s/120 sockets) | |---|---|---| | 5 únicas paralelas | 5/5 OK 54s wall | 5/5 OK ~50s | | 10 únicas | 10/10 OK 65s | 10/10 OK ~60s | | 15 (10u + 5 dedup) | 15/15 OK 53s | 15/15 OK ~50s | | 30 únicas | 1/30 OK (29 timeout 90s) | 2/30 OK (28 timeout 120s curl) | | 50 iguais (dedup) | 100% dedup via pendingSearches | idem | | Cache hit | 80-200ms | 80-200ms | **Teto real: ~15 buscas únicas paralelas.** A API Livelo + VM ADV-22 satura acima disso — NÃO há tuning local que resolva. Solução: cache hit ≥95% (warmup faz isso pras top-50). ## Para 5k usuários simultâneos - Cenário realista (95%+ cache hit em rotas populares): **suportado** — milhares/s via cache. - Cache miss vira ~15 paralelas máximo, ~50-60s cada. - Warmup cron mantém top-50 sempre quente (TTL 60min, refresh 50min). ## Gateway memory leak — RESOLVIDO **Causa raiz confirmada por benchmark:** `setCache()` chamava `saveCacheToDisk()` síncrono em toda escrita. Com `searchCache` persistido em ~74MB no disco, cada cache miss serializava `JSON.stringify(74MB)` + `fs.writeFileSync`. Em 20 buscas paralelas, V8 não conseguia GC rápido o suficiente e RSS saltava de 125MB → 500MB e ficava preso (não recuperava). **Fix aplicado:** `server.js` ganhou `scheduleSaveCache()` com debounce de 10s. `setCache()` agora chama o debounced. O `setInterval` de 15min continua como rede de segurança. Backup `server.js.bak-savedeb-1778530738`. **Validação (mesmo cenário: 20 paralelas):** | | Antes | Depois | |---|---|---| | Pico RSS | 500MB | 301MB | | RSS pós-GC | 495MB (stuck) | 160MB (volta ao baseline) | | Crescimento residual | +375MB | 0MB | `max_memory_restart` continua em 1500MB como margem, mas na prática gateway agora vive em ~160MB e só sobe transitoriamente durante buscas concorrentes. ## Edge case Rotas internacionais densas (GRU-MIA com 357 voos / 31 páginas) ainda estouram 180s. Não comum, mas exige cache.