Lucee creates duplicate datasource pools - jmx storm with fusion Reactor

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

Reproduction: GitHub - deanmaunder/lucee-pool-shadow · GitHub

1 Like

excellent bug report!

Firstly, I’ve fixed the pool stats reporting to show this problem

measure first, cut twice as they say

Fixed

https://luceeserver.atlassian.net/browse/LDEV-6296

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.

1 Like

Amazing! Thank you.

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.

Hi @felix

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.

Hi Felix, I filed this as LDEV-6562, we’ll follow up there.

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.