Class TransactionLifecycleMonitor
java.lang.Object
org.broadleafcommerce.common.persistence.transaction.TransactionLifecycleMonitor
- All Implemented Interfaces:
EventListener,BroadleafApplicationListener<TransactionLifecycleEvent>,SqlStatementLoggable,org.springframework.beans.factory.Aware,org.springframework.context.ApplicationContextAware,org.springframework.context.ApplicationListener<TransactionLifecycleEvent>,org.springframework.context.Lifecycle,org.springframework.context.Phased,org.springframework.context.SmartLifecycle
@Component("blTransactionLifecycleMonitor")
public class TransactionLifecycleMonitor
extends Object
implements BroadleafApplicationListener<TransactionLifecycleEvent>, org.springframework.context.ApplicationContextAware, org.springframework.context.SmartLifecycle, SqlStatementLoggable
Responsible for most of the work related to monitoring and logging transaction state. This class is notified via a
LifecycleAwareJpaTransactionManager when a key TransactionLifecycleEvent occurs. Based on this information,
this monitor attempts to detect transaction fault states and report them as part of SUPPORT level logging (SupportLogger).
Currently, the monitor attempts to recognize the following fault states:
- TRANSACTIONMONITOR(1) - Leaked Resource: The transaction thread is not considered stuck, but no queries have been issued against the
tracked EntityManager in
loggingReportingLagThreshold. This could indicate the thread has moved on and the transaction was not correctly finalized. - TRANSACTIONMONITOR(2) - Long Running Transaction: The transaction thread is not considered stuck, but the transaction info has been alive
for
loggingThreshold. This is not necessarily a fault, but may warrant review. Long running or stuck transactions can account for long resource lock times. Note, this case can later become a stuck thread if thestuckThresholdhas not elapsed. - TRANSACTIONMONITOR(3) - Exception During Finalization: The transaction is attempting to finalize normally, but has experienced an exception during that finalization attempt. This could indicate a problem communicating with the backing database and may result in orphaned resources in the data tier.
- TRANSACTIONMONITOR(4) - Stuck Thread: The transaction thread is considered stuck with a transaction info alive
for
loggingThresholdand no change in thread stack forstuckThreshold. Long running or stuck transactions can account for long resource lock times. - TRANSACTIONMONITOR(5) - Alive At Shutdown: The transaction info is considered active at the time of container shutdown. This is not necessarily a fault, but may warrant review. Items at shutdown may simply be waiting for final closure. However, subsequent system kill calls (if applicable) could prematurely exit these connections and they may be interesting for review.
LifecycleAwareJpaTransactionManager via LifecycleAwareJpaTransactionManager.isEnabled().
If no enabled transaction manager is found, the monitor is fully disabled.
The loggingThreshold variable can be controlled via the 'log.transaction.lifecycle.logging.threshold.millis' property.
The default value is 600000.
The stuckThreshold variable can be controlled via the 'log.transaction.lifecycle.stuck.threshold.millis' property.
The default values is 300000.
The loggingPollingResolution variable can be controlled via the 'log.transaction.lifecycle.logging.polling.resolution.millis' property.
The default value is 30000.
The loggingReportingLagThreshold variable can be controlled via the 'log.transaction.lifecycle.reporting.lag.threshold.millis' property.
The default value is 300000.
The countMax variable can be controlled via the 'log.transaction.lifecycle.info.count.max' property. The default value is 5000.
The useCompression variable can be controlled via the 'log.transaction.lifecycle.use.compression' property.
The default value is true.
The abbreviateStatements variable can be controlled via the 'log.transaction.lifecycle.abbreviate.statements' property.
The default value is true.
The abbreviateStatementsLength variable can be controlled via the 'log.transaction.lifecycle.abbreviate.statements.length' property.
The default value is 200.
The decompressStatementForLog variable can be controlled via the 'log.transaction.lifecycle.decompress.statement' property.
The default value is false.
The maxQueryListSize variable can be controlled via the 'log.transaction.lifecycle.query.list.max.size' property.
The default value is 100.
This monitor is intended for temporary usage as part of transaction related debugging efforts. From both a heap utilization
and performance standpoint, this monitor is suitable for production with default settings. Performance impacts should be minor and will generally
be related to creation of the intial call stack, compression of that call stack and subsequent compression of sql statements.
Compression can be turned off via the 'log.transaction.lifecycle.use.compression' for a minor performance benefit at the cost
of additional heap usage. Heap usage can be capped via the 'log.transaction.lifecycle.info.count.max' property, but if the max
happens to be reached, new transactions will not be monitored. This is more a safety net feature than anything and it's not
anticipated that the default max count will be reached during normal usage. Set 'log.transaction.lifecycle.info.count.max' to
-1 to uncap growth.
To avoid overly large disk usage in logs (in case many log statements are emitted with many queries per), the system will truncate
statements to a default length of 200 characters and leave those statements compressed in the logs (if they were configured to be
compressed). See the abbreviateStatements and abbreviateStatementsLength variable to change this behavior.
To view the decompressed version, you'll need to pass the compressed string from the log line to the
decompressLogLine(String) method to see the decompressed version. This can be easily done by writing a simple
main program that instantiates TransactionLifecycleMonitor and calls this method. To emit the uncompressed version of the
queries instead to the logs, change the decompressStatementForLog property value. Finally, by default, the system
will only remember and emit the last 100 queries in the transaction. This value can be tweaked via the maxQueryListSize
variable. Set this value to -1 to uncap the expansion of the query list.- Author:
- Jeff Fischer
-
Field Summary
FieldsModifier and TypeFieldDescriptionprotected booleanprotected intprotected intprotected booleanprotected booleanprotected Map<Integer,TransactionInfo> protected booleanprotected longprotected longprotected longprotected intprotected List<TransactionInfoCustomModifier>protected longprotected Timerprotected List<LifecycleAwareJpaTransactionManager>protected booleanFields inherited from interface org.springframework.context.SmartLifecycle
DEFAULT_PHASE -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected StackTraceElement[]compileThreadInformation(long currentTime, TransactionInfo info, Thread thread) decompressLogLine(String compressedFromLog) protected booleandetectExpiry(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info, StackTraceElement[] elements) protected booleandetectLeakage(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info) protected voidintprotected TransactionInfoprotected jakarta.persistence.EntityManagergetEntityManagerFromTransactionObject(Object transactionObject) static TransactionLifecycleMonitorlonglonglongintgetPhase()longprotected voidvoidinit()booleanIndicates if this application listener should be run in a background thread if a TaskExecutor is configured.protected booleanbooleanbooleanbooleanvoidvoidvoidsetApplicationContext(org.springframework.context.ApplicationContext applicationContext) voidsetCountMax(int countMax) voidsetLoggingPollingResolution(long loggingPollingResolution) voidsetLoggingReportingLagThreshold(long loggingReportingLagThreshold) voidsetLoggingThreshold(long loggingThreshold) voidsetStuckThreshold(long stuckThreshold) voidsetUseCompression(boolean useCompression) voidstart()voidstop()voidMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.springframework.context.ApplicationListener
supportsAsyncExecution
-
Field Details
-
transactionManagers
-
modifiers
-
loggingThreshold
@Value("${log.transaction.lifecycle.logging.threshold.millis:600000}") protected long loggingThreshold -
stuckThreshold
@Value("${log.transaction.lifecycle.stuck.threshold.millis:300000}") protected long stuckThreshold -
loggingPollingResolution
@Value("${log.transaction.lifecycle.logging.polling.resolution.millis:30000}") protected long loggingPollingResolution -
loggingReportingLagThreshold
@Value("${log.transaction.lifecycle.reporting.lag.threshold.millis:300000}") protected long loggingReportingLagThreshold -
countMax
@Value("${log.transaction.lifecycle.info.count.max:5000}") protected int countMax -
useCompression
@Value("${log.transaction.lifecycle.use.compression:true}") protected boolean useCompression -
abbreviateStatements
@Value("${log.transaction.lifecycle.abbreviate.statements:true}") protected boolean abbreviateStatements -
abbreviateStatementsLength
@Value("${log.transaction.lifecycle.abbreviate.statements.length:200}") protected int abbreviateStatementsLength -
decompressStatementForLog
@Value("${log.transaction.lifecycle.decompress.statement:false}") protected boolean decompressStatementForLog -
maxQueryListSize
@Value("${log.transaction.lifecycle.query.list.max.size:100}") protected int maxQueryListSize -
infos
-
isStarted
protected boolean isStarted -
enabled
protected boolean enabled -
timer
-
-
Constructor Details
-
TransactionLifecycleMonitor
public TransactionLifecycleMonitor()
-
-
Method Details
-
getInstance
-
init
@PostConstruct public void init() -
setApplicationContext
public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws org.springframework.beans.BeansException - Specified by:
setApplicationContextin interfaceorg.springframework.context.ApplicationContextAware- Throws:
org.springframework.beans.BeansException
-
isAutoStartup
public boolean isAutoStartup()- Specified by:
isAutoStartupin interfaceorg.springframework.context.SmartLifecycle
-
stop
- Specified by:
stopin interfaceorg.springframework.context.SmartLifecycle
-
start
public void start()- Specified by:
startin interfaceorg.springframework.context.Lifecycle
-
stop
public void stop()- Specified by:
stopin interfaceorg.springframework.context.Lifecycle
-
isRunning
public boolean isRunning()- Specified by:
isRunningin interfaceorg.springframework.context.Lifecycle
-
getPhase
public int getPhase()- Specified by:
getPhasein interfaceorg.springframework.context.Phased- Specified by:
getPhasein interfaceorg.springframework.context.SmartLifecycle
-
isAsynchronous
public boolean isAsynchronous()Description copied from interface:BroadleafApplicationListenerIndicates if this application listener should be run in a background thread if a TaskExecutor is configured. If no TaskExecutor is configure with a bean name of "blApplicationEventMulticastTaskExecutor" then this will be run synchronously regardless. Generally, this should return false as the default implementation has no reliability guarantees associated with Asynch processing. However, for convenience this allows asynch processing for situations that don't require guaranteed processing. For example, publishing statistics or log events in a background thread would be candidates for background processing. Handling important database updates would not.- Specified by:
isAsynchronousin interfaceBroadleafApplicationListener<TransactionLifecycleEvent>- Returns:
- See Also:
-
onApplicationEvent
- Specified by:
onApplicationEventin interfaceorg.springframework.context.ApplicationListener<TransactionLifecycleEvent>
-
log
- Specified by:
login interfaceSqlStatementLoggable
-
decompressLogLine
- Specified by:
decompressLogLinein interfaceSqlStatementLoggable- Throws:
IOException
-
getLoggingThreshold
public long getLoggingThreshold() -
setLoggingThreshold
public void setLoggingThreshold(long loggingThreshold) -
getStuckThreshold
public long getStuckThreshold() -
setStuckThreshold
public void setStuckThreshold(long stuckThreshold) -
getLoggingPollingResolution
public long getLoggingPollingResolution() -
setLoggingPollingResolution
public void setLoggingPollingResolution(long loggingPollingResolution) -
getLoggingReportingLagThreshold
public long getLoggingReportingLagThreshold() -
setLoggingReportingLagThreshold
public void setLoggingReportingLagThreshold(long loggingReportingLagThreshold) -
getCountMax
public int getCountMax() -
setCountMax
public void setCountMax(int countMax) -
isUseCompression
public boolean isUseCompression() -
setUseCompression
public void setUseCompression(boolean useCompression) -
groomInProgressTransactionInfos
protected void groomInProgressTransactionInfos() -
detectLeakage
protected boolean detectLeakage(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info) -
detectExpiry
protected boolean detectExpiry(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info, StackTraceElement[] elements) -
compileThreadInformation
protected StackTraceElement[] compileThreadInformation(long currentTime, TransactionInfo info, Thread thread) -
finalizeTransaction
-
getEntityManagerFromTransactionObject
protected jakarta.persistence.EntityManager getEntityManagerFromTransactionObject(Object transactionObject) -
getCurrentTransactionInfo
-
isAtLeastOneTransactionManagerEnabled
protected boolean isAtLeastOneTransactionManagerEnabled()
-