# Fix Scraping Bloccato

## Problema Critico Risolto

Lo scraping si bloccava al 74% con:
- Node.js al 100% CPU
- Browser Chromium aperto ma bloccato su una pagina
- Job che non terminava mai

## Cause Identificate

1. **Timeout non rispettati**: `page.goto()` si bloccava su siti lenti ignorando il timeout
2. **Nessun circuit breaker**: Continuava a ritentare siti problematici
3. **Pagine non chiuse**: Memory leak con pagine aperte che si accumulavano
4. **Error propagation**: Un errore bloccava l'intero job
5. **waitUntil networkidle2**: Troppo lento, aspettava 500ms senza traffico

## Fix Implementati

### 1. Timeout Multi-Livello

**Prima**: Singolo timeout ignorabile
```javascript
await page.goto(url, {
  waitUntil: 'networkidle2',
  timeout: 15000
});
```

**Dopo**: Triplo timeout
```javascript
// 1. Timeout a livello pagina
page.setDefaultTimeout(timeout);
page.setDefaultNavigationTimeout(timeout);

// 2. Timeout di navigazione più veloce
const navigationPromise = page.goto(url, {
  waitUntil: 'domcontentloaded', // Invece di networkidle2
  timeout
});

// 3. Timeout manuale esterno
const timeoutPromise = new Promise((_, reject) => 
  setTimeout(() => reject(new Error('Navigation timeout')), timeout)
);

await Promise.race([navigationPromise, timeoutPromise]);
```

### 2. Circuit Breaker per Siti Problematici

```javascript
constructor() {
  this.failedSites = new Map(); // domain -> { failures, lastAttempt }
  this.maxFailures = 2;
}

shouldSkipSite(url) {
  const domain = new URL(url).hostname;
  const siteInfo = this.failedSites.get(domain);
  
  // Skippa se ha troppi fallimenti recenti (<1h)
  if (siteInfo?.failures >= this.maxFailures) {
    const oneHourAgo = Date.now() - (60 * 60 * 1000);
    return siteInfo.lastAttempt > oneHourAgo;
  }
  return false;
}
```

**Effetto**: Dopo 2 fallimenti su un dominio, viene skippato per 1 ora

### 3. Chiusura Pagine Automatica

```javascript
// Chiudi pagina dopo uso
await page.close().catch(() => {});

// In browserPool.release()
const pages = await browser.pages();
await Promise.all(pages.map(page => page.close()));
```

**Effetto**: Zero memory leak, pagine sempre pulite

### 4. Error Handling Non-Bloccante

**Prima**: Error propagato
```javascript
catch (error) {
  logger.error('Error:', error);
  throw error; // ❌ Blocca il job
}
```

**Dopo**: Ritorna dati vuoti
```javascript
catch (error) {
  logger.error('Error:', error.message);
  return { emails: [], company: '', website: url, phones: [] }; // ✅ Continua
}
```

### 5. Browser Pool Resiliente

```javascript
// Se browser è corrotto durante release
catch (error) {
  // Non rimetterlo nel pool
  this.browsers.splice(index, 1);
  
  // Sostituiscilo con uno nuovo
  const newBrowser = await puppeteer.launch({...});
  this.browsers.push(newBrowser);
  this.available.push(newBrowser);
}
```

### 6. Outer Timeout per Homepage e Contact Pages

```javascript
// Timeout wrapper di 20s oltre al timeout interno di 15s
const homeData = await Promise.race([
  this.extractEmailsFromUrl(result.url),
  new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Outer timeout')), 20000)
  )
]);
```

## Configurazione Timeout Ottimizzata

| Operazione | Timeout | Motivo |
|------------|---------|---------|
| Page navigation | 15s | Massimo accettabile per siti lenti |
| Page selector wait | 3s | Se non c'è subito, skip |
| Contact page | 10s | Pagine più semplici |
| Outer wrapper homepage | 20s | Safety net |
| Outer wrapper contacts | 12s | Safety net |

## Test Rapido

```bash
# 1. Riavvia backend
pm2 restart gix-demtools-backend

# 2. Verifica che sia partito
pm2 logs gix-demtools-backend --lines 10

# 3. Dall'app, lancia nuovo scraping con:
Query: "agenzia commerciale milano"
Num results: 5

# 4. Monitora
pm2 logs gix-demtools-backend --lines 0 -f

# Dovresti vedere:
# - "Processing site 1/5"
# - "Found X emails on homepage"
# - Se un sito si blocca, dopo 15-20s continua al prossimo
# - "Skipping X - circuit breaker open" se un dominio fallisce 2 volte
```

## Comportamento Atteso

### Prima
- Job bloccato al 74%
- CPU al 100%
- Timeout ignorati
- Manual kill richiesto

### Dopo
- Job progredisce sempre
- Se un sito si blocca → timeout dopo 15-20s → continua
- Se un dominio è problematico → skippato automaticamente
- CPU stabile 30-50%
- Completa in 2-3 minuti per 10 risultati

## Monitoraggio

```bash
# 1. CPU e memoria
htop

# 2. Processi Chrome (dovrebbero essere max 2-3)
ps aux | grep chrome | wc -l

# 3. Log real-time
pm2 logs gix-demtools-backend -f

# 4. Errori recenti
pm2 logs gix-demtools-backend --err --lines 50
```

## Se Si Blocca Ancora

### Opzione 1: Timeout più aggressivo
```bash
# In .env
SCRAPING_TIMEOUT_MS=10000  # Da 15s a 10s
```

### Opzione 2: Circuit breaker più severo
```javascript
// In scraper.service.js constructor
this.maxFailures = 1; // Da 2 a 1 - skippa dopo primo fallimento
```

### Opzione 3: Kill job manualmente dalla UI
L'app ora dovrebbe avere il bottone per stoppare job in corso.

### Opzione 4: ForceKill da terminale
```bash
# Killa tutti i browser
pkill -9 chrome chromium

# Restart
pm2 restart gix-demtools-backend
```

## Limiti Accettabili

Per evitare problemi, consiglio:
- **Max 10-15 risultati** per query (non 30+)
- **Query specifiche** (es. "agenzia milano centro" invece di solo "agenzia")
- **Non lanciare più job contemporanei**

## File Modificati

- ✅ [services/scraper.service.js](src/services/scraper.service.js)
  - Circuit breaker
  - Multi-level timeout
  - Error handling non-bloccante
  - Page close automatico
  
- ✅ [services/browserPool.service.js](src/services/browserPool.service.js)
  - Release resiliente con error recovery
  - Page cleanup automatico

## Note

⚠️ Se vedi ancora blocchi dopo queste fix, significa che il problema è:
1. **Server troppo lento** (upgrade RAM/CPU)
2. **Siti target troppo pesanti** (usa query più specifiche)
3. **Chromium non adatto** (considera fallback ad axios per siti statici)
