I am doing some testing with the new AsyncManager in Coldbox 6 with ACF 2016. When trying to execute a process that acesses the database I noticed that the performance was worse. When I did a thread dump, I see that the threads are blocking each other then trying to get a database connection. Below is the code I am using to test this.
I am guessing it may have something to do with sharing database connections accross threads but am not really sure. Any ideas are appreciated. I also tested in ACF 2018 with the same results. It seems to run fine using the runAsync()
in ACF 2018.
var myExecutor = asyncManager.getExecutor("my-executor");
var futures = [
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor ),
asyncManager.newFuture( function() { return getData(); }, myExecutor )
];
var myFuture = asyncManager.all( futures );
var results = myFuture.get();
Here is the getData()
function. It does a basic select statement and has some logging to debug execution time.
private query function getData() {
var timer1 = getTickCount();
var result = queryExecute("select 1");
var execTime = (getTickCount() - timer1) * 0.001;
logger.debug("Executed getData() in #execTime# seconds");
return result;
}
Here is my debug log.
"Information","pool-10-thread-5","01/29/21","07:47:47","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 1.12 seconds ExtraInfo: "
"Information","pool-10-thread-16","01/29/21","07:47:48","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 1.644 seconds ExtraInfo: "
"Information","pool-10-thread-15","01/29/21","07:47:49","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 2.282 seconds ExtraInfo: "
"Information","pool-10-thread-14","01/29/21","07:47:49","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 2.922 seconds ExtraInfo: "
"Information","pool-10-thread-13","01/29/21","07:47:50","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 3.577 seconds ExtraInfo: "
"Information","pool-10-thread-12","01/29/21","07:47:50","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 4.234 seconds ExtraInfo: "
"Information","pool-10-thread-11","01/29/21","07:47:51","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 4.866 seconds ExtraInfo: "
"Information","pool-10-thread-10","01/29/21","07:47:52","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 5.502 seconds ExtraInfo: "
"Information","pool-10-thread-9","01/29/21","07:47:52","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 6.126 seconds ExtraInfo: "
"Information","pool-10-thread-8","01/29/21","07:47:53","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 6.782 seconds ExtraInfo: "
"Information","pool-10-thread-7","01/29/21","07:47:54","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 7.429 seconds ExtraInfo: "
"Information","pool-10-thread-6","01/29/21","07:47:54","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 8.052 seconds ExtraInfo: "
"Information","pool-10-thread-2","01/29/21","07:47:55","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 8.716 seconds ExtraInfo: "
"Information","pool-10-thread-1","01/29/21","07:47:56","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 9.356 seconds ExtraInfo: "
"Information","pool-10-thread-3","01/29/21","07:47:56","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 9.999 seconds ExtraInfo: "
"Information","pool-10-thread-4","01/29/21","07:47:57","8CD941044A9B03648817E9FBE62AABA2","handlers.TestAsyncManager Executed getData() in 10.643 seconds ExtraInfo: "
And finally when doing a thread dump, I see a bunch of entries like this one.
"pool-10-thread-1"
java.lang.Thread.State: BLOCKED
at coldfusion.server.j2ee.sql.pool.JDBCPool.checkOut(JDBCPool.java:380)
at coldfusion.server.j2ee.sql.pool.JDBCPool.requestConnection(JDBCPool.java:866)
at coldfusion.server.j2ee.sql.pool.JDBCManager.requestConnection(JDBCManager.java:125)
at coldfusion.server.j2ee.sql.JRunDataSource.getConnection(JRunDataSource.java:137)
at coldfusion.sql.CFDataSource.getConnection(CFDataSource.java:44)
at coldfusion.sql.DataSrcImpl.getCachedConnection(DataSrcImpl.java:156)
at coldfusion.sql.DataSrcImpl.getConnection(DataSrcImpl.java:110)
at coldfusion.sql.SqlImpl.execute(SqlImpl.java:363)
at coldfusion.tagext.sql.QueryTag.executeQuery(QueryTag.java:1181)
at coldfusion.tagext.sql.QueryTag.startQueryExecution(QueryTag.java:815)
at coldfusion.tagext.sql.QueryUtils.executeQuery(QueryUtils.java:72)
at coldfusion.runtime.CFPage.QueryExecute(CFPage.java:10165)
at cfTestAsyncManager2ecfc1373059746$funcGETDATA.runFunction(C:\dev\MyApp\handlers\TestAsyncManager.cfc:13)
at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:493)
at coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:426)
at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:389)
at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:95)
at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:340)
at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:235)
at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:3697)
at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:3677)
at cfTestAsyncManager2ecfc1373059746$func_CF_ANONYMOUSCLOSURE_0.runFunction(C:\dev\MyApp\handlers\TestAsyncManager.cfc:27)
at coldfusion.runtime.Closure.invoke(Closure.java:109)
at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:389)
at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:95)
at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:340)
at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:235)
at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:3697)
at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:3677)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:3267)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:3224)
at cfSupplier2ecfc867635558$funcGET.runFunction(C:\dev\MyApp\coldbox\system\async\proxies\Supplier.cfc:35)
at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:493)
at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:389)
at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:95)
at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:340)
at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:235)
at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:654)
at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:443)
at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:413)
at coldfusion.runtime.java.CFCDynamicProxy.invoke(CFCDynamicProxy.java:157)
at com.sun.proxy.$Proxy37.get(Unknown Source)
at java.base@11.0.4/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
at java.base@11.0.4/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@11.0.4/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@11.0.4/java.lang.Thread.run(Thread.java:834)