I have an application that trades virtual items and have a single page which gets all my accounts and for each one creates a thread that firstly logs the account in and then searches and buys for items for as long as the session is active. I should point out at this point that this is my first experience of using cfthread.
I'm having issues with it. Every 30 minutes (if not less) my ColdFusion server comes to a standstill and I have to restart the service. Upon restarting the service I check the logs and there are errors that say "GC Overhead Limit Exceeded".
I have looked extensively online but as much as cfthread is new to me, so is the JVM and how it operates. I'm running on CF10 Enterprise Edition and have loaded up the server monitor and surely enough I can see the JVM memory usage grow and grow until the limit has been reached (just now I have it set as 2gb as when I had it set higher the memory seemed to fill up quicker). Even when I select the Run GC option in the monitor it does not reduce the memory usage very much, if at all.
Is this more than likely something to do with my code? At the moment I have just under 50 threads being created but as I add more accounts to the application then the more threads that will be required.
Here is the code from the page...
<script>
/* RELOAD PAGE EVERY 65 MINUTES */
setTimeout(function(){
window.location.reload(1);
}, 3900000);
</script>
<!--- GET ACTIVE ACCOUNTS --->
<cfquery name="getLogins" datasource="myDB">
SELECT * FROM Logins WHERE active = 1
</cfquery>
<!--- LOOP THROUGH ACCOUNT --->
<cfloop query="getLogins">
<!--- HAVE A SLEEP SO IP DOESN'T GET FLAGGED FOR SENDING TOO MANY REQUESTS AT ONCE --->
<cfset Sleep(30000) />
<!--- CREATE THREAD FOR ACCOUNT --->
<cfthread
name="#getLogins.accountName#"
action="run"
accountName="#Trim(getLogins.accountName)#"
email="#Trim(getLogins.email)#"
password="#Trim(getLogins.password)#"
resourceId="#Trim(getLogins.resourceID)#">
<!--- DEFAULT SESSION VARIABLES --->
<cfset SESSION["#attributes.accountName#LoggedIn"] = 0 />
<cfset SESSION["#attributes.accountName#LoginAttempts"] = 0 />
<!--- WHILE ACCOUNT NOT LOGGED IN AND LESS THAN 8 LOGIN ATTEMPTS MADE --->
<cfscript>
while (SESSION['#attributes.accountName#LoggedIn'] EQ 0 AND SESSION['#attributes.accountName#LoginAttempts'] LT 8) {
// ATTEMPT LOGIN
THREAD.logInAccount = Application.cfcs.Login.logInAccount(attributes.email,attributes.password);
// IF LOGIN ATTEMPT UNSUCCESSFUL
if (THREAD.logInAccount EQ 0) {
// INCREASE ATTEMPT COUNT
SESSION['#attributes.accountName#LoginAttempts'] = SESSION['#attributes.accountName#LoginAttempts'] + 1;
}
// ELSE IF RETURNED VALUE IS 481 THEN ACCOUNT IS LOCKED
else if (THREAD.logInAccount EQ 481) {
// SET LOGIN ATTEMPT COUNT TO STOP LOOP
SESSION['#attributes.accountName#LoginAttempts'] = 8;
// UPDATE ACCOUNT TO MARK AS LOCKED
THREAD.updLogin = Application.cfcs.Login.updLogin(attributes.email);
}
}
</cfscript>
<!--- IF ACCOUNT LOGGED IN --->
<cfif SESSION['#attributes.accountName#LoggedIn'] EQ 1>
<!--- SET ID FOR SEARCHING --->
<cfset THREAD.definitionID = attributes.resourceID - 1610612736 />
<!--- WHILE ACCOUNT LOGGED IN --->
<cfloop condition="SESSION['#attributes.accountName#LoggedIn'] EQUALS 1">
<!--- GET LATEST LOWEST BUY NOW PRICE --->
<cfquery name="THREAD.getMinBIN" datasource="FUT" cachedWithin="#CreateTimeSpan(0,0,1,0)#">
SELECT TOP 1 * FROM v_FUT14BINPrices WHERE resourceID = #attributes.resourceId# ORDER BY lastUpdated DESC
</cfquery>
<!--- INCLUDE FILE THAT CALCULATES BUYING AND SELLING PRICES --->
<cfinclude template="sellingPrices.cfm" />
<!--- IF BIDDING PRICE HAS BEEN SET --->
<cfif StructKeyExists(THREAD,"biddingPrice")>
<!--- MAKE SEARCH REQUEST, TIMING THE REQUEST --->
<cfset THREAD.requestStart = GetTickCount() />
<cfset THREAD.search = Application.cfcs.Search.dosearchOld(attributes.resourceId,THREAD.biddingPrice,0) />
<cfset THREAD.requestDuration = GetTickCount() - THREAD.requestStart />
<!--- IF SEARCH CONTAINS FILE CONTENT --->
<cfif StructKeyExists(THREAD.search,"FileContent")>
<!--- DECLARE NUMBER OF RESULTS VARIABLE --->
<cfset THREAD.numResults = 0 />
<!--- IF JSON RETURNED --->
<cfif IsJSON(THREAD.search.FileContent)>
<!--- DESERIALIZE JSON --->
<cfset THREAD.searchResults = DeserializeJSON(THREAD.search.FileContent) />
<!--- IF PLAYER SEARCH RETURNS AUCTIONINFO STRUCT --->
<cfif StructKeyExists(THREAD.searchResults,"auctionInfo")>
<!--- SET NUMBER OF CARDS RETURNED FROM SEARCH --->
<cfset THREAD.numResults = ArrayLen(THREAD.searchResults.auctionInfo) />
<cfset THREAD.statusCode = "Successful" />
<cfif THREAD.numResults EQ 0>
<cfset THREAD.statusCode = "Successful - No Results" />
</cfif>
<!--- ELSE IF ERROR CODE RETURNED --->
<cfelseif StructKeyExists(THREAD.searchResults,"code")>
<cfset THREAD.statusCode = THREAD.searchResults.code />
<!--- IF CODE 401 THEN SESSION HAS EXPIRED --->
<cfif THREAD.statusCode EQ 401>
<!--- SET SESSION AS LOGGED OUT AND ATTEMPT SESSION REFRESH --->
<cfset SESSION['#attributes.accountName#LoggedIn'] = 0 />
<cfset THREAD.logInAccount = Application.cfcs.Login.logInAccount(attributes.email,attributes.password) />
</cfif>
<!--- ELSE SOMETHING ELSE HAS HAPPENED --->
<cfelse>
<cfset THREAD.statusCode = "Something Else - " & THREAD.searchResults.code />
</cfif>
<!--- IF RESULTS RETURNED --->
<cfif THREAD.numResults GT 0>
<!--- LOOP ROUND RESULTS AND CHECK IF MATCH BUYING CRITERIA --->
<cfloop index="i" from="1" to="#THREAD.numResults#">
<!--- ***SAFETY CHECK*** - ENSURE ID OF CURRENT CARD IS SAME AS ONE SEARCHING FOR --->
<cfif THREAD.searchResults.auctionInfo[i].itemData.resourceID EQ attributes.resourceId AND THREAD.getMinBIN.resourceID EQ attributes.resourceId>
<!--- ENSURE BIN PRICE SET AND IS LESS THAN SET BUYING PRICE --->
<cfif THREAD.searchResults.auctionInfo[i].buyNowPrice GT 0 AND THREAD.searchResults.auctionInfo[i].buyNowPrice LTE THREAD.biddingPrice>
<!--- SET AUCTION END TIME --->
<cfset THREAD.timeLeft = THREAD.searchResults.auctionInfo[i].expires />
<cfset THREAD.auctionEnds = DateAdd("s",THREAD.timeLeft,Now()) />
<!--- BUY CARD --->
<cfset THREAD.buyCard = Application.cfcs.Bid.doBIN(THREAD.searchResults.auctionInfo[i].tradeID,THREAD.searchResul ts.auctionInfo[i].buyNowPrice,THREAD.searchResults.auctionInfo[i].startingBid,THREAD.searc hResults.auctionInfo[i].itemData.ID,THREAD.searchResults.auctionInfo[i].itemData.resourceI D,THREAD.startPrice,THREAD.binPrice,THREAD.lowestBIN,THREAD.searchResults.auctionInfo[i].i temData.discardValue,THREAD.auctionEnds,THREAD.requestStart,THREAD.requestDuration) />
</cfif>
</cfif>
</cfloop>
</cfif>
<cfelse>
<cfset THREAD.statusCode = THREAD.search.FileContent />
</cfif>
<cfset THREAD.sleepDuration = 1000 - THREAD.requestDuration />
<cfif THREAD.sleepDuration GT 0><cfset Sleep(THREAD.sleepDuration) /></cfif>
</cfif>
<!--- INSERT SEARCH RECORD --->
<cfset THREAD.insSearchRecord = Application.cfcs.Search.insSearchRecord(THREAD.definitionID,THREAD.statusCode,THREAD.requ estDuration,THREAD.numResults,THREAD.biddingPrice) />
</cfif>
</cfloop>
</cfif>
</cfthread>
</cfloop>
I would have thought that the memory would have stayed around the same usage as each loop is performing the same set of actions so once the loop has went back to the start then I thought the previous loop would have been removed from memory (freeing up space) and then the same actions would be performed so the same memory total would then be used up but it seems almost as if each loop is being kept in memory and that is why it is growing.
Could someone please help me out and offer some guidance on how I could remedy this issue? If you need any more info then just let me know
Thanks in advance