WDO MySQL - Troubleshooting¶
Guida alla diagnostica per gli errori più frequenti del driver MySQL.
Mappa sintomo → soluzione¶
| Sintomo | Vai a |
|---|---|
Errore 10048 / WSAEADDRINUSE all'avvio |
§1 |
| La DLL non si carica / "not found" in console | §2 |
WDO_Get() restituisce NIL sotto carico |
§3 |
| "MySQL server has gone away" | §4 |
| "Too many connections" in MySQL | §5 |
| L'app si blocca all'avvio senza messaggi | §6 |
WARN RELEASE_RECLAIM nel log |
§7 |
1. Errore 10048 WSAEADDRINUSE¶
Causa: Stai usando WDO_MySql():New() all'interno di un handler HTTP (o fuori dal pool). Ogni richiesta apre un socket che rimane in TIME_WAIT ~60 s. Dopo ~10.000 richieste rapide, Windows esaurisce le porte efimere (intervallo 49152–65535).
Soluzione: Usa sempre WDO_Get("mysql") negli handler. Mai New() all'interno di un controller.
New() negli handler - avviso nel log
Il driver rileva questo pattern e scrive nel log:
Se vedi questo avviso, individua l'handler che lo genera e modificalo per usare il pool.2. DLL non trovata¶
Cause possibili:
- La DLL non si trova in nessuno dei percorsi cercati.
- Il campo
"dll"inconfig.jsonpunta a un percorso errato. - Stai usando
"driver": "mysql"ma hai sololibmariadb64.dll(o viceversa).
Soluzioni:
Per vedere quale DLL sta usando il driver in fase di esecuzione:
oSrv:AddRouteGet( "dbinfo", "/dbinfo", {||
LOCAL oConn := WDO_Get( "mysql" )
IF oConn == NIL ; RETURN USendError( 503, "no pool" ) ; ENDIF
USendJson( { "dll_path" => oConn:DllPath(), "dll_source" => oConn:DllSource() } )
oConn:Close()
} )
3. WDO_Get restituisce NIL sotto carico¶
Causa: Tutti gli slot del pool sono occupati. Gli handler impiegano più del previsto e gli slot non vengono liberati in tempo.
Diagnostica:
// Aggiungi questo endpoint per vedere lo stato in tempo reale
oSrv:AddRouteGet( "health.db", "/health/db", {||
USendJson( WDO_PoolStats( "mysql" ) )
// { "size" => 5, "busy" => 5, "free" => 0, "closed" => false }
} )
Soluzioni (in ordine):
- Aumentare
pool_sizeinconfig.json(inizia raddoppiando). - Verificare che tutti gli handler chiamino
oConn:Close()- un solo handler che perde una connessione sotto carico riempie il pool. - Rivedere la latenza delle query - se le query lente occupano gli slot troppo a lungo, ottimizzare gli indici o aggiungere più slot.
- Aumentare
timeout_msse i picchi sono brevi e il pool si libera presto.
4. MySQL server has gone away¶
Causa: La connessione TCP del pool era inattiva più a lungo del wait_timeout di MySQL (di default 8 ore, a volte 30 minuti in hosting condiviso). MySQL ha chiuso la connessione dal suo lato, ma lo slot del pool non lo sa.
Soluzione: Attiva "ping": true in config.json. Prima di consegnare ogni slot, il pool esegue un ping e riconnette se il socket è inattivo:
Costo del ping
Il ping aggiunge ~0.5 ms per WDO_Get(). In pratica, irrilevante rispetto al costo di qualsiasi query reale. Consigliato sempre in produzione con MySQL remoto.
Se il problema persiste, aumenta wait_timeout in MySQL (my.cnf):
5. Too many connections¶
Causa: Il numero di connessioni attive supera max_connections di MySQL (default 151).
Diagnostica:
Soluzioni:
- Ridurre
pool_sizeinconfig.json- non dovresti avere più connessioni WDO di quelle che MySQL può accettare. - Aumentare
max_connectionsinmy.cnf(con cautela: ogni connessione consuma ~1 MB di RAM): - Verificare che non ci siano pool duplicati - se più istanze dell'app puntano allo stesso MySQL, il totale delle connessioni è la somma di tutti i pool.
Regola d'oro: MySQL.max_connections > HIX_workers ≥ WDO_pool_size + 20 (margine admin).
6. App si blocca all'avvio¶
L'app si avvia, non ci sono messaggi di errore, ma non risponde.
Causa più frequente: La DLL si carica ma mysql_real_connect rimane in attesa - l'host non risponde (firewall, MySQL spento, hostname errato).
Diagnostica:
localhost vs 127.0.0.1 su Linux
Su Linux, mysql_real_connect("localhost") usa il socket Unix (/var/run/mysqld/mysqld.sock) invece di TCP. Se il socket non esiste, la connessione si blocca. WDO rimappa automaticamente "localhost" in "127.0.0.1" per forzare TCP, ma se hai problemi su Linux verifica che mysqld sia in ascolto su TCP (bind-address = 0.0.0.0).
Altre cause:
lAbortOnFail = .T.inHIX_InitPoolsFromConfig→ il processo attendeInkey(0). Se viene avviato come servizio senza console, si blocca in attesa di un tasto. Controlla il log di sistema (journalctl -u hixsu Linux).- Timeout di rete molto alto - prova a ridurre
timeout_msa 3000 per fallire rapidamente durante la diagnostica.
7. WARN RELEASE_RECLAIM¶
Causa: Un handler ha acquisito una connessione con WDO_Get() ma non ha chiamato oConn:Close() prima di terminare. Il dispatcher di HIX rileva il leak al termine della richiesta e libera lo slot automaticamente.
Gravità: Non è un errore critico - lo slot viene recuperato. Ma il thread ha tenuto la connessione più a lungo del necessario, il che riduce la disponibilità del pool sotto carico.
Soluzione: Individua l'handler che non chiude la connessione e aggiungi oConn:Close(), idealmente in un blocco FINALLY:
Messaggi del logger (riferimento rapido)¶
| Messaggio | Livello | Significato |
|---|---|---|
WDO_LOG_POOL_INIT_FAIL |
WARN | Uno slot del pool non è riuscito ad aprirsi all'avvio |
WDO_LOG_ACQUIRE_TIMEOUT |
WARN | WDO_Get() ha atteso timeout_ms senza ottenere uno slot |
WDO_LOG_MYSQL_POOL_INIT_FAIL |
WARN | Nessuno slot del pool si è aperto; pool non disponibile |
WDO_ERR_LIB_NOT_FOUND |
ERROR | La DLL non esiste nel percorso cercato |
WDO_ERR_LIB_LOAD_FAIL |
ERROR | La DLL esiste ma hb_LibLoad non è riuscito a caricarla |
WDO_WARN_NEW_IN_REQUEST |
WARN | New() chiamato all'interno di un handler HTTP |
WDO_WARN_BERROR_NOT_FOUND |
WARN | La funzione del campo berror non è collegata |
WDO_LOG_POOLS_INIT |
INFO | N/M pools inizializzati correttamente |
WDO_LOG_POOLS_END |
INFO | Pool chiusi allo spegnimento del server |