diff --git a/CHANGELOG.md b/CHANGELOG.md index fdcea70..7d4dd71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes are documented here. This project follows Keep a Changelog a ## [Unreleased] ### Fixed +- Only one runtime attributes per JVM: starting attribution on a newer instance (a hot swap, or repeated server starts in tests) stops the previous one, so old instances are never pinned by a forgotten listener and flusher. - Each table has its own icon (the app icon is now `storage`), so nav entries are distinguishable and do not repeat icons used elsewhere. - Home tables keep at most four columns with plain headers (readable at every width): secondary facts such as calls and mean time, the session's query text, and a unit's kind and top table move into a second line of the first cell. diff --git a/src/main/java/com/kingsrook/qbits/dbperformance/DbPerfRuntime.java b/src/main/java/com/kingsrook/qbits/dbperformance/DbPerfRuntime.java index fd0cb34..c76b6d0 100644 --- a/src/main/java/com/kingsrook/qbits/dbperformance/DbPerfRuntime.java +++ b/src/main/java/com/kingsrook/qbits/dbperformance/DbPerfRuntime.java @@ -42,6 +42,12 @@ public class DbPerfRuntime implements QSupplementalInstanceMetaData private final DbPerfQBitConfig config; private AttributionFlusher attributionFlusher; + ///////////////////////////////////////////////////////////////////////////// + // statement listeners are JVM-wide: only one runtime attributes at a time // + // (a newer instance, e.g. after a hot swap or in tests, replaces the old) // + ///////////////////////////////////////////////////////////////////////////// + private static final AtomicReference ACTIVE_ATTRIBUTION = new AtomicReference<>(); + /******************************************************************************* ** Runtime bound to a single host instance. @@ -149,6 +155,11 @@ public class DbPerfRuntime implements QSupplementalInstanceMetaData { if(attributionFlusher == null && Boolean.TRUE.equals(config.getAttributionEnabled())) { + DbPerfRuntime previous = ACTIVE_ATTRIBUTION.getAndSet(this); + if(previous != null && previous != this) + { + previous.stopAttribution(); + } attributionFlusher = new AttributionFlusher(this); attributionFlusher.start(); } @@ -160,6 +171,7 @@ public class DbPerfRuntime implements QSupplementalInstanceMetaData *******************************************************************************/ public synchronized void stopAttribution() { + ACTIVE_ATTRIBUTION.compareAndSet(this, null); if(attributionFlusher != null) { attributionFlusher.close(); diff --git a/src/test/java/com/kingsrook/qbits/dbperformance/attribution/AttributionTest.java b/src/test/java/com/kingsrook/qbits/dbperformance/attribution/AttributionTest.java index 4999a02..17736cb 100644 --- a/src/test/java/com/kingsrook/qbits/dbperformance/attribution/AttributionTest.java +++ b/src/test/java/com/kingsrook/qbits/dbperformance/attribution/AttributionTest.java @@ -569,6 +569,25 @@ class AttributionTest } + /******************************************************************************* + ** Only one runtime attributes per JVM: starting another stops the first, so + ** repeated server starts (tests, hot swaps) never pile up listeners. + *******************************************************************************/ + @Test + void oneActiveRuntime() throws Exception + { + StatementExecutionListeners.unregister(listener); + runtime.startAttribution(); + DbPerfRuntime other = new DbPerfRuntime(runtime.getInstance(), runtime.getConfig()); + other.startAttribution(); + runtime.stopAttribution(); + assertThat(StatementExecutionListeners.isActive()).isTrue(); + other.stopAttribution(); + assertThat(StatementExecutionListeners.isActive()).isFalse(); + } + + + /******************************************************************************* ** Deliver a statement with exact counter values. *******************************************************************************/