Saltar a contenido

WDO MySQL — Configuración

Hay tres formas de arrancar el pool MySQL. La recomendada es la declarativa desde config.json.

Método 1 — Declarativo (recomendado)

Añade la sección databases en www/config.json:

{
  "databases": {
    "mysql": {
      "driver":     "mysql",
      "host":       "127.0.0.1",
      "user":       "harbour",
      "pwd":        "hb1234",
      "db":         "employees",
      "port":       3306,
      "pool_size":         5,
      "timeout_ms":        5000,
      "ping":              true,
      "read_timeout_s":    30,
      "connect_timeout_s": 10,
      "berror":            "WDO_DefaultErrorHandler"
    }
  }
}

En app.prg con hixstyle no necesitas nada más: Start() llama automáticamente a HIX_InitPoolsFromConfig(.T.) y HIX_EndPoolsFromConfig().

En apps sin hixstyle:

PROCEDURE Main()
   LOCAL oSrv := THixServer():New()
   HIX_ConfigAppLoad( "www/config.json" )
   HIX_InitPoolsFromConfig()   // aborta si algún pool falla (default)
   oSrv:Start()
   IF oSrv:hThread != NIL
      hb_threadJoin( oSrv:hThread )
   ENDIF
   HIX_EndPoolsFromConfig()
RETURN

Múltiples pools

Puedes declarar varias bases de datos con claves distintas:

{
  "databases": {
    "mysql":     { "driver": "mysql",   "host": "127.0.0.1", ... },
    "analytics": { "driver": "mariadb", "host": "10.0.0.42", ... }
  }
}

En los handlers: WDO_Get("mysql") y WDO_Get("analytics") coexisten en el mismo proceso.

Campos disponibles

Campo Default Descripción
driver (obligatorio) "mysql" o "mariadb" (case-insensitive)
host "localhost" IP o hostname del servidor
user "" Usuario de autenticación
pwd "" Contraseña
db "" Base de datos por defecto
port 3306 Puerto TCP
pool_size 5 Número de conexiones al arrancar
timeout_ms 5000 Tiempo máximo de espera en WDO_Get() (0 = infinito)
ping true Ping antes de entregar cada slot; reconecta si está muerto
read_timeout_s 30 Timeout de lectura/escritura del socket MySQL en segundos (0 = sin timeout). Acota el bloqueo en recv() cuando un hilo hijo del dispatcher excede exec_timeout_ms; sin esto la conexión queda zombi y el slot nunca vuelve al pool
connect_timeout_s 10 Timeout de la fase de conexión al servidor en segundos (0 = sin timeout). Evita que el arranque del pool quede colgado si el servidor MySQL no responde
debug false Si cualquier pool tiene "debug": true, se activa HIX_Dbg() globalmente: dbg.log captura trazas de Acquire/Release del pool y toda llamada HIX_Dbg() instrumentada en controllers. dbg.log se vacía al arrancar. Usar solo en desarrollo — tiene coste de I/O.
berror — Nombre de función Harbour para manejar errores del pool
dll — Ruta explícita a la DLL (override de la resolución automática)

Relación con exec_timeout_ms

read_timeout_s debe ser mayor que la query legítima más lenta y acorde con el exec_timeout_ms del dispatcher (en hix.json). Si subes exec_timeout_ms, sube también read_timeout_s: si una query tarda más que el socket read timeout MySQL retornará error antes de que la query termine.

berror por defecto

"berror": "WDO_DefaultErrorHandler" ya viene incluido en la lib: loguea el error con le() y devuelve 500 si hay request activo. Suficiente para la mayoría de proyectos.

Método 2 — Programático hash

WDO_InitPoolMySqlEx( "mysql", { ;
   "host"       => hb_GetEnv( "DB_HOST" ), ;
   "user"       => hb_GetEnv( "DB_USER" ), ;
   "pwd"        => hb_GetEnv( "DB_PWD" ),  ;
   "db"         => "employees",            ;
   "pool_size"  => 10,                     ;
   "timeout_ms" => 5000,                   ;
   "ping"       => .T.,                    ;
   "berror"     => "WDO_DefaultErrorHandler" ;
} )

Útil cuando las credenciales se leen de variables de entorno o secrets.

Método 3 — Posicional (legacy)

WDO_InitPoolMySql( "localhost", "harbour", "hb1234", "employees", 3306, ;
                    5,     /* pool_size   */ ;
                    5000,  /* timeout_ms  */ ;
                    .T.,   /* ping        */ ;
                    {|oErr, oConn| WdoErrorHandler( oErr, oConn ) } )

Registra el pool bajo la clave fija "MYSQL". Compatible con código anterior a v2.3.01.


DLLs — driver MySQL vs MariaDB

El driver necesita la librería cliente del protocolo MySQL:

"driver" DLL Windows SO Linux
"mysql" libmysql64.dll libmysqlclient.so
"mariadb" libmariadb64.dll libmariadb.so

Precedencia de resolución de la DLL

El driver busca la librería en este orden (mayor prioridad primero):

  1. Campo "dll" en config.json — ruta absoluta explícita por pool.
  2. Variable de entorno WDO_LIB_MYSQL — ruta absoluta al fichero.
  3. Variable de entorno WDO_PATH_MYSQL — directorio; el driver añade el nombre canónico.
  4. Directorio del ejecutable (hb_DirBase()).
  5. PATH del sistema — nombre canónico sin ruta.

Diagnóstico

oSrv:AddRouteGet( "dbinfo", "/dbinfo", {||
   LOCAL oConn := WDO_Get( "mysql" )
   IF oConn == NIL ; RETURN USendError( 503, "pool unavailable" ) ; ENDIF
   USendJson( { ;
      "dll_source" => oConn:DllSource(), ;
      "dll_path"   => oConn:DllPath(),   ;
      "server"     => oConn:mysql_get_server_info() ;
   } )
   oConn:Close()
} )

DllSource() retorna "override", "WDO_LIB_MYSQL", "WDO_PATH_MYSQL", "exedir" o "default".

Si la DLL no se encuentra

El pool falla al arrancar y escribe en consola:

==> Error: Cannot load MySQL DLL

Con HIX_InitPoolsFromConfig(.T.) el servidor aborta. Con .F. continúa, pero WDO_Get("mysql") retornará NIL y los handlers responderán 503.


Handler de error personalizado

Si necesitas lógica custom (alertas Slack, métricas, etc.), define una función pública:

FUNCTION WdoErrorHandler( oErr, oConn )

   HB_SYMBOL_UNUSED( oConn )

   le( "[WDO] " + oErr:description + " @ " + oErr:operation )

   IF HIX_GetRequest() != NIL
      USendError( 500, "DB error: " + oErr:description )
   ENDIF

RETURN NIL

Y referénciala en el JSON:

"berror": "WdoErrorHandler"

Requisitos de la función

Debe ser una FUNCTION pública (no STATIC, no PROCEDURE) y estar estáticamente enlazada al ejecutable. Si vive en un .hrb dinámico, hb_isFunction no la encontrará al arrancar.