diff --git a/org.adempiere.base/src/org/compiere/db/StatementProxy.java b/org.adempiere.base/src/org/compiere/db/StatementProxy.java index 9fd61a7d8e..2da803be93 100644 --- a/org.adempiere.base/src/org/compiere/db/StatementProxy.java +++ b/org.adempiere.base/src/org/compiere/db/StatementProxy.java @@ -25,6 +25,7 @@ import javax.sql.RowSet; import org.adempiere.exceptions.DBException; +import org.compiere.model.SystemProperties; import org.compiere.util.CCachedRowSet; import org.compiere.util.CLogger; import org.compiere.util.CStatementVO; @@ -133,8 +134,11 @@ public Object invoke(Object obj, Method method, Object[] args) } } Method m = p_stmt.getClass().getMethod(name, method.getParameterTypes()); + String nullTrxName = null; try { + if (SystemProperties.isTraceNullTrxConnection() && p_vo.getTrxName() == null) + nullTrxName = Trx.registerNullTrx(); return m.invoke(p_stmt, args); } catch (InvocationTargetException e) @@ -143,6 +147,8 @@ public Object invoke(Object obj, Method method, Object[] args) } finally { + if (nullTrxName != null && p_vo.getTrxName() == null) + Trx.unregisterNullTrx(nullTrxName); if (log.isLoggable(Level.FINE) && logSql != null && logOperation != null) { log.fine((DisplayType.getDateFormat(DisplayType.DateTime)).format(new Date(System.currentTimeMillis()))+","+logOperation+","+logSql+","+(p_vo.getTrxName() != null ? p_vo.getTrxName() : "")+" (end)"); diff --git a/org.adempiere.base/src/org/compiere/model/SystemProperties.java b/org.adempiere.base/src/org/compiere/model/SystemProperties.java index d8c01ece04..2f2f396f19 100644 --- a/org.adempiere.base/src/org/compiere/model/SystemProperties.java +++ b/org.adempiere.base/src/org/compiere/model/SystemProperties.java @@ -54,6 +54,7 @@ public class SystemProperties { private static final String PropertyFile = "PropertyFile"; private static final String PropertyHomeFile = "PropertyHomeFile"; private static final String TestOCI = "TestOCI"; + private static final String TRACE_NULL_TRX_CONNECTION = "TRACE_NULL_TRX_CONNECTION"; private static final String ZK_THEME = MSysConfig.ZK_THEME; private static final String ZkUnitTest = "ZkUnitTest"; @@ -257,4 +258,14 @@ public static boolean isZkUnitTest() { return "true".equals(System.getProperty(ZkUnitTest)); } + /** + * TRACE_NULL_TRX_CONNECTION=true to allow tracing null transactions on idempiereMonitor + * WARNING! this setting can have a big performance impact, it is disabled by default + * use it with care in production just temporarily to trace problematic connection slowness or leaks + * @return + */ + public static boolean isTraceNullTrxConnection() { + return "true".equals(System.getProperty(TRACE_NULL_TRX_CONNECTION)); + } + } diff --git a/org.adempiere.base/src/org/compiere/util/Trx.java b/org.adempiere.base/src/org/compiere/util/Trx.java index 71dea2a440..3e8cba722d 100644 --- a/org.adempiere.base/src/org/compiere/util/Trx.java +++ b/org.adempiere.base/src/org/compiere/util/Trx.java @@ -34,6 +34,7 @@ import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.DBException; import org.compiere.Adempiere; +import org.compiere.db.StatementProxy; import org.compiere.model.MSysConfig; import org.compiere.model.PO; @@ -877,4 +878,40 @@ public void run() { }, 2, TimeUnit.SECONDS); } } + + /** + * Register a null trx + * @return + */ + public static String registerNullTrx() { + String nullTrxName = "NullTrx_" + UUID.randomUUID().toString(); + Trx nullTrx = new Trx(nullTrxName); + nullTrx.trace = new Exception(); + nullTrx.m_startTime = System.currentTimeMillis(); + String displayName = null; + StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); + Optional stackName = walker.walk(frames -> frames.map( + stackFrame -> stackFrame.getClassName() + "." + + stackFrame.getMethodName() + ":" + + stackFrame.getLineNumber()) + .filter(f -> ! (f.startsWith(Trx.class.getName() + ".") || f.startsWith(StatementProxy.class.getName() + ".") || f.startsWith("jdk.proxy") || f.startsWith("org.compiere.util.DB."))) + .findFirst()); + displayName = (stackName.orElse(null)); + if (displayName != null) + nullTrx.setDisplayName(displayName); + s_cache.put(nullTrxName, nullTrx); + return nullTrxName; + } + + /** + * Unregister a null trx + * @param nullTrxName + */ + public static void unregisterNullTrx(String nullTrxName) { + Trx nullTrx = s_cache.get(nullTrxName); + nullTrx.setDisplayName(null); + nullTrx.trace = null; + s_cache.remove(nullTrxName); + } + } // Trx