Copy link to clipboard
Copied
I have a cfc that is exposed as a web service . However when a request from 2 different servers consume the webserice at the same time one request is dropped and the other processed. Im not sure why this happens and would really appreciate some insight
Thanks!
Some immediate suggestions
1) localtest is undefined; define it.
2) Var all your function-local variables:
<cfset var getArrD= arraynew(2)>
<cfset var size = 0>
<cfset var soapBodyd = "">
<cfset var soapResponsed = "">
<cfset var responseNodesd = "">
<cfset var FindTPIN = "">
<cfset var strXml = "">
<cfset var xmlRequest = "">
<cfset var XMLDOM = "">
<cfset var products = "">
<cfset var results = "">
<cfset var myqueryD = queryNew("")>
<cfset var temp = "">
<cfset var filteredData = queryNew("")>
3) Where you us
...Copy link to clipboard
Copied
One possible solution that I can think of would be to use CFTHREAD for the requests. That should eliminate any collisions, conflicts, or (a VERY small, miniscule, remote chance) identical IDs.
HTH,
^_^
UPDATE: I highly doubt that two servers could both have the same CFID/CFTOKEN combination, so never mind..
Copy link to clipboard
Copied
Hmm something like this ....
<cfset VARIABLES.Instance.LockID = CreateUUID() />
<cffunction
name="Add"
access="public"
returntype="any"
output="false"
>
<!--- Define arguments. --->
<cfargument
name="Message"
type="string"
required="true"
/>
<!--- Lock method call. --->
<cflock
name="#VARIABLES.Instance.LockID#-Message"
type="exclusive"
timeout="5">
<cfreturn SUPER.Add(
Message = ARGUMENTS.Message
) />
</cflock>
</cffunction>
Copy link to clipboard
Copied
This is the code you have at the moment?
The exclusive lock you have there will cause the behavior you have described. The lock wont let another request run the code until the previous request is done
Copy link to clipboard
Copied
There is something in your code that is not thread safe. Can you share the code from your service that you are having issues with? I have written many services like you describe and have not ran into the issue you are having. You shouldn't need to use cflock for this.
--Dave
Copy link to clipboard
Copied
this is the code that is giving the issue BKBK‌
<cffunction name="sendPaymentConfirmation" returntype="any" access="remote" >
<cfargument name="tid" type="string" required="true" />
<cfargument name="cid" type="string" required="true" />
<cfargument name="declarantFlag" type="string" required="true" />
<cfargument name="pflag" type="numeric" required="true" />
<cfargument name="aNumber" type="string" required="true" />
<cfargument name="aPaid" type="numeric" required="true" />
<cfargument name="iAmt" type="numeric" required="true" />
<cfargument name="pAmt" type="numeric" required="true" />
<cfargument name="asYear" type="string" required="true" />
<cfargument name="pEntry" type="string" required="true" />
<cfargument name="rSerial" type="string" required="true" />
<cfargument name="rNumber" type="string" required="true" />
<cfargument name="rDate" type="string" required="false"/>
<cfargument name="pType" type="string" required="true"/>
<cfargument name="source" type="string" required="true" />
<cfargument name="sourceID" type="string" required="true" />
<cfargument name="timestamp" type="string" required="true"/>
<cfargument name="type" type="string" required="true" />
<cfargument name="country" type="string" required="true" />
<cfif #pflag# eq 2>
<cfset declarantCode=getDeclarant(#cid#,#pEntry#,#rNumber#,#rSerial#,#asYear#)>
<cfif #iAmt# gt 0>
<cfquery datasource="smartpay_log">
INSERT INTO Log_table (taxPerIdentification, aNumber, aPaid, asYear, pEntry, rSerial, rNumber, rDate, source, sourceID, timestamp, type, country,asyResponse,log_time,item_type,declarant_code)
VALUES (<cfqueryparam value="#cid#">,<cfqueryparam value="#rNumber#">,<cfqueryparam value="#iAmt#">,<cfqueryparam value="#asYear#">,<cfqueryparam value="#pEntry#">,<cfqueryparam value="#rSerial#">,<cfqueryparam value="#rNumber#">,<cfqueryparam value="#asYear#">,<cfqueryparam value="#source#">,<cfqueryparam value="#sourceID#">,<cfqueryparam value="#timestamp#">,<cfqueryparam value="#type#">,<cfqueryparam value="#country#">,<cfqueryparam value=" ">,<cfqueryparam value="#DateFormat(now(),"DD-MM-YYYY")# #TimeFormat(now(),"HH:mm:ss")#">,<cfqueryparam value="INTQ">,<cfqueryparam value="#declarantCode.declarantCode#">)
</cfquery>
<cfset var xmlPacket_details_INT = "">
<cfxml variable="xmlPacket_details_INT">
<cfoutput><sendPaymentConfirmationResponse>
<PaymentConfirmation>
<pType>1</pType>
<reasonCode>0</reasonCode>
<reasonDescription>PAYMENTQUEUED</reasonDescription>
<source>#source#</source>
<sourceID>#sourceID#</sourceID>
<timestamp>#DateFormat(now(),"YYYY-MM-DD")#T#TimeFormat(now(),"HH:mm:ss")#</timestamp>
<type>#type#</type>
<country>#country#</country>
</PaymentConfirmation>
</sendPaymentConfirmationResponse></cfoutput>
</cfxml>
</cfif>
<cfif #pAmt# gt 0>
<cfquery datasource="smartpay_log">
INSERT INTO Log_table (taxPerIdentification, aNumber, aPaid, asYear, pEntry, rSerial, rNumber, rDate, source, sourceID, timestamp, type, country,asyResponse,log_time,item_type,declarant_code)
VALUES (<cfqueryparam value="#cid#">,<cfqueryparam value="#rNumber#">,<cfqueryparam value="#pAmt#">,<cfqueryparam value="#asYear#">,<cfqueryparam value="#pEntry#">,<cfqueryparam value="#rSerial#">,<cfqueryparam value="#rNumber#">,<cfqueryparam value="#asYear#">,<cfqueryparam value="#source#">,<cfqueryparam value="#sourceID#">,<cfqueryparam value="#timestamp#">,<cfqueryparam value="#type#">,<cfqueryparam value="#country#">,<cfqueryparam value=" ">,<cfqueryparam value="#DateFormat(now(),"DD-MM-YYYY")# #TimeFormat(now(),"HH:mm:ss")#">,<cfqueryparam value="PRNQ">,<cfqueryparam value="#declarantCode.declarantCode#">)
</cfquery>
</cfif>
<cfif #iAmt# gt 0>
</cfif>
</cfif>
</cffunction>
Copy link to clipboard
Copied
I haven't looked into the content. But I do believe you can improve the code by doing something like
<cffunction name="sendPaymentConfirmation" returntype="any" access="remote" >
<cfargument name="tid" type="string" required="true" />
<cfargument name="cid" type="string" required="true" />
<cfargument name="declarantFlag" type="string" required="true" />
<cfargument name="pflag" type="numeric" required="true" />
<cfargument name="aNumber" type="string" required="true" />
<cfargument name="aPaid" type="numeric" required="true" />
<cfargument name="iAmt" type="numeric" required="true" />
<cfargument name="pAmt" type="numeric" required="true" />
<cfargument name="asYear" type="string" required="true" />
<cfargument name="pEntry" type="string" required="true" />
<cfargument name="rSerial" type="string" required="true" />
<cfargument name="rNumber" type="string" required="true" />
<cfargument name="rDate" type="string" required="false"/>
<cfargument name="pType" type="string" required="true"/>
<cfargument name="source" type="string" required="true" />
<cfargument name="sourceID" type="string" required="true" />
<cfargument name="timestamp" type="string" required="true"/>
<cfargument name="type" type="string" required="true" />
<cfargument name="country" type="string" required="true" />
<cfset var declarantCode = "">
<cfset var xmlPacket_details_INT = "">
<cfif arguments.pflag eq 2>
<cfset declarantCode=getDeclarant(arguments.cid,arguments.pEntry,arguments.rNumber,arguments.rSerial,arguments.asYear)>
<cfif arguments.iAmt gt 0>
<cfquery datasource="smartpay_log">
INSERT INTO Log_table (taxPerIdentification, aNumber, aPaid, asYear, pEntry, rSerial, rNumber, rDate, source, sourceID, timestamp, type, country,asyResponse,log_time,item_type,declarant_code)
VALUES (<cfqueryparam value="#arguments.cid#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rNumber#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.iAmt#" cfsqltype="CF_SQL_NUMERIC">,
<cfqueryparam value="#arguments.asYear#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.pEntry#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rSerial#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rNumber#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.asYear#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.source#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.sourceID#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.timestamp#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.type#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.country#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value=" " cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#DateFormat(now(),'DD-MM-YYYY')# #TimeFormat(now(),'HH:mm:ss')#" cfsqltype="CF_SQL_DATE">,
<cfqueryparam value="INTQ" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.declarantCode.declarantCode#" cfsqltype="CF_SQL_VARCHAR">)
</cfquery>
<cfxml variable="xmlPacket_details_INT">
<cfoutput><sendPaymentConfirmationResponse>
<PaymentConfirmation>
<pType>1</pType>
<reasonCode>0</reasonCode>
<reasonDescription>PAYMENTQUEUED</reasonDescription>
<source>#arguments.source#</source>
<sourceID>#arguments.sourceID#</sourceID>
<timestamp>#DateFormat(now(),"YYYY-MM-DD")#T#TimeFormat(now(),"HH:mm:ss")#</timestamp>
<type>#arguments.type#</type>
<country>#arguments.country#</country>
</PaymentConfirmation>
</sendPaymentConfirmationResponse></cfoutput>
</cfxml>
</cfif>
<cfif arguments.pAmt gt 0>
<cfquery datasource="smartpay_log">
INSERT INTO Log_table (taxPerIdentification, aNumber, aPaid, asYear, pEntry, rSerial, rNumber, rDate, source, sourceID, timestamp, type, country,asyResponse,log_time,item_type,declarant_code)
VALUES (<cfqueryparam value="#arguments.cid#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rNumber#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.pAmt#" cfsqltype="CF_SQL_NUMERIC">,
<cfqueryparam value="#arguments.asYear#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.pEntry#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rSerial#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.rNumber#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.asYear#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.source#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.sourceID#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.timestamp#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.type#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#arguments.country#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value=" " cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#DateFormat(now(),'DD-MM-YYYY')# #TimeFormat(now(),'HH:mm:ss')#" cfsqltype="CF_SQL_DATE">,
<cfqueryparam value="PRNQ" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#declarantCode.declarantCode#" cfsqltype="CF_SQL_VARCHAR">)
</cfquery>
</cfif>
<cfif arguments.iAmt gt 0>
</cfif>
</cfif>
<cfreturn someVar><!---Otherwise remove this line and set returntype = "void"--->
</cffunction>
Copy link to clipboard
Copied
OK ill try that just to make it more efficient .. Do you suppose this could solve the problem ? Im unfortunately not able to test it as i dont have a clustered environment on my local machine to consume the webservice concurrently
Copy link to clipboard
Copied
As I said, I haven't looked at the content. That might solve the problem, assuming that the database connection and declarantCode don't introduce any external influence.
Copy link to clipboard
Copied
Beyond what BKBK‌ suggested. There might be an issue with the getDeclarant function code. Also, the lack of var scoping in the code could also lead to issues. Based on this sample I am willing to bet that the code in that function is not thread safe either.
Copy link to clipboard
Copied
Thanks I'm looking thru the entire code. Any good articles on thread safety I can look at ?
Copy link to clipboard
Copied
Please read <cfqueryparam value="#declarantCode.declarantCode#" cfsqltype="CF_SQL_VARCHAR"> in place of <cfqueryparam value="#arguments.declarantCode.declarantCode#" cfsqltype="CF_SQL_VARCHAR">.
Chinyenje wrote:
Any good articles on thread safety I can look at ?
Adobe ColdFusion: Locking code with cflock
Could you show us the 'declarant' code?
Copy link to clipboard
Copied
Thank for the link. Below is the declarant code
<cffunction name="getDeclarant" returntype="any" access="private">
<cfargument name="clienttpi" type="string" required="no" hint="This carries the TPIN" >
<cfargument name="poe" type="string" required="yes" >
<cfargument name="rnum" type="string" required="yes" >
<cfargument name="rser" type="string" required="yes" >
<cfargument name="registrationYear" type="string" required="yes" >
<cfset getArrD= arraynew(2)>
<cfset var size = 0>
<cfsavecontent variable="soapBodyd">
<cfoutput>
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:asy="http://www.asy.org">
<soapenv:Header/>
<soapenv:Body>
<asy:getutp>
<!--Optional:-->
<asy:tinfo>
<bankCode>SCB</bankCode>
<!--Optional:-->
<ctpi>#arguments.clienttpi#</ctpi>
</asy:tinfo>
</asy:getutp>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
<cfif localtest eq "YES">
<cfhttp url="http://localhost" method="head" result="httpResponse" >
</cfhttp>
<cfelse>
<cfhttp
url="https://127.0.0.1:8443/asy/WSZ"
method="post"
result="httpResponse" username="123" password="123">
<cfhttpparam
type="header"
name="SOAPAction"
value="urn:getU"
/>
<cfhttpparam
type="header"
name="accept-encoding"
value="no-compression"
/>
<cfhttpparam
type="xml"
value="#trim( soapBodyd )#"
/>
</cfhttp>
</cfif>
<cfif localtest eq "YES">
<cfset tranXML =getTransXML()>
</cfif>
<cfif find( "200", httpResponse.statusCode )>
<cfif localtest eq "YES">
<cfset soapResponsed = xmlParse( #tranXML# ) />
<cfelse>
<cfset soapResponsed = xmlParse( httpResponse.fileContent ) />
</cfif>
<!--- --->
<cfset responseNodesd = xmlSearch(
soapResponsed,
"//*[ local-name() = 'unSR' ]"
) />
<cfset FindTPIN = xmlSearch(
soapResponsed,
"//*[ local-name() = 'TPIN' ]"
) />
<cfoutput>
<cfsavecontent variable="strXml"> #soapResponsed# </cfsavecontent>
<cfset strXml = strXml.ReplaceAll(
"(</?)(\w+:)",
"$1"
) />
<cfset strXml = strXml.ReplaceAll(
"xmlns(:\w+)?=""[^""]*""",
""
) />
<cfset xmlRequest = XmlParse(
strXml.Trim()
) />
<cfset XMLDOM = xmlParse(xmlRequest)>
<cfset i=1>
<cfset products =ArrayNew(2)>
<cfloop index="results" array="#xmlsearch(XMLDOM,'/Envelope/Body/getUTResponse/unSR/assmntPaid')#">
<cfset results = xmlparse(results)>
<cfset getArrD[1]=#results.asmTpd.declarantCode.xmltext#>
<cfset getArrD[2]=#results.asmTpd.TPIN.xmltext#>
<cfset getArrD[3]=#results.asmTpd.officeCode.XmlText#>
<cfset getArrD[4]=#results.asmTpd.astp.XmlText#>
<cfset getArrD[5]=#results.asmTpd.registrationYear.xmltext#>
<cfset getArrD[6]=#results.asmTpd.rser.xmltext#>
<cfset getArrD[7]=#results.asmTpd.rnum.xmltext#>
<cfset getArrD[8]=#results.asmTpd.amtP.xmltext#>
<cfset i=i+1>
</cfloop>
</cfoutput>
</cfif>
<cfset size = ArrayLen(getArrD)>
<cfif size gte 1>
<cfset myqueryD = QueryNew("trans_id,declarantCode,tpi,poe,astp,ayear,rser,rnum,amtP,reasonCode,reasonDescription,source,sourceID,timestamp,type,country","VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar") >
<cfset temp = QueryAddRow(myqueryD,#size#)>
<cfloop index="i" from = "1" to = #size#>
<cfset temp = QuerySetCell(myqueryD, "trans_id", #i#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "declarantCode", #getArrD[1]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "tpi", #getArrD[2]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "poe", #getArrD[3]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "astp", #getArrD[4]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "ayear", #getArrD[5]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "rser", #getArrD[6]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "rnum", #getArrD[7]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "amtP", #getArrD[8]#, #i#)>
<cfset temp = QuerySetCell(myqueryD, "reasonCode", "0", #i#)>
<cfset temp = QuerySetCell(myqueryD, "reasonDescription", "Sucessful", #i#)>
<cfset temp = QuerySetCell(myqueryD, "source", "SP" ,#i#)>
<cfset temp = QuerySetCell(myqueryD, "sourceID", "1234", #i#)>
<cfset temp = QuerySetCell(myqueryD, "timestamp", "12:00", #i#)>
<cfset temp = QuerySetCell(myqueryD, "type", "CHN", #i#)>
<cfset temp = QuerySetCell(myqueryD, "country", "CHN", #i#)>
</cfloop>
<cfelse>
<cfset myqueryD = QueryNew("trans_id,declarantCode,tpi,poe,astp,ayear,rser,rnum,amtP,reasonCode,reasonDescription,source,sourceID,timestamp,type,country","VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar,VarChar") >
</cfif>
<cfquery name="filteredData" dbtype="query">
select * from myqueryD where
rnum=<cfqueryparam value="#arguments.rnum#"> and rser=<cfqueryparam value="#arguments.rser#"> and poe =<cfqueryparam value="#arguments.poe#">
and ayear= <cfqueryparam value="#arguments.registrationYear#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfreturn filteredData>
</cffunction>
Copy link to clipboard
Copied
Some immediate suggestions
1) localtest is undefined; define it.
2) Var all your function-local variables:
<cfset var getArrD= arraynew(2)>
<cfset var size = 0>
<cfset var soapBodyd = "">
<cfset var soapResponsed = "">
<cfset var responseNodesd = "">
<cfset var FindTPIN = "">
<cfset var strXml = "">
<cfset var xmlRequest = "">
<cfset var XMLDOM = "">
<cfset var products = "">
<cfset var results = "">
<cfset var myqueryD = queryNew("")>
<cfset var temp = "">
<cfset var filteredData = queryNew("")>
3) Where you use arguments, prefix them with arguments.someVar
Copy link to clipboard
Copied
localtest is a global variable defined just after the cfcomponet declaration.
Copy link to clipboard
Copied
OK.
In any case, as a general rule, avoid using a global variable. If one user takes it into his room, others may have to wait outside wondering what he's doing with it and when he will be done. I mean of course thread issues.
Copy link to clipboard
Copied
Thank you all for pointing me in the right direction i can confirm it was as BKBK‌ said along with with everybody else. The service works exactly as i want it to
Copy link to clipboard
Copied
By default, requests to a web service run concurrently, each as a separate thread. If that is different in your service, then it means you have to redesign your service. Could you show us the CFC?