- VERSION 5.09.19

- feat(sqlite): Implementa optimización de SQLite (WAL e Índices)
- fix(manager): Extiende el comando 'test' para verificar todos los pools de conexión configurados.

- Mejoras al subsistema de logs y diagnóstico del servidor jRDC2-Multi.
- Cambios principales:
1. Optimización del Rendimiento de SQLite (users.db):
*   Habilitación de WAL: Se implementó PRAGMA journal_mode=WAL y PRAGMA synchronous=NORMAL en `InitializeSQLiteDatabase`. Esto reduce la contención de disco y mejora el rendimiento de I/O en las escrituras transaccionales de logs por lotes.
*   Índices de logs: Se agregaron índices a las columnas `timestamp` y `duration_ms` en `query_logs`, y a `timestamp` en `errores`. Esto acelera drásticamente las operaciones de limpieza periódica (`borraArribaDe15000Logs`) y la generación de reportes de consultas lentas (`slowqueries`).

2. Mejora del Comando de Diagnóstico 'test':
*   Se corrigió el comando `manager?command=test` para que no solo pruebe la conexión de `DB1`, sino que itere sobre `Main.listaDeCP` y fuerce la adquisición y liberación de una conexión (`GetConnection`) en *todos* los `RDCConnector` configurados (DB1, DB2, DB3, etc.).
*   La nueva lógica garantiza una prueba de vida rigurosa de cada pool C3P0, devolviendo un mensaje detallado del estado de conectividad y registrando un error crítico vía `LogServerError` si algún pool no responde.
This commit is contained in:
2025-09-27 20:33:44 -06:00
parent 616013f0fb
commit 8b876e5095
10 changed files with 391 additions and 71 deletions

View File

