When a <cfquery> tag specifies username and password attributes that are identical to the credentials already embedded in the server-level datasource, Lucee creates a second, separate connection pool for the same datasource instead of reusing the registered server-level pool.
Under concurrent load the duplicate pool is regularly evicted (60-second idle timeout hardcoded in ConfigImpl.POOL_MAX_IDLE), then recreated — triggering the jmxRegister retry storm described in POOL-393 on every eviction/burst cycle. Commons-pool2 library is updated in Lucee 7 so this impact isn’t as high as seen in Lucee 6
Standalone reproduction harness exercises 500 datasources with a sequential warmup followed by a barrier-synced concurrent matching-cred burst. AMD 9950x3d, 32 hardware threads.
Branch
commons-pool2
Warmup
Burst
Burst/Warmup
Pool delta
MBean delta
6.2 pre-fix (6.2.7.12)
2.9.0
2255 ms
2413 ms
1.07× slower
+495
+500
6.2 post-fix (6.2.7.14)
2.9.0
2271 ms
53 ms
43× faster
+0
+0
7.0 pre-fix (7.0.4.29)
2.12.1
1634 ms
2015 ms
1.23× slower
+500
+500
7.0 post-fix (7.0.4.30)
2.12.1
1559 ms
317 ms
5× faster
+0
+0
7.1 pre-fix (7.1.0.110)
2.12.1
2059 ms
6169 ms (median, 2.2–40s range)
3× slower
+500
+500
7.1 post-fix (7.1.0.111)
2.12.1
2528 ms
87 ms
29× faster
+0
+0
Post-fix figures are medians of 3 consistent runs. Pre-fix 7.1 burst is median of 5 runs because of massive run-to-run variance (range column).
Key signals:
Pool/MBean delta goes from +495–500 (every burst created N shadow pools) to deterministic +0 across all branches. Trigger is gone.
Burst time flips from slower-than-sequential (JMX MBeanServer serialisation under concurrent register attempts) to faster-than-sequential (actual parallel speedup, because there’s nothing being registered).
Storm magnitude on pre-fix 7.1 spans 18× run-to-run (2.2s–40s); that variance is gone post-fix.
Connection budget for an affected datasource is now deterministic at the configured connectionLimit instead of sometimes-doubling.
The createId() normalisation removes the trigger uniformly across all three branches regardless of commons-pool2 version. POOL-393 (commons-pool2 2.12.0+) was helping but never enough to mask the underlying serialisation; the only durable fix is not creating the duplicate pool in the first place.
Upgraded from 6.2.6.19 to 6.2.7.16 and LDEV-6296 is working for us: getSystemMetrics().datasourceConnections shows one pool per datasource/user (20–29 at any time), where before our cfquery calls passing matching credentials created shadow pools.
However, pool objects are still leaking. About 13 hours after restart on one server:
Registered pools (getSystemMetrics): 26
Live lucee.runtime.config.DatasourceConnPool (jcmd GC.class_histogram): ~2,800, growing ~85/hour
:type=GenericObjectPool, MBeans in the platform MBeanServer: 2,862
The registered set changes as low-traffic sites go idle and come back, so it looks like pools evicted by the POOL_MAX_IDLE sweep are dropped from the config without close() being called. Their MBeans are never unregistered, which keeps the pools reachable. Each recreation also takes the MBeanServer write lock, the same lock involved in the original storm.
Setup: Lucee 6.2.7.16, Java 21, Tomcat via AJP, ~25 sites per instance with one server-level MariaDB datasource each.
Thanks for confirming LDEV-6296 on 6.2.7.16, good to hear the shadow pools are gone and getSystemMetrics looks right.
The leak you describe after idle eviction is a separate problem. Your numbers (26 registered vs ~2,800 live DatasourceConnPool / ~2,862 GenericObjectPool MBeans, growing ~85/hour) and the POOL_MAX_IDLE / missing close() theory are very useful. We’ll dig into that path and file a ticket; We will report back here with the LDEV once we have one.
We had a closer look at the leak you’re seeing on 6.2.7.16. That version never closes pools that were dropped after sitting idle, so each dropped pool leaves its DatasourceConnPool and its MBean behind. That matches the steady growth you see. This was fixed in LDEV-5967, first released in 6.2.8.10: idle pools are now closed properly and their MBeans unregistered.
While digging, we also found a race where two requests arriving at the same moment could each create a pool for the same datasource. That’s fixed under LDEV-6562 for 7.1.
Could you retest on 6.2.8.x or newer, ideally the current 6.2.9.4, and check whether the DatasourceConnPool and MBean counts stay flat? If they do, we’ll close LDEV-6562’s original report as a duplicate of LDEV-5967.