Files
jRDC-MultiDB-Hikari/HikariConnectionPool.bas
jaguerrau 9c9e2975e9 - VERSION 5.10.27
- feat(arquitectura): Consolidación de estabilidad y diagnóstico.
- refactor: Arquitectura de base de datos local y políticas de logs.
- arch(sqlite): Aislamiento total de las conexiones SQLite en SQL_Auth y SQL_Logs. Esto protege las operaciones de autenticación críticas de la alta carga de I/O generada por el subsistema de logs.
- feat(logs): Implementación de modo de almacenamiento flexible para logs (disco o en memoria), mejorando la capacidad de testing.
- refactor(logs): Se estandariza el límite de retención de registros a 10,000 para todas las tablas de logs, y se renombra la subrutina de limpieza a borraArribaDe10000Logs.
2025-10-29 05:25:56 -06:00

334 lines
12 KiB
QBasic

B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=6.01
@EndOfDesignText@
'Class module : HikariConnectionPool
'Author: Oliver Ackermann
'Created on: 2018/05/07
'Based on: https://github.com/AnywhereSoftware/B4J_Server/blob/master/src/anywheresoftware/b4j/object/ConnectionPool.java
'Resources:
' https://github.com/brettwooldridge/HikariCP
' addDataSourceProperty - individual driver property
' setDataSourceProperties - Java Property values
Sub Class_Globals
Private poolJO As JavaObject
Private poolProperties As Map
Private PoolSizeConfig As Int
Private MinIdleConfig As Int
Private MaxLifeConfig As Long
Private ConnTimeoutConfig As Long
Private LeakDetectionThresholdConfig As Long
Private KeepAliveTimeConfig As Long
End Sub
Public Sub Initialize
Dim properties As Map
properties.Initialize
'Frequently used
properties.Put("autocommit", "boolean")
properties.Put("connectiontimeout", "int")
properties.Put("idletimeout", "int")
properties.Put("maxlifetime", "int")
properties.Put("connectiontestquery", "string")
properties.Put("minimumidle", "int")
properties.Put("maximumpoolsize", "int")
properties.Put("poolname", "string")
properties.Put("keepalivetime", "int")
'Infrequently used
properties.Put("initializationfailtimeout", "int")
properties.Put("isolateinternalqueries", "boolean")
properties.Put("allowpoolsuspension", "boolean")
properties.Put("readonly", "boolean")
properties.Put("registermbeans", "boolean")
properties.Put("connectioninitsql", "string")
properties.Put("validationtimeout", "int")
properties.Put("leakdetectionthreshold", "int")
poolProperties = properties
End Sub
'Creates the pool.
' driverClass - The JDBC driver class.
' jdbcUrl - JDBC connection url.
' aUser / aPassword - Connection credentials.
Public Sub CreatePool(driverClass As String, jdbcUrl As String, aUser As String, aPassword As String)
CreatePool2(driverClass, jdbcUrl, aUser, aPassword, 0)
End Sub
'Creates the pool.
' driverClass - The JDBC driver class.
' jdbcUrl - JDBC connection url.
' aUser / aPassword - Connection credentials.
' poolSize - Maximum size of connection pool. Pool's default used when < 1.
Public Sub CreatePool2(driverClass As String, jdbcUrl As String, aUser As String, aPassword As String, poolSize As Int)
Dim jo As JavaObject
poolJO = jo.InitializeNewInstance("com.zaxxer.hikari.HikariDataSource", Null)
poolJO.RunMethod("setDriverClassName",Array As Object(driverClass))
poolJO.RunMethod("setJdbcUrl", Array As Object(jdbcUrl))
poolJO.RunMethod("setUsername", Array As Object(aUser))
poolJO.RunMethod("setPassword", Array As Object(aPassword))
If poolSize > 0 Then poolJO.RunMethod("setMaximumPoolSize", Array As Object(poolSize))
End Sub
Public Sub CreatePool3(options As Map)
CreatePool2(options.Get("driver"), options.Get("url"), options.Get("user"), options.Get("password"), options.Get("poolsize"))
End Sub
'https://github.com/openbouquet/HikariCP/blob/master/src/main/java/com/zaxxer/hikari/HikariConfig.java
'HikariConfig can use Properties object. But this must be done before creating the pool. The pool than is created using
'that HikariConfig object.
Public Sub SetProperties(properties As Map)
' Optimal default values for HikariCP (in milliseconds)
Private const DEFAULT_MAX_LIFE As Long = 1750000
Private const DEFAULT_CONN_TIMEOUT As Long = 30000
Private const DEFAULT_LEAK_THR As Long = 35000
Private const DEFAULT_REG_MBEANS As Boolean = True
Private const DEFAULT_MIN_IDLE As Int = 10
Private const DEFAULT_MAX_SIZE As Int = 10
Private const DEFAULT_KEEP_ALIVE_TIME As Long = 300000
Dim rawValue As String
Dim processedKeys As List
processedKeys.Initialize
Dim maxSize As Int ' Temporary variable for the calculated maximum size
' 1. MaxLifetime (Long) - Key: maxlifetime
Dim maxLife As Long
If properties.ContainsKey("maxlifetime") Then
rawValue = properties.Get("maxlifetime").As(String).Trim
maxLife = rawValue
poolJO.RunMethod("setMaxLifetime", Array As Object(maxLife))
Log($"Ponemos MaxLifetime: ${maxLife}"$)
Else
maxLife = DEFAULT_MAX_LIFE
poolJO.RunMethod("setMaxLifetime", Array As Object(maxLife))
Log($"Ponemos (LONG, Default): MaxLifetime, ${DEFAULT_MAX_LIFE}"$)
End If
processedKeys.Add("maxlifetime")
MaxLifeConfig = maxLife ' Store the config value
' 2. ConnectionTimeout (Long) - Key: connectiontimeout
Dim connTimeout As Long
If properties.ContainsKey("connectiontimeout") Then
rawValue = properties.Get("connectiontimeout").As(String).Trim
connTimeout = rawValue
poolJO.RunMethod("setConnectionTimeout", Array As Object(connTimeout))
Log($"Ponemos ConnectionTimeout: ${connTimeout}"$)
Else
connTimeout = DEFAULT_CONN_TIMEOUT
poolJO.RunMethod("setConnectionTimeout", Array As Object(connTimeout))
Log($"Ponemos ConnectionTimeout: ${DEFAULT_CONN_TIMEOUT}"$)
End If
processedKeys.Add("connectiontimeout")
ConnTimeoutConfig = connTimeout ' Store the config value
' 3. LeakDetectionThreshold (Long) - Key: leakdetectionthreshold
Dim leakThr As Long
If properties.ContainsKey("leakdetectionthreshold") Then
rawValue = properties.Get("leakdetectionthreshold").As(String).Trim
leakThr = rawValue
poolJO.RunMethod("setLeakDetectionThreshold", Array As Object(leakThr))
Log($"Ponemos LeakDetectionThreshold: ${leakThr}"$)
Else
leakThr = DEFAULT_LEAK_THR
poolJO.RunMethod("setLeakDetectionThreshold", Array As Object(leakThr))
Log($"Ponemos (LONG, Default): LeakDetectionThreshold, ${DEFAULT_LEAK_THR}"$)
End If
processedKeys.Add("leakdetectionthreshold")
LeakDetectionThresholdConfig = leakThr ' Store the config value
' 4. MaximumPoolSize (Int) - Key: maximumpoolsize
If properties.ContainsKey("maximumpoolsize") Then
rawValue = properties.Get("maximumpoolsize").As(String).Trim
maxSize = rawValue.As(Int)
poolJO.RunMethod("setMaximumPoolSize", Array As Object(maxSize))
Log($"Ponemos MaximumPoolSize: ${maxSize}"$)
Else
maxSize = DEFAULT_MAX_SIZE
poolJO.RunMethod("setMaximumPoolSize", Array As Object(DEFAULT_MAX_SIZE))
Log($"Ponemos MaximumPoolSize: ${DEFAULT_MAX_SIZE}"$)
End If
processedKeys.Add("maximumpoolsize")
PoolSizeConfig = maxSize ' Store the config value
' 5. MinimumIdle (Int) - Key: minimumidle
Dim minIdleFinal As Int
If properties.ContainsKey("minimumidle") Then
rawValue = properties.Get("minimumidle").As(String).Trim
minIdleFinal = rawValue.As(Int)
poolJO.RunMethod("setMinimumIdle", Array As Object(minIdleFinal))
Log($"Ponemos MinimumIdle: ${minIdleFinal}"$)
Else
' APPLY FIXED POOL AXIOM: MinimumIdle = MaximumPoolSize (maxSize)
minIdleFinal = maxSize
poolJO.RunMethod("setMinimumIdle", Array As Object(minIdleFinal))
Log($"Ponemos MinimumIdle: ${minIdleFinal} (Igual a MaximumPoolSize)"$)
End If
processedKeys.Add("minimumidle")
MinIdleConfig = minIdleFinal ' Store the config value
' 6. RegisterMbeans (Boolean) - Key: registermbeans
If properties.ContainsKey("registermbeans") Then
Dim regMbeans As Boolean = properties.Get("registermbeans")
poolJO.RunMethod("setRegisterMbeans", Array As Object(regMbeans))
Log($"Ponemos RegisterMbeans: ${regMbeans}"$)
Else
poolJO.RunMethod("setRegisterMbeans", Array As Object(DEFAULT_REG_MBEANS))
Log($"Ponemos RegisterMbeans: ${DEFAULT_REG_MBEANS}"$)
End If
processedKeys.Add("registermbeans")
' 7. KeepaliveTime (Long) - Key: keepalivetime
Dim keepAlive As Long
If properties.ContainsKey("keepalivetime") Then
rawValue = properties.Get("keepalivetime").As(String).Trim
keepAlive = rawValue
' The minimum accepted value is 30,000 ms [4].
If keepAlive < 30000 Then keepAlive = 30000
poolJO.RunMethod("setKeepaliveTime", Array As Object(keepAlive))
Log($"Ponemos (LONG, Config): KeepaliveTime, ${keepAlive}"$)
Else
keepAlive = DEFAULT_KEEP_ALIVE_TIME
poolJO.RunMethod("setKeepaliveTime", Array As Object(keepAlive))
Log($"Ponemos KeepaliveTime: ${DEFAULT_KEEP_ALIVE_TIME} (50 minutes)"$)
End If
processedKeys.Add("keepalivetime")
KeepAliveTimeConfig = keepAlive ' Store the config value
' Process remaining properties
Dim intValue As Int
Dim booleanValue As Boolean
Dim variableType As String
For Each k As String In properties.Keys
If processedKeys.IndexOf(k) = -1 Then
If poolProperties.ContainsKey(k) Then
variableType = poolProperties.Get(k)
Dim valueToUse As String = properties.Get(k)
If variableType = "string" Then
valueToUse = valueToUse.Trim
End If
Dim dynamicMethodName As String = $"set${k}"$
Try
If variableType = "int" Then
intValue = valueToUse
poolJO.RunMethod(dynamicMethodName, Array As Object(intValue))
Else If variableType = "string" Then
poolJO.RunMethod(dynamicMethodName, Array As Object(valueToUse))
Else If variableType = "boolean" Then
booleanValue = valueToUse
GetPoolJO.RunMethod(dynamicMethodName, Array As Object(booleanValue))
Else
Log($"Connection pool property ${k} has unsupported variable type of ${variableType}"$)
End If
Log($"Ponemos (Remaining): ${k}, ${properties.Get(k)}"$)
Catch
Log($"Warning (Remaining): Method: ${dynamicMethodName} not matched or type mismatch for property ${k}. Error: ${LastException.Message}"$)
End Try
Else
Log($"Warning: Property ${k} not supported"$)
End If
End If
Next
End Sub
' Applies specific JDBC Driver properties (e.g., MySQL caching)
' These properties are applied using addDataSourceProperty, which is not supported in standard SetProperties.
Public Sub SetDriverProperties(properties As Map)
' properties is the map extracted by RDCConnector.LoadDriverProperties (e.g., driver.mysql.*).
Dim value As Object
For Each k As String In properties.Keys
value = properties.Get(k)
' Use addDataSourceProperty to configure the Driver, not the Pool.
poolJO.RunMethod("addDataSourceProperty", Array As Object(k, value))
Log($"HikariCP: Driver Property [${k}] added with value: ${value}"$)
Next
End Sub
'Check if JDBC URL is supported
Public Sub SupportUrl(jdbcUrl As String) As Boolean
If jdbcUrl.StartsWith("jdbc:sqlite") Then
Log("Error: Hikari connection pool does not support SQLite")
Return False
End If
Return True
End Sub
'See if pool's java object supports standard getConnection method
Public Sub IsStandardConnection As Boolean
Return True
End Sub
'Get pool's underlying java object. Used if IsStandardConnection is True
Public Sub GetPoolJO As JavaObject
Return poolJO
End Sub
'Retrieves a connection from the pool. Make sure to close the connection when you are done with it.
Public Sub GetConnection As SQL
Dim newSQL As SQL
Dim jo As JavaObject = newSQL
jo.SetField("connection", poolJO.RunMethod("getConnection", Null))
Return newSQL
End Sub
'Closes all unused pooled connections.
Public Sub ClosePool
If poolJO.IsInitialized Then
poolJO.RunMethod("close", Null)
End If
End Sub
' WARNING: This subroutine translates internal HikariCP method names
' to the generic keys (e.g., BusyConnections) that RDCConnector expects.
' It now includes the STATIC CONFIGURATION saved in the class variables.
Public Sub GetStats As Map
Dim stats As Map
stats.Initialize
' 1. ADD STATIC PROPERTIES (STORED CONFIGURATION)
stats.Put("MaxPoolSize", PoolSizeConfig)
stats.Put("MinPoolSize", MinIdleConfig)
stats.Put("MaxLifetime", MaxLifeConfig)
stats.Put("ConnectionTimeout", ConnTimeoutConfig)
stats.Put("LeakDetectionThreshold", LeakDetectionThresholdConfig)
stats.Put("KeepaliveTime", KeepAliveTimeConfig)
' Note: You can add other relevant static properties here.
' 2. GET RUNTIME METRICS (JMX/MBean)
If poolJO.IsInitialized Then
Try
Dim poolMBean As JavaObject = poolJO.RunMethod("getHikariPoolMXBean", Null)
Dim busyConn As Object = poolMBean.RunMethod("getActiveConnections", Null)
stats.Put("BusyConnections", busyConn)
Dim idleConn As Object = poolMBean.RunMethod("getIdleConnections", Null)
stats.Put("IdleConnections", idleConn)
Dim activeCount As Int = busyConn
Dim idleCount As Int = idleConn
stats.Put("TotalConnections", activeCount + idleCount)
Dim awaitingConn As Object = poolMBean.RunMethod("getThreadsAwaitingConnection", Null)
stats.Put("ThreadsAwaitingConnection", awaitingConn)
Catch
Dim ErrorMsg As String = "HikariCP.GetStats: Error getting dynamic metrics from MBean: " & LastException.Message
Log(ErrorMsg)
stats.Put("Error_Runtime", ErrorMsg)
End Try
Else
stats.Put("Error", "Pool JO not initialized")
End If
Return stats
End Sub