@@ -1,19 +1,15 @@
AppType=StandardJava
Build1=Default,b4j.JRDCMulti
File1=config.DB2.properties
File10=start2.bat
File11=stop.bat
File2=config.DB3.properties
File3=config.DB4.properties
File4=config.properties
File5=login.html
File6=manager.html
File7=reiniciaProcesoBow.bat
File8=reiniciaProcesoPM2.bat
File9=start.bat
File5=reiniciaProcesoBow.bat
File6=reiniciaProcesoPM2.bat
File7=start.bat
File8=start2.bat
File9=stop.bat
FileGroup1=Default Group
FileGroup10=Default Group
FileGroup11=Default Group
FileGroup2=Default Group
FileGroup3=Default Group
FileGroup4=Default Group
@@ -23,15 +19,15 @@ FileGroup7=Default Group
FileGroup8=Default Group
FileGroup9=Default Group
Group=Default Group
Library1=byteconverter
Library2=javaobject
Library3=jcore
Library4=jrandomaccessfile
Library5=jserver
Library6=jshell
Library7=json
Library8=jsql
Library9=bcrypt
Library1=bcrypt
Library2=byteconverter
Library3=javaobject
Library4=jcore
Library5=jrandomaccessfile
Library6=jserver
Library7=jshell
Library8=json
Library9=jsql
Module1=Cambios
Module10=Manager
Module11=Manager0
@@ -49,7 +45,7 @@ Module6=faviconHandler
Module7=GlobalParameters
Module8=LoginHandler
Module9=LogoutHandler
NumberOfFiles=11
NumberOfFiles=9
NumberOfLibraries=9
NumberOfModules=17
Version=10.3
@@ -60,7 +56,7 @@ Version=10.3
#CommandLineArgs:
#MergeLibraries: True
' VERSION 5.09.18
' VERSION 5.09.19
'###########################################################################################################
'###################### PULL #############################################################
'Ctrl + click ide://run?file=%WINDIR%\System32\cmd.exe&Args=/c&Args=git&Args=pull
@@ -148,7 +144,22 @@ Sub AppStart (Args() As String)
#End If
' --- Subrutina principal que se ejecuta al iniciar la aplicación ---
' SSE.Initialize
' La subcarpeta es "www"
CopiarRecursoSiNoExiste("manager.html", "www")
CopiarRecursoSiNoExiste("login.html", "www")
' --- Copiar los archivos .bat de la raíz ---
' La subcarpeta es "" (vacía) porque están en la raíz de "Files"
CopiarRecursoSiNoExiste("config.properties", "")
CopiarRecursoSiNoExiste("config.DB2.properties", "")
CopiarRecursoSiNoExiste("config.DB3.properties", "")
CopiarRecursoSiNoExiste("start.bat", "")
CopiarRecursoSiNoExiste("start2.bat", "")
CopiarRecursoSiNoExiste("stop.bat", "")
CopiarRecursoSiNoExiste("reiniciaProcesoBow.bat", "")
CopiarRecursoSiNoExiste("reiniciaProcesoPM2.bat", "")
'
' Log("Verificación de archivos completada.")
bc.Initialize("BC")
QueryLogCache.Initialize
@@ -343,7 +354,6 @@ Sub InitializeSQLiteDatabase
If File.Exists(File.DirApp, dbFileName) = False Then
Log("Creando nueva base de datos de usuarios: " & dbFileName)
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
' Crear tabla 'users'
Dim createUserTable As String = "CREATE TABLE users (username TEXT PRIMARY KEY, password_hash TEXT NOT NULL)"
SQL1.ExecNonQuery(createUserTable)
@@ -353,6 +363,9 @@ Sub InitializeSQLiteDatabase
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)
SQL1.ExecNonQuery("PRAGMA journal_mode=WAL;")
SQL1.ExecNonQuery("PRAGMA synchronous=NORMAL;")
' Insertar usuario por defecto
Dim defaultUser As String = "admin"
Dim defaultPass As String = "12345"
@@ -364,10 +377,23 @@ Sub InitializeSQLiteDatabase
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)
If logger Then Log("Creando índices de rendimiento en tablas de logs.")
' Índice en timestamp para limpieza rápida (DELETE/ORDER BY) en query_logs
SQL1.ExecNonQuery("CREATE INDEX idx_query_timestamp ON query_logs(timestamp)")
' Índice en duration_ms para la consulta 'slowqueries' (ORDER BY)
SQL1.ExecNonQuery("CREATE INDEX idx_query_duration ON query_logs(duration_ms)")
' Índice en timestamp para limpieza rápida de la tabla de errores
SQL1.ExecNonQuery("CREATE INDEX idx_error_timestamp ON errores(timestamp)")
Else
SQL1.InitializeSQLite(File.DirApp, dbFileName, True)
Log("Base de datos de usuarios cargada.")
SQL1.ExecNonQuery("PRAGMA journal_mode=WAL;")
SQL1.ExecNonQuery("PRAGMA synchronous=NORMAL;")
' >>> INICIO: Lógica de migración (ALTER TABLE) si la DB ya existía <<<
If logger Then Log("Verificando y migrando tabla 'query_logs' si es necesario.")
@@ -671,7 +697,6 @@ Public Sub WriteErrorLogsBatch
End Sub
' --- Borra los registros más antiguos de la tabla 'query_logs' y hace VACUUM. ---
' ¡MODIFICADA PARA USAR FILTRADO GLOBAL!
Sub borraArribaDe15000Logs 'ignore
If IsAnySQLiteLoggingEnabled Then ' Solo ejecutar si al menos una DB requiere logs.
@@ -690,4 +715,65 @@ Sub borraArribaDe15000Logs 'ignore
' Si IsAnySQLiteLoggingEnabled es False, el Timer no debería estar activo.
If logger Then Log("AVISO: Tarea de limpieza de logs omitida. El logging global de SQLite está deshabilitado.")
End If
End Sub
End Sub
'Copiamos recursos del jar al directorio de la app
Sub CopiarRecursoSiNoExiste(NombreArchivo As String, SubCarpeta As String)
Dim DirDestino As String = File.Combine(File.DirApp, SubCarpeta)
If SubCarpeta <> "" And File.Exists(DirDestino, "") = False Then
File.MakeDir(DirDestino, "")
End If
Dim ArchivoDestino As String = File.Combine(DirDestino, NombreArchivo)
If File.Exists(DirDestino, NombreArchivo) = False Then
Dim RutaRecurso As String
If SubCarpeta <> "" Then
RutaRecurso = "Files/" & SubCarpeta & "/" & NombreArchivo
Else
RutaRecurso = "Files/" & NombreArchivo
End If
Dim classLoader As JavaObject = GetThreadContextClassLoader
Dim InStream As InputStream = classLoader.RunMethod("getResourceAsStream", Array(RutaRecurso))
If InStream.IsInitialized Then
Log($"Copiando recurso: '${RutaRecurso}'..."$)
' Llamamos a nuestra propia función de copiado manual
Dim OutStream As OutputStream = File.OpenOutput(DirDestino, NombreArchivo, False)
CopiarStreamManualmente(InStream, OutStream)
Log($"'${ArchivoDestino}' copiado correctamente."$)
Else
Log($"ERROR: No se pudo encontrar el recurso con la ruta interna: '${RutaRecurso}'"$)
End If
End If
End Sub
' No depende de ninguna librería extraña.
Sub CopiarStreamManualmente (InStream As InputStream, OutStream As OutputStream)
Try
Dim buffer(1024) As Byte
Dim len As Int
len = InStream.ReadBytes(buffer, 0, buffer.Length)
Do While len > 0
OutStream.WriteBytes(buffer, 0, len)
len = InStream.ReadBytes(buffer, 0, buffer.Length)
Loop
Catch
LogError(LastException)
End Try
InStream.Close
OutStream.Close
End Sub
' Función ayudante para obtener el Class Loader correcto.
Sub GetThreadContextClassLoader As JavaObject
Dim thread As JavaObject
thread = thread.InitializeStatic("java.lang.Thread").RunMethod("currentThread", Null)
Return thread.RunMethod("getContextClassLoader", Null)
End Sub