mirror of
https://github.com/KeymonSoft/jRDC-Multi.git
synced 2026-04-17 21:06:24 +00:00
- VERSION 5.09.16
- feat: Implementa tolerancia de parámetros configurable y mejora estabilidad general del servidor. - La tolerancia de parametros permite que si un query requiere 3 parametros y se mandan 4, NO mande un error, solo manda a la base de datos los parametros correctos y tira los extras, y guarda una "ADVERTENCIA" en el Log de errores. - Este commit introduce la funcionalidad de `parameterTolerance` configurable y aborda varias mejoras críticas para la estabilidad y eficiencia del jRDC2-Multi. - Principales cambios y beneficios: - **Tolerancia de Parámetros**: Añade la propiedad `parameterTolerance` en `config.properties` para controlar el manejo de parámetros de más. Cuando está habilitada, recorta los parámetros excesivos; si está deshabilitada (modo estricto, por defecto), genera un error, aumentando la robustez de la validación. - **Inicialización Multi-DB Confiable**: Corrige la lógica de inicialización en `Main.AppStart` para `RDCConnector` de DB3 y DB4, asegurando que cada base de datos tenga su propio *pool* de conexiones correctamente configurado. - **Optimización de Ejecución SQL**: Elimina llamadas duplicadas a `ExecQuery2` y `ExecNonQuery2` en `DBHandlerB4X.bas`, garantizando que solo los parámetros validados se utilicen y evitando ejecuciones redundantes en la base de datos. - **Refactorización y Limpieza**: Se eliminó la declaración duplicada de `ActiveRequestsCountByDB` en `Main.bas` y la subrutina `Handle0` obsoleta en `Manager.bas`, mejorando la claridad y mantenibilidad del código.
This commit is contained in:
136
jRDC_Multi.b4j
136
jRDC_Multi.b4j
@@ -32,9 +32,10 @@ Library8=jsql
|
||||
Library9=bcrypt
|
||||
Module1=Cambios
|
||||
Module10=Manager
|
||||
Module11=ping
|
||||
Module12=RDCConnector
|
||||
Module13=TestHandler
|
||||
Module11=ParameterValidationUtils
|
||||
Module12=ping
|
||||
Module13=RDCConnector
|
||||
Module14=TestHandler
|
||||
Module2=ChangePassHandler
|
||||
Module3=DBHandlerB4X
|
||||
Module4=DBHandlerJSON
|
||||
@@ -45,7 +46,7 @@ Module8=LoginHandler
|
||||
Module9=LogoutHandler
|
||||
NumberOfFiles=10
|
||||
NumberOfLibraries=9
|
||||
NumberOfModules=13
|
||||
NumberOfModules=14
|
||||
Version=10.3
|
||||
@EndOfDesignText@
|
||||
'Non-UI application (console / server application)
|
||||
@@ -53,7 +54,7 @@ Version=10.3
|
||||
#Region Project Attributes
|
||||
#CommandLineArgs:
|
||||
#MergeLibraries: True
|
||||
' VERSION 5.09.15
|
||||
' VERSION 5.09.16
|
||||
'###########################################################################################################
|
||||
'###################### PULL #############################################################
|
||||
'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull
|
||||
@@ -107,11 +108,15 @@ Sub Process_Globals
|
||||
' Objeto de bloqueo (ReentrantLock) para proteger el mapa Main.Connectors durante operaciones de recarga (Hot-Swap).
|
||||
Public MainConnectorsLock As JavaObject
|
||||
|
||||
' Mapa para contar las peticiones activas por cada base de datos. Es thread-safe.
|
||||
Public ActiveRequestsCountByDB As Map
|
||||
|
||||
' Timer para ejecutar tareas periódicas, como la limpieza de logs.
|
||||
Public timerLogs As Timer
|
||||
|
||||
' Tipo para encapsular el resultado de la validación de parámetros.
|
||||
Type ParameterValidationResult ( _
|
||||
Success As Boolean, _
|
||||
ErrorMessage As String, _
|
||||
ParamsToExecute As List _ ' La lista de parámetros final a usar en la ejecución SQL
|
||||
)
|
||||
End Sub
|
||||
|
||||
Sub AppStart (Args() As String)
|
||||
@@ -149,12 +154,20 @@ Sub AppStart (Args() As String)
|
||||
|
||||
' === 4. INICIALIZACIÓN DEL CONECTOR PARA LA BASE DE DATOS PRINCIPAL (DB1) ===
|
||||
' DB1 siempre usa el archivo 'config.properties' por defecto.
|
||||
Dim con1 As RDCConnector ' Declara una variable específica y única para el conector de DB1.
|
||||
con1.Initialize("DB1") ' Inicializa la instancia del conector para "DB1".
|
||||
Connectors.Put("DB1", con1) ' Asocia el identificador "DB1" con su instancia de RDCConnector.
|
||||
srvr.Port = con1.serverPort ' El puerto del servidor HTTP se obtiene del config.properties de DB1.
|
||||
listaDeCP.Add("DB1") ' Añade "DB1" a la lista de bases de datos gestionadas.
|
||||
Log($"Main.AppStart: Conector 'DB1' inicializado exitosamente en puerto: ${srvr.Port}"$)
|
||||
Try
|
||||
Dim con1 As RDCConnector ' Declara una variable específica y única para el conector de DB1.
|
||||
con1.Initialize("DB1") ' Inicializa la instancia del conector para "DB1".
|
||||
Connectors.Put("DB1", con1) ' Asocia el identificador "DB1" con su instancia de RDCConnector.
|
||||
srvr.Port = con1.serverPort ' El puerto del servidor HTTP se obtiene del config.properties de DB1.
|
||||
listaDeCP.Add("DB1") ' Añade "DB1" a la lista de bases de datos gestionadas.
|
||||
Log($"Main.AppStart: Conector 'DB1' inicializado exitosamente en puerto: ${srvr.Port}"$)
|
||||
Catch
|
||||
Dim ErrorMsg As String = $"Main.AppStart: ERROR CRÍTICO al inicializar conector 'DB1': ${LastException.Message}"$
|
||||
Log(ErrorMsg)
|
||||
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB1", Null, Null)
|
||||
' Si DB1 falla, el servidor no puede arrancar correctamente.
|
||||
ExitApplication
|
||||
End Try
|
||||
|
||||
' === 5. DETECCIÓN E INICIALIZACIÓN DE BASES DE DATOS ADICIONALES (DB2, DB3, DB4) ===
|
||||
' El servidor busca archivos de configuración adicionales (ej. config.DB2.properties)
|
||||
@@ -164,27 +177,48 @@ Sub AppStart (Args() As String)
|
||||
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.")
|
||||
Try
|
||||
Dim con2 As RDCConnector
|
||||
con2.Initialize("DB2")
|
||||
Connectors.Put("DB2", con2)
|
||||
listaDeCP.Add("DB2")
|
||||
Log("Main.AppStart: Conector 'DB2' inicializado exitosamente.")
|
||||
Catch
|
||||
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB2': ${LastException.Message}"$
|
||||
Log(ErrorMsg)
|
||||
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB2", Null, Null)
|
||||
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
|
||||
End Try
|
||||
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.")
|
||||
Try
|
||||
Dim con3 As RDCConnector
|
||||
con3.Initialize("DB3")
|
||||
Connectors.Put("DB3", con3)
|
||||
listaDeCP.Add("DB3")
|
||||
Log("Main.AppStart: Conector 'DB3' inicializado exitosamente.")
|
||||
Catch
|
||||
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB3': ${LastException.Message}"$
|
||||
Log(ErrorMsg)
|
||||
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB3", Null, Null)
|
||||
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
|
||||
End Try
|
||||
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.")
|
||||
Try
|
||||
Dim con4 As RDCConnector
|
||||
con4.Initialize("DB4")
|
||||
Connectors.Put("DB4", con4)
|
||||
listaDeCP.Add("DB4")
|
||||
Log("Main.AppStart: Conector 'DB4' inicializado exitosamente.")
|
||||
Catch
|
||||
Dim ErrorMsg As String = $"Main.AppStart: ERROR al inicializar conector 'DB4': ${LastException.Message}"$
|
||||
Log(ErrorMsg)
|
||||
LogServerError("ERROR", "Main.AppStart", ErrorMsg, "DB4", Null, Null)
|
||||
' Si un conector secundario falla, el servidor puede continuar, pero es importante saberlo.
|
||||
End Try
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
@@ -261,6 +295,15 @@ Sub InitializeSQLiteDatabase
|
||||
' 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}"$)
|
||||
|
||||
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)
|
||||
|
||||
' >>> INICIO: Creación de la tabla errores con columnas de error/advertencia <<<
|
||||
Log("Creando tabla 'errores' para registrar eventos.")
|
||||
Dim createErrorsTable As String = "CREATE TABLE errores (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, type TEXT, source TEXT, message TEXT, db_key TEXT, command_name TEXT, client_ip TEXT)"
|
||||
SQL1.ExecNonQuery(createErrorsTable)
|
||||
' >>> FIN: Creación de la tabla errores <<<
|
||||
Else
|
||||
' Si el archivo de la base de datos ya existe, simplemente se abre.
|
||||
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
|
||||
@@ -311,6 +354,19 @@ Sub InitializeSQLiteDatabase
|
||||
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
|
||||
|
||||
' >>> INICIO: Lógica de migración para 'errores' si la DB ya existía <<<
|
||||
Log("Verificando y migrando tabla 'errores' si es necesario.")
|
||||
If SQL1.ExecQuerySingleResult("SELECT name FROM sqlite_master WHERE type='table' AND name='errores'") = Null Then
|
||||
Log("Tabla 'errores' no encontrada, creándola.")
|
||||
Dim createErrorsTable As String = "CREATE TABLE errores (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, type TEXT, source TEXT, message TEXT, db_key TEXT, command_name TEXT, client_ip TEXT)"
|
||||
SQL1.ExecNonQuery(createErrorsTable)
|
||||
Else
|
||||
' Si la tabla ya existe, podrías añadir lógica para ALTER TABLE si se añaden nuevas columnas en el futuro.
|
||||
' Por ahora, asumimos que la estructura inicial es suficiente.
|
||||
Log("Tabla 'errores' ya existe.")
|
||||
End If
|
||||
' >>> FIN: Lógica de migración para 'errores' <<<
|
||||
End If
|
||||
' >>> FIN: Lógica de migración (ALTER TABLE) <<<
|
||||
End If
|
||||
@@ -330,13 +386,33 @@ Public Sub LogQueryPerformance(QueryName As String, DurationMs As Long, DbKey As
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
' Subrutina para registrar errores y advertencias en la tabla 'errores'.
|
||||
' Type: "ERROR" o "ADVERTENCIA"
|
||||
' Source: Módulo.Subrutina donde ocurrió el evento (ej. "DBHandlerJSON.Handle")
|
||||
' Message: El mensaje descriptivo del error/advertencia.
|
||||
' DBKey: La clave de la base de datos involucrada (ej. "DB1", "DB2"), Null si no aplica.
|
||||
' CommandName: El nombre del comando SQL (ej. "select_user"), Null si no aplica.
|
||||
' ClientIp: La dirección IP del cliente que originó la petición, Null si no aplica.
|
||||
Public Sub LogServerError(Type0 As String, Source As String, Message As String, DBKey As String, CommandName As String, ClientIp As String)
|
||||
Try
|
||||
SQL1.ExecNonQuery2("INSERT INTO errores (timestamp, type, source, message, db_key, command_name, client_ip) VALUES (?, ?, ?, ?, ?, ?, ?)", _
|
||||
Array As Object(DateTime.Now, Type0, Source, Message, DBKey, CommandName, ClientIp))
|
||||
Catch
|
||||
Log("ERROR CRÍTICO: Fallo al guardar el log de error/advertencia en SQLite (Main.LogServerError): " & LastException.Message)
|
||||
' En este punto, no podemos hacer mucho más que loggear el fallo en la consola,
|
||||
' para evitar un bucle infinito de errores de logging.
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
' Subrutina de evento para el Timer 'timerLogs'.
|
||||
' Se ejecuta periódicamente (cada 10 minutos) para limpiar la tabla de logs.
|
||||
Sub TimerLogs_Tick
|
||||
Try
|
||||
borraArribaDe15000Logs ' Llama a la función para limpiar los logs.
|
||||
Catch
|
||||
Log("ERROR en TimerLogs_Tick al intentar borrar logs: " & LastException.Message)
|
||||
Dim ErrorMsg As String = "ERROR en TimerLogs_Tick al intentar borrar logs: " & LastException.Message
|
||||
Log(ErrorMsg)
|
||||
LogServerError("ERROR", "Main.TimerLogs_Tick", ErrorMsg, Null, "log_cleanup", Null) ' <-- Nuevo Log
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
|
||||
Reference in New Issue
Block a user