Copy link to clipboard
Copied
I am using ColdFusion 9 standard on a 32-bit version Windows Server 2008 virtual machine with 6 GB of RAM.
Since migrating an application from a physical server running CF 8 to this new virtual machine running CF 9, I have experienced a very strange behavior.
I am using the CFINVOKE tag (as a web service) to pull down data. When the web service code is wrapped in a CFLOCK, the request eventually times out with no response. It appears that the request is stuck waiting to receive the exclusive lock and the ten second window passes. If I call the web service directly, bypassing the CFLOCK, the request goes through with no problems.
Why would the combination of these thwo tags cause this problem?
Here is what the code looks like in Application.cfm:
<CFLOCK Name="DataPullIntranet" Type="exclusive" TimeOut="10">
<CFTRY>
<CFIF IsDefined("URL.ResetData") or
Len(Application.MediaTrackerCacheDate) is 0 or
DateCompare(Now(), Application.MediaTrackerCacheDate, "d") is not 0 or
Not IsDefined("Application.qHomepageNews")
>
<!--- cache frequently used queries --->
<CFINCLUDE Template="RefreshNews.cfm" />
<!--- reset cache once a day --->
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFIF>
<!--- likely that lock has timed out, initialize data sets to empty --->
<CFCATCH>
<CFSET Application.qHomepageNews = QueryNew("NewsID") />
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFCATCH>
</CFTRY>
</CFLOCK>
Here is the web service code in RefreshNews.cfm:
<!--- cache news --->
<CFINVOKE
WebService="#Application.MediaTrackerServiceURL#"
Method="GetNewsForHomepage"
ReturnVariable="Application.qHomepageNews"
>
<CFINVOKEARGUMENT Name="Domain" Value="#CGI.Server_Name#" />
</CFINVOKE>
Why would the web service time out when combined with a CFLOCK? The lock is exclusive, but the data refresh should only be triggered by the first request each day. The rest of the requests should each receive the exclusive lock one at a time, but bypass all the other code and immediately release the lock. My server is not pegged at 100%, so it does not seem like there are too many requests waiting to gain the exclusive lock.
Any ideas would be appreciated,
Michael Grove
Copy link to clipboard
Copied
What is the purpose of the lock?
Copy link to clipboard
Copied
The purpose of the lock is to prevent multiple requests from triggering the data refresh at the same time.
Here is a test case:
If 500 requests come in simultaniously at midnight, all 500 will try to trigger the data refresh. The lock allows the first request to pull the data and the other 499 to wait (10 second timeout). Once the first request has completed, the other 499 requests will each have their turn with the exclusive lock, but discover that no refresh needs to be done and give up the lock. I am assuming the other 499 requests release the lock quickly and avoid clogging up the lock queue.
Copy link to clipboard
Copied
Wouldn't it be simpler to schedule the data refresh and leave the web applications out of the equation?
Copy link to clipboard
Copied
That is a good suggestion, but it does not prevent a situation where one or more requests come in while the scheduled data refresh is taking place.
There is one other complication with the scheduling method, which is more of a personal preference (nitpicking). If I rely on the scheduled task to refresh the data, I eliminate the possibility of being able to install the application by just dropping the files on a new server. Making one scheduled task entry is not a big deal, but it has always been safer for me to assume that the person installing the application is not sharp enough to read installation instructions.
Copy link to clipboard
Copied
In my experience, if I try to select from a table which is being updated, my select query waits until the update is complete. That will probably happen irrespective of any cf code you have.
Copy link to clipboard
Copied
I will do an experiment with removing the lock and see if I get a different behavior.
I am just not sure how the server will handle multiple requests trying to set an application variable at the same time.
I will posts my results here.
Copy link to clipboard
Copied
asdfdsafsdfssdfdfdsad123 wrote:
I am just not sure how the server will handle multiple requests trying to set an application variable at the same time.
Why, that is known already. Each request will, in turn, update the value of the application variable, with or without a lock.
Copy link to clipboard
Copied
I have discovered that when I move the lock inside of the <CFIF> statement, my manual refreshes both work properly.
Here is what the code looks like:
<!--- reset data cache once a day --->
<CFTRY>
<CFIF IsDefined("URL.ResetData") or
Len(Application.MediaTrackerCacheDate) is 0 or
DateCompare(Now(), Application.MediaTrackerCacheDate, "d") is not 0 or
Not IsDefined("Application.qHomepageNews")
>
<CFLOCK Name="DataPullIntranet" Type="exclusive" TimeOut="10">
<!--- cache frequently used queries --->
<CFINCLUDE Template="#Application.CFMPath#/RefreshNews.cfm" />
</CFLOCK>
<!--- reset MediaTracker cache once a day --->
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFIF>
<!--- likely that lock has timedout, initialize data sets to empty --->
<CFCATCH>
<CFSET Application.qHomepageNews = QueryNew("NewsID") />
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFCATCH>
</CFTRY>
I am curious if the <CFLOCK> is even required at this point. I will preform another test without the <CFLOCK> to see if the server behaves any differently.
Thanks everyone for the help and suggestions.
Copy link to clipboard
Copied
I have finished my testing and it appears that using Dan Bracuk's suggestion of scheduling ColdFusion to refresh the data sets does eliminate the problem I was having. This method fixed the problem because it eliminated the need for the <CFLOCK>, since only one call is made to this routine (instead of relying on the first user request after midnight to trigger the data refresh).
Unfortunately, I ran into one problem with this method.
Occasionally, the server runs its automated maintenance task which installs windows updates. This usually requires a server restart early in the morning. In a situation like this the data refresh has already run, but the server restart wipes that out. So the server starts back up with no cached data, and if not manually corrected, it will not have data until the next scheduled data refresh occurs at midnight the following day.
This situation leads me back to the situation I was having before where I am relying on triggering a refresh based on a user request. This is not a problem by itself, but it brings me back to the issues with <CFINVOKE> and <CFLOCK>.
On a side note: I am still puzzled by this stalling behavior when these two tags are nested. I might just have to eliminate the <CFLOCK> tag and accept the fact that some users could potentially get errors for the first few minutes after midnight, because the data refresh has not completed.
Copy link to clipboard
Copied
ColdFusion is just doing as it is told. As you yourself say, when subsequent requests arrive at the lock, they will wait until the lock is released. However, some of the variables which determine the conditions for release(Application.qHomepageNews, Application.MediaTrackerCacheDate) are updated within the lock. This results in the lock's timeout exception never being caught.
In my opinion, that update should be done outside the lock. For example, like this
<CFTRY>
<CFLOCK Name="DataPullIntranet" Type="exclusive" TimeOut="10">
<CFIF IsDefined("URL.ResetData") or
Len(Application.MediaTrackerCacheDate) is 0 or
DateCompare(Now(), Application.MediaTrackerCacheDate, "d") is not 0 or
Not IsDefined("Application.qHomepageNews")
>
<!--- cache frequently used queries --->
<CFINCLUDE Template="RefreshNews.cfm" />
<!--- reset cache once a day --->
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFIF>
</CFLOCK>
<!--- likely that lock has timed out, initialize data sets to empty --->
<CFCATCH>
<CFSET Application.qHomepageNews = QueryNew("NewsID") />
<CFSET Application.MediaTrackerCacheDate = Now() />
</CFCATCH>
</CFTRY>