About a year ago I asked a question about errors I was getting in an app, that indicated a possible race condition:
Possible race condition creating Structs in ColdFusion
A year on, I'm still having issues with this and other apps where the same technique is employed and it seems that frankly, you cannot create and update a struct reliably in the same request. This of course seems ludicrous, so I must be doing something wrong - but I'd appreciate some help.
Here's an explanation of what's going on:
- I have a function, instantiated in the Application scope, that accepts a comma separated list of IDs as a parameter.
- The function hits the database requesting all the records with matching IDs
- We then loop through the recordset, and create a locally scoped struct, dynamically named using the ID of the record, e.g.
Local.myStruct['item_' & myrecordset.ID]
- Finally we add data to this struct.
The issue is that very intermittently, the code will error on the last step, stating that the dynamically named struct that was created literally two lines previous, does not exist... Interestingly, the line of code in question is updating the struct, and so if it didn't exist, I'd expect it to simply create it - I wonder if using Array notation to set the value of dynamically named variables somehow behaves differently than using dot notation?
So, here's some code:
<cffunction name="GetPrices" access="public" returntype="struct">
<cfargument name="ItemID" type="string" required="true" />
<cfargument name="PriceBand" type="string" default="1" />
<cfset var Local = {} />
<!--- Query the database for the part details --->
<cfquery name="Local.GetParts" datasource="#this.sDSN#">
SELECT TOP 200
[ItemID]
, [Price1]
, [Price2]
, [Price3]
FROM [Items]
WHERE 1 = 1
<cfif ListLen(Arguments.ItemID) GT 1>
AND ItemID IN (<cfqueryparam cfsqltype="cf_sql_integer" list="yes" value="#Arguments.ItemID#">)
<cfelse>
AND itemID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.ItemID#">
</cfif>
</cfquery>
<cfloop query="Local.GetParts">
<cfset Local.ReturnStruct['item_' & Local.GetParts.ItemID] = {} />
<cfset Local.ReturnStruct['item_' & Local.GetParts.itemID].CurrentPrice = Local.GetParts['price' & Arguments.PriceBand] />
</cfloop>
<cfreturn Local.ReturnStruct />
</cffunction>
The function would be called thus:
Variables.GetPrices = Application.com.Items.GetPrices(
ItemID = '8263,1996,324686,32,12746,297807,1763,37568,2359782,321,3525,563466,323'
, PriceBand = 2
)
As you can see, the function is varscoped, and in any case we're running CF10 where I'm lead to believe that an implicit Local scope is thread safe in a function. Var scoping is where we landed last year on this problem, but alas the problem persists. Can anyone offer any other suggestions?
Thanks
EDIT: As requested, here's an exmple of the errors I'm receiving:
Element item_61284 is undefined in a CFML structure referenced as part of an expression.
The item ID is different every time. It's probably worth noting that the code posted has been manipulated slightly for the purposes of the question - so the stacktrace below may show bits and pieces that can't be attributed to the code above.
I'm confident that nothing removed from the production code is relevant to the issue, as these symptoms occurr across tens of functions in multiple apps, wherever the same technique is used.
coldfusion.runtime.UndefinedElementException: Element item_61284 is undefined in a CFML structure referenced as part of an expression. at coldfusion.runtime.CfJspPage.ArrayGetAt(CfJspPage.java:974) at coldfusion.runtime.CfJspPage._arrayGetAt(CfJspPage.java:985) at coldfusion.runtime.CfJspPage._arrayGetAt(CfJspPage.java:980) at cfparts2ecfc1041715109$funcREDACTED.runFunction(REDACTED.cfc:665) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472) at coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:405) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:321) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:518) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:660) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:469) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2373) at REDACTEDcfm969331649.runPage(REDACTED.cfm:43) at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:244) at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:444) at coldfusion.runtime.CfJspPage._emptyTcfTag(CfJspPage.java:2799) at REDACTEDcfm522717774.runPage(REDACTED.cfm:83) at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:244) at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:444) at coldfusion.runtime.CfJspPage._emptyTcfTag(CfJspPage.java:2799) at cfapplication2ecfc1345221948$funcONREQUEST.runFunction(REDACTED\Application.cfc:573) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472) at coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:405) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:321) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:220) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:655) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:444) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:414) at coldfusion.runtime.AppEventInvoker.invoke(AppEventInvoker.java:108) at coldfusion.runtime.AppEventInvoker.onRequest(AppEventInvoker.java:300) at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:424) at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48) at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40) at coldfusion.filter.PathFilter.invoke(PathFilter.java:112) at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94) at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46) at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38) at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22) at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62) at coldfusion.CfmServlet.service(CfmServlet.java:219) at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42) at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at sun.reflect.GeneratedMethodAccessor63.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter(WrappedFilterChain.java:97) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doNext(FusionReactorRequestHandler.java:437) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doHttpServletRequest(FusionReactorRequestHandler.java:311) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doFusionRequest(FusionReactorRequestHandler.java:192) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.handle(FusionReactorRequestHandler.java:472) at com.intergral.fusionreactor.j2ee.filter.FusionReactorCoreFilter.doFilter(FusionReactorCoreFilter.java:36) at sun.reflect.GeneratedMethodAccessor61.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter(WrappedFilterChain.java:79) at sun.reflect.GeneratedMethodAccessor60.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at com.intergral.fusionreactor.agent.filter.FusionReactorStaticFilter.doFilter(FusionReactorStaticFilter.java:53) at com.intergral.fusionreactor.agent.pointcuts.NewFilterChainPointCut$1.invoke(NewFilterChainPointCut.java:41) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:928) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:414) at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:204) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:539) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:298) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)