mirror of
https://github.com/KeymonSoft/jRDC-Multi.git
synced 2026-04-19 13:49:16 +00:00
- VERSION 5.09.14
-feat: Implementación robusta de monitoreo de pool de conexiones y peticiones activas -Este commit resuelve problemas críticos en el monitoreo del pool de conexiones (C3P0) y el conteo de peticiones activas por base de datos, mejorando significativamente la visibilidad y fiabilidad del rendimiento del servidor jRDC2-Multi. -Problemas Identificados y Resueltos: -1. **Métricas de `BusyConnections` y `TotalConnections` inconsistentes o siempre en `0` en el `Manager` y `query_logs`:** * **Problema**: Anteriormente, la métrica `busy_connections` en `query_logs` a menudo reportaba `0` o no reflejaba el estado real. De manera similar, el panel de `Manager?command=totalcon` consistentemente mostraba `BusyConnections: 0` y `TotalConnections` estancadas en `InitialPoolSize`, a pesar de que Oracle sí reportaba conexiones activas. Esto generaba confusión sobre el uso real y la expansión del pool. * **Solución**: Se modificó la lógica en los *handlers* (`DBHandlerJSON.bas` y `DBHandlerB4X.bas`) para capturar la métrica `BusyConnections` directamente del pool de C3P0 **inmediatamente después de que el *handler* adquiere una conexión** (`con = Connector.GetConnection(finalDbKey)`). Este valor se pasa explícitamente a la subrutina `Main.LogQueryPerformance` para su registro en `query_logs` y para ser consumido por `Manager.bas` a través de `RDCConnector.GetPoolStats`. Esto garantiza que el valor registrado y reportado refleje con precisión el número de conexiones activas en el instante de su adquisición. Pruebas exhaustivas confirmaron que C3P0 sí reporta conexiones ocupadas y sí expande `TotalConnections` hasta `MaxPoolSize` cuando la demanda lo exige. -2. **Contador `handler_active_requests` no decrementaba correctamente:** * **Problema**: El contador de peticiones activas por base de datos (`GlobalParameters.ActiveRequestsCountByDB`) no mostraba un decremento consistente, resultando en un conteo que solo aumentaba o mostraba valores erráticos en los logs. * **Solución**: * Se aseguró la declaración `Public ActiveRequestsCountByDB As Map` en `GlobalParameters.bas`. * Se garantizó su inicialización como un `srvr.CreateThreadSafeMap` en `Main.AppStart` para un manejo concurrente seguro de los contadores. * En `DBHandlerJSON.bas`, la `dbKey` (obtenida del parámetro `dbx` del JSON) ahora se resuelve *antes* de incrementar el contador, asegurando que el incremento y el decremento se apliquen siempre a la misma clave de base de datos correcta. * Se implementó una coerción explícita a `Int` (`.As(Int)`) para todas las operaciones de lectura y escritura (`GetDefault`, `Put`) en `GlobalParameters.ActiveRequestsCountByDB`, resolviendo problemas de tipo que causaban inconsistencias y el fallo en el decremento. * La lógica de decremento en `Private Sub CleanupAndLog` (presente en ambos *handlers*) se hizo más robusta, verificando que el contador sea mayor que cero antes de decrementar para evitar valores negativos. -Beneficios de estos Cambios: * **Monitoreo Preciso y Fiable**: Las métricas `busy_connections` y `handler_active_requests` en `query_logs` y el panel `Manager` ahora son totalmente fiables, proporcionando una visión clara y en tiempo real del uso del pool de conexiones y la carga de peticiones activas por base de datos. * **Diagnóstico Mejorado**: La visibilidad interna del estado del pool de C3P0 durante las pruebas confirma que la configuración de `RDCConnector` es correcta y que el pool se expande y contrae según lo esperado por la demanda. * **Robustez del Código**: La gestión de contadores de peticiones activas es ahora consistente, thread-safe y a prueba de fallos de tipo, mejorando la estabilidad general del servidor bajo carga.
This commit is contained in:
272
jRDC_Multi.b4j
272
jRDC_Multi.b4j
@@ -53,7 +53,7 @@ Version=10.3
|
||||
#Region Project Attributes
|
||||
#CommandLineArgs:
|
||||
#MergeLibraries: True
|
||||
' VERSION 5.09.014
|
||||
' VERSION 5.09.14
|
||||
'###########################################################################################################
|
||||
'###################### PULL #############################################################
|
||||
'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull
|
||||
@@ -66,18 +66,6 @@ Version=10.3
|
||||
'###########################################################################################################
|
||||
#End Region
|
||||
|
||||
'- VERSION 5.09.08
|
||||
'- Se agregó que se puedan configurar en el config.properties los siguientes parametros:
|
||||
'
|
||||
' - setInitialPoolSize = 3
|
||||
' - setMinPoolSize = 2
|
||||
' - setMaxPoolSize = 5
|
||||
'
|
||||
'- Se agregaron en duro a RDConnector los siguientes parametros:
|
||||
'
|
||||
' - setMaxIdleTime <-- Tiempo máximo de inactividad de la conexión.
|
||||
' - setMaxConnectionAge <-- Tiempo de vida máximo de una conexión.
|
||||
' - setCheckoutTimeout <-- Tiempo máximo de espera por una conexión.
|
||||
'change based on the jdbc jar file
|
||||
'#AdditionalJar: mysql-connector-java-5.1.27-bin
|
||||
'#AdditionalJar: postgresql-42.7.0
|
||||
@@ -105,6 +93,7 @@ Sub Process_Globals
|
||||
Public SQL1 As SQL ' Objeto SQL para interactuar con la base de datos de usuarios (SQLite).
|
||||
Private bc As BCrypt ' Objeto para realizar operaciones de hashing de contraseñas de forma segura (para autenticación).
|
||||
Public MainConnectorsLock As JavaObject ' Objeto de bloqueo para proteger Main.Connectors
|
||||
Public ActiveRequestsCountByDB As Map
|
||||
End Sub
|
||||
|
||||
Sub AppStart (Args() As String)
|
||||
@@ -120,6 +109,8 @@ Sub AppStart (Args() As String)
|
||||
GlobalParameters.mpTotalRequests.Initialize ' Mapa para contar peticiones por endpoint/DB.
|
||||
GlobalParameters.mpTotalConnections.Initialize ' Mapa para almacenar el estado de los pools de conexión por DB.
|
||||
GlobalParameters.mpBlockConnection.Initialize ' Mapa para gestionar IPs bloqueadas (si la funcionalidad está activa).
|
||||
GlobalParameters.ActiveRequestsCountByDB = srvr.CreateThreadSafeMap ' Aseguramos que sea thread-safe para conteo de peticiones activas por DB [___new 3.txt, conversación]
|
||||
|
||||
|
||||
' 3. Inicializa las estructuras principales del servidor HTTP.
|
||||
listaDeCP.Initialize ' Inicializa la lista que contendrá los IDs de las bases de datos.
|
||||
@@ -145,105 +136,182 @@ Sub AppStart (Args() As String)
|
||||
' en el mismo directorio donde se ejecuta el JAR.
|
||||
cpFiles = File.ListFiles("./")
|
||||
If cpFiles.Size > 0 Then
|
||||
For i = 0 To cpFiles.Size - 1
|
||||
' Procesa 'config.DB2.properties'
|
||||
If cpFiles.Get(i) = "config.DB2.properties" Then
|
||||
Dim con2 As RDCConnector ' Declara una variable específica y única para el conector de DB2.
|
||||
con2.Initialize("DB2") ' Inicializa la instancia del conector para "DB2".
|
||||
Connectors.Put("DB2", con2) ' Asocia "DB2" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB2") ' Añade "DB2" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.")
|
||||
End If
|
||||
For i = 0 To cpFiles.Size - 1
|
||||
' Procesa 'config.DB2.properties'
|
||||
If cpFiles.Get(i) = "config.DB2.properties" Then
|
||||
Dim con2 As RDCConnector ' Declara una variable específica y única para el conector de DB2.
|
||||
con2.Initialize("DB2") ' Inicializa la instancia del conector para "DB2".
|
||||
Connectors.Put("DB2", con2) ' Asocia "DB2" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB2") ' Añade "DB2" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.")
|
||||
End If
|
||||
|
||||
' Procesa 'config.DB3.properties'
|
||||
If cpFiles.Get(i) = "config.DB3.properties" Then
|
||||
Dim con3 As RDCConnector ' Declara una variable específica y única para el conector de DB3.
|
||||
con3.Initialize("DB3") ' Inicializa la instancia del conector para "DB3".
|
||||
Connectors.Put("DB3", con3) ' Asocia "DB3" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB3") ' Añade "DB3" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.")
|
||||
End If
|
||||
' Procesa 'config.DB3.properties'
|
||||
If cpFiles.Get(i) = "config.DB3.properties" Then
|
||||
Dim con3 As RDCConnector ' Declara una variable específica y única para el conector de DB3.
|
||||
con3.Initialize("DB3") ' Inicializa la instancia del conector para "DB3".
|
||||
Connectors.Put("DB3", con3) ' Asocia "DB3" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB3") ' Añade "DB3" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.")
|
||||
End If
|
||||
|
||||
' Procesa 'config.DB4.properties'
|
||||
If cpFiles.Get(i) = "config.DB4.properties" Then
|
||||
Dim con4 As RDCConnector ' Declara una variable específica y única para el conector de DB4.
|
||||
con4.Initialize("DB4") ' Inicializa la instancia del conector para "DB4".
|
||||
Connectors.Put("DB4", con4) ' Asocia "DB4" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB4") ' Añade "DB4" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.")
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
Log("Main.AppStart: No se encontraron archivos de configuración adicionales (config.DBx.properties).")
|
||||
End If
|
||||
' Procesa 'config.DB4.properties'
|
||||
If cpFiles.Get(i) = "config.DB4.properties" Then
|
||||
Dim con4 As RDCConnector ' Declara una variable específica y única para el conector de DB4.
|
||||
con4.Initialize("DB4") ' Inicializa la instancia del conector para "DB4".
|
||||
Connectors.Put("DB4", con4) ' Asocia "DB4" con su instancia de RDCConnector.
|
||||
listaDeCP.Add("DB4") ' Añade "DB4" a la lista de bases de datos.
|
||||
Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.")
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
Log("Main.AppStart: No se encontraron archivos de configuración adicionales (config.DBx.properties).")
|
||||
End If
|
||||
|
||||
' Log final de las bases de datos que el servidor está gestionando.
|
||||
Dim sbListaDeCP_Log As StringBuilder
|
||||
sbListaDeCP_Log.Initialize
|
||||
For Each item As String In listaDeCP
|
||||
sbListaDeCP_Log.Append(item).Append(", ")
|
||||
Next
|
||||
If sbListaDeCP_Log.Length > 0 Then
|
||||
sbListaDeCP_Log.Remove(sbListaDeCP_Log.Length - 2, sbListaDeCP_Log.Length) ' Elimina la última ", "
|
||||
End If
|
||||
Log($"Main.AppStart: Bases de datos configuradas y listas: [${sbListaDeCP_Log.ToString}]"$)
|
||||
' Log final de las bases de datos que el servidor está gestionando.
|
||||
Dim sbListaDeCP_Log As StringBuilder
|
||||
sbListaDeCP_Log.Initialize
|
||||
For Each item As String In listaDeCP
|
||||
sbListaDeCP_Log.Append(item).Append(", ")
|
||||
Next
|
||||
If sbListaDeCP_Log.Length > 0 Then
|
||||
sbListaDeCP_Log.Remove(sbListaDeCP_Log.Length - 2, sbListaDeCP_Log.Length) ' Elimina la última ", "
|
||||
End If
|
||||
Log($"Main.AppStart: Bases de datos configuradas y listas: [${sbListaDeCP_Log.ToString}]"$)
|
||||
|
||||
' === 6. REGISTRO DE HANDLERS HTTP PARA EL SERVIDOR ===
|
||||
' Asocia rutas URL específicas con clases que manejarán las peticiones correspondientes.
|
||||
' El último parámetro (True) indica que el handler se ejecutará en un nuevo hilo,
|
||||
' lo que es recomendable para la mayoría de los casos para evitar bloqueos.
|
||||
srvr.AddHandler("/ping", "ping", True) ' Endpoint simple para verificar si el servidor está activo.
|
||||
srvr.AddHandler("/test", "TestHandler", True) ' Endpoint para pruebas de conexión y estado del servidor.
|
||||
srvr.AddHandler("/login", "LoginHandler", True) ' Muestra la página HTML de login.
|
||||
srvr.AddHandler("/dologin", "DoLoginHandler", True) ' Procesa el intento de inicio de sesión.
|
||||
srvr.AddHandler("/logout", "LogoutHandler", True) ' Cierra la sesión del usuario.
|
||||
srvr.AddHandler("/changepass", "ChangePassHandler", True) ' Permite a los usuarios cambiar su contraseña.
|
||||
srvr.AddHandler("/manager", "Manager", True) ' Panel de administración del servidor (requiere autenticación).
|
||||
srvr.AddHandler("/DBJ", "DBHandlerJSON", True) ' Handler para clientes web (ej. JavaScript, Node.js) que usan JSON.
|
||||
srvr.AddHandler("/dbrquery", "DBHandlerJSON", True) ' Un alias para el handler JSON, por si se usa en clientes específicos.
|
||||
srvr.AddHandler("/favicon.ico", "faviconHandler", True) ' Sirve el icono de la página (favicon).
|
||||
srvr.AddHandler("/*", "DBHandlerB4X", True) ' Handler por defecto para clientes B4X (DBRequestManager),
|
||||
' procesa peticiones dinámicamente según la URL.
|
||||
' === 6. REGISTRO DE HANDLERS HTTP PARA EL SERVIDOR ===
|
||||
' Asocia rutas URL específicas con clases que manejarán las peticiones correspondientes.
|
||||
' El último parámetro (True) indica que el handler se ejecutará en un nuevo hilo,
|
||||
' lo que es recomendable para la mayoría de los casos para evitar bloqueos.
|
||||
srvr.AddHandler("/ping", "ping", True) ' Endpoint simple para verificar si el servidor está activo.
|
||||
srvr.AddHandler("/test", "TestHandler", True) ' Endpoint para pruebas de conexión y estado del servidor.
|
||||
srvr.AddHandler("/login", "LoginHandler", True) ' Muestra la página HTML de login.
|
||||
srvr.AddHandler("/dologin", "DoLoginHandler", True) ' Procesa el intento de inicio de sesión.
|
||||
srvr.AddHandler("/logout", "LogoutHandler", True) ' Cierra la sesión del usuario.
|
||||
srvr.AddHandler("/changepass", "ChangePassHandler", True) ' Permite a los usuarios cambiar su contraseña.
|
||||
srvr.AddHandler("/manager", "Manager", True) ' Panel de administración del servidor (requiere autenticación).
|
||||
srvr.AddHandler("/DBJ", "DBHandlerJSON", False) ' Handler para clientes web (ej. JavaScript, Node.js) que usan JSON.
|
||||
srvr.AddHandler("/dbrquery", "DBHandlerJSON", False) ' Un alias para el handler JSON, por si se usa en clientes específicos.
|
||||
srvr.AddHandler("/favicon.ico", "faviconHandler", True) ' Sirve el icono de la página (favicon).
|
||||
srvr.AddHandler("/*", "DBHandlerB4X", False) ' Handler por defecto para clientes B4X (DBRequestManager),
|
||||
' procesa peticiones dinámicamente según la URL.
|
||||
|
||||
' 7. Inicia el servidor HTTP.
|
||||
srvr.Start
|
||||
Log("===========================================================")
|
||||
Log($"-=== jRDC está funcionando en el puerto: ${srvr.Port} (versión = $1.2{VERSION}) ===-"$)
|
||||
Log("===========================================================")
|
||||
|
||||
' 8. Inicia el bucle de mensajes de B4J. Es esencial para que la aplicación
|
||||
' de servidor continúe ejecutándose y procesando eventos.
|
||||
StartMessageLoop
|
||||
' 7. Inicia el servidor HTTP.
|
||||
srvr.Start
|
||||
Log("===========================================================")
|
||||
Log($"-=== jRDC está funcionando en el puerto: ${srvr.Port} (versión = $1.2{VERSION}) ===-"$)
|
||||
Log("===========================================================")
|
||||
|
||||
' 8. Inicia el bucle de mensajes de B4J. Es esencial para que la aplicación
|
||||
' de servidor continúe ejecutándose y procesando eventos.
|
||||
StartMessageLoop
|
||||
End Sub
|
||||
|
||||
' --- Subrutina para inicializar la base de datos de usuarios local (SQLite) ---
|
||||
' Esta base de datos se utiliza para almacenar credenciales de usuarios que pueden
|
||||
' acceder al panel de administración del servidor jRDC.
|
||||
' acceder al panel de administración del servidor jRDC y los logs de queries.
|
||||
Sub InitializeSQLiteDatabase
|
||||
Dim dbFileName As String = "users.db" ' Nombre del archivo de la base de datos SQLite.
|
||||
|
||||
' Verifica si el archivo de la base de datos ya existe en el directorio de la aplicación.
|
||||
If File.Exists(File.DirApp, dbFileName) = False Then
|
||||
Log("Creando nueva base de datos de usuarios: " & dbFileName)
|
||||
' Inicializa la conexión a la base de datos SQLite, creándola si no existe (último parámetro en True).
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
Dim dbFileName As String = "users.db" ' Nombre del archivo de la base de datos SQLite.
|
||||
|
||||
' Verifica si el archivo de la base de datos ya existe en el directorio de la aplicación.
|
||||
If File.Exists(File.DirApp, dbFileName) = False Then
|
||||
Log("Creando nueva base de datos de usuarios: " & dbFileName)
|
||||
' Inicializa la conexión a la base de datos SQLite, creándola si no existe (último parámetro en True).
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
|
||||
' Define y ejecuta la sentencia SQL para crear la tabla 'users'.
|
||||
Dim createUserTable As String = "CREATE TABLE users (username TEXT PRIMARY KEY, password_hash TEXT NOT NULL)"
|
||||
SQL1.ExecNonQuery(createUserTable)
|
||||
|
||||
' >>> INICIO: Creación de la tabla query_logs con las nuevas columnas desde CERO <<<
|
||||
Log("Creando tabla 'query_logs' con columnas de rendimiento.")
|
||||
Dim createQueryLogsTable As String = "CREATE TABLE query_logs (id INTEGER PRIMARY KEY AUTOINCREMENT, query_name TEXT, duration_ms INTEGER, timestamp INTEGER, db_key TEXT, client_ip TEXT, busy_connections INTEGER, handler_active_requests INTEGER)"
|
||||
SQL1.ExecNonQuery(createQueryLogsTable)
|
||||
' >>> FIN: Creación de la tabla query_logs <<<
|
||||
|
||||
' Crea un usuario por defecto para facilitar el primer acceso al panel de administración.
|
||||
Dim defaultUser As String = "admin"
|
||||
Dim defaultPass As String = "12345"
|
||||
' Genera un hash seguro de la contraseña usando BCrypt, lo cual es crucial para la seguridad.
|
||||
Dim hashedPass As String = bc.hashpw(defaultPass, bc.gensalt)
|
||||
' Inserta el usuario por defecto en la tabla 'users'.
|
||||
SQL1.ExecNonQuery2("INSERT INTO users (username, password_hash) VALUES (?, ?)", Array As Object(defaultUser, hashedPass))
|
||||
Log($"Usuario por defecto creado -> user: ${defaultUser}, pass: ${defaultPass}"$)
|
||||
Else
|
||||
' Si el archivo de la base de datos ya existe, simplemente se abre.
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
Log("Base de datos de usuarios cargada.")
|
||||
|
||||
' >>> INICIO: Lógica de migración (ALTER TABLE) si la DB ya existía <<<
|
||||
Log("Verificando y migrando tabla 'query_logs' si es necesario.")
|
||||
|
||||
' Define y ejecuta la sentencia SQL para crear la tabla 'users'.
|
||||
Dim createUserTable As String = "CREATE TABLE users (username TEXT PRIMARY KEY, password_hash TEXT NOT NULL)"
|
||||
SQL1.ExecNonQuery(createUserTable)
|
||||
|
||||
' Crea un usuario por defecto para facilitar el primer acceso al panel de administración.
|
||||
Dim defaultUser As String = "admin"
|
||||
Dim defaultPass As String = "12345"
|
||||
' Genera un hash seguro de la contraseña usando BCrypt, lo cual es crucial para la seguridad.
|
||||
Dim hashedPass As String = bc.hashpw(defaultPass, bc.gensalt)
|
||||
' Inserta el usuario por defecto en la tabla 'users'.
|
||||
SQL1.ExecNonQuery2("INSERT INTO users (username, password_hash) VALUES (?, ?)", Array As Object(defaultUser, hashedPass))
|
||||
Log($"Usuario por defecto creado -> user: ${defaultUser}, pass: ${defaultPass}"$)
|
||||
Else
|
||||
' Si el archivo de la base de datos ya existe, simplemente se abre.
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
Log("Base de datos de usuarios cargada.")
|
||||
End If
|
||||
' Primero, verificar si la tabla query_logs existe.
|
||||
If SQL1.ExecQuerySingleResult("SELECT name FROM sqlite_master WHERE type='table' AND name='query_logs'") = Null Then
|
||||
Log("Tabla 'query_logs' no encontrada, creándola con columnas de rendimiento.")
|
||||
Dim createQueryLogsTable As String = "CREATE TABLE query_logs (id INTEGER PRIMARY KEY AUTOINCREMENT, query_name TEXT, duration_ms INTEGER, timestamp INTEGER, db_key TEXT, client_ip TEXT, busy_connections INTEGER, handler_active_requests INTEGER)"
|
||||
SQL1.ExecNonQuery(createQueryLogsTable)
|
||||
Else
|
||||
' Si la tabla query_logs ya existe, entonces verificamos y añadimos las columnas faltantes.
|
||||
Dim columnExists As Boolean
|
||||
Dim rs As ResultSet
|
||||
|
||||
' --- VERIFICAR Y AÑADIR busy_connections ---
|
||||
columnExists = False
|
||||
' Ejecutamos PRAGMA sin WHERE y lo filtramos en código.
|
||||
rs = SQL1.ExecQuery("PRAGMA table_info(query_logs)")
|
||||
Do While rs.NextRow
|
||||
If rs.GetString("name").EqualsIgnoreCase("busy_connections") Then
|
||||
columnExists = True
|
||||
Exit ' La columna ya existe, salimos del bucle.
|
||||
End If
|
||||
Loop
|
||||
rs.Close ' ¡Importante cerrar el ResultSet!
|
||||
|
||||
If columnExists = False Then
|
||||
Log("Añadiendo columna 'busy_connections' a query_logs.")
|
||||
SQL1.ExecNonQuery("ALTER TABLE query_logs ADD COLUMN busy_connections INTEGER DEFAULT 0")
|
||||
End If
|
||||
|
||||
' --- VERIFICAR Y AÑADIR handler_active_requests ---
|
||||
columnExists = False
|
||||
rs = SQL1.ExecQuery("PRAGMA table_info(query_logs)") ' Ejecutamos PRAGMA nuevamente para esta columna.
|
||||
Do While rs.NextRow
|
||||
If rs.GetString("name").EqualsIgnoreCase("handler_active_requests") Then
|
||||
columnExists = True
|
||||
Exit ' La columna ya existe, salimos del bucle.
|
||||
End If
|
||||
Loop
|
||||
rs.Close ' ¡Importante cerrar el ResultSet!
|
||||
|
||||
If columnExists = False Then
|
||||
Log("Añadiendo columna 'handler_active_requests' a query_logs.")
|
||||
SQL1.ExecNonQuery("ALTER TABLE query_logs ADD COLUMN handler_active_requests INTEGER DEFAULT 0")
|
||||
End If
|
||||
End If
|
||||
' >>> FIN: Lógica de migración (ALTER TABLE) <<<
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' Subrutina para registrar las métricas de rendimiento de las queries
|
||||
Public Sub LogQueryPerformance(QueryName As String, DurationMs As Long, DbKey As String, ClientIp As String, HandlerActiveRequests As Int, PoolBusyConnections As Int)
|
||||
Try
|
||||
' El valor PoolBusyConnections ya se recibe directamente del handler.
|
||||
' Removemos la lógica anterior de obtenerlo del conector.
|
||||
' Dim connector As RDCConnector = Main.Connectors.Get(DbKey).As(RDCConnector)
|
||||
' Dim poolBusyConnections As Int = 0
|
||||
' If connector.IsInitialized Then
|
||||
' Dim poolStats As Map = connector.GetPoolStats
|
||||
' If poolStats.ContainsKey("BusyConnections") Then
|
||||
' poolBusyConnections = poolStats.Get("BusyConnections")
|
||||
' End If
|
||||
' Else
|
||||
' Log($"ADVERTENCIA: Conector RDC para ${DbKey} no inicializado al intentar loguear rendimiento."$)
|
||||
' End If
|
||||
|
||||
' Insertamos los datos en la tabla query_logs de SQLite
|
||||
SQL1.ExecNonQuery2("INSERT INTO query_logs (query_name, duration_ms, timestamp, db_key, client_ip, busy_connections, handler_active_requests) VALUES (?, ?, ?, ?, ?, ?, ?)", _
|
||||
Array As Object(QueryName, DurationMs, DateTime.Now, DbKey, ClientIp, PoolBusyConnections, HandlerActiveRequests))
|
||||
Catch
|
||||
Log("Error al guardar log de query en SQLite (Main.LogQueryPerformance): " & LastException.Message)
|
||||
End Try
|
||||
End Sub
|
||||
Reference in New Issue
Block a user