On the server, you can do a couple of things. You can submit to an intermediate page which converts the form variables to session variables and forwards the user to the real page. Or, you can cfflush some text onto the real page and then start processing.
Thanks for the reply. I did actually try the intermediate page approach, and unfortunately it did not work, if I was quick enough I can easily start two sessions for the same person.
I did a little research and would have posted it earlier but the forum went down after I made my initial post.
What I foundwas that a CFLOCK appeared to do the trick, I put a CFLOCK around the whole page which performs the processing and after testing many times I have not managed to trip it. I've never used CFLOCK before, I'm wondering if there are any negative effects to this approach.
Here's the code i used, as you can see I made the scope SESSION, I presume that otherwise it might lock out other users
<cflock timeout = "999"
scope = "Session"
throwOnTimeout = "No"
type = "exclusive">
scrub the answer below, I gave it another test this morning and I managed to trip it immediately, I guess I was slowing down on the button pressing last night!
In my opinion, you did correctly diagnose the cause of the problem, namely, race conditions. I am assuming you configured your application properly (sessionManagement, sessionTimeOut, etc). Then there would just be one session for the client. Not sessions in plural, as you suggest.
The race conditions would then have been caused by two requests that attempted to access the same block of session-scoped code. A lock is indeed the answer. I would use a lock timeout of between 5 and 30 seconds, certainly not 999.
However, there is another possible problem. Coldfusion needs a finite amount of time to clear the deleted session variables from memory. If you fired two requests in quick succession while that was going on, you could indeed initiate new sessions.
An undeleted session variable is no big problem. Accidentally triggering a new session is. A new session means a new client or a new order. You wouldn't want your application to begin a new session halfway through a current one, or to end the current one prematurely. So pay particular attention where you delete the session variables, especially Coldfusion's session cookies.
You almost certainly shouldn't do that upon submission of a form. Validate the form on the action page. Invalid data should halt execution.
You would normally delete session variables on the logout page. The cflogout tag, coupled with <cflogin> and the setting loginStorage="session", would prevent any inconsistencies..
Nevertheless, without some code, this all amounts to speculation.
Thanks for the in-depth reply.
This is what I use to initiate the session
I thought the CFLOCK worked but the next day I was able to trip it, however, now I'm not able to do so, weird, I think the shared server that I had this on was possibly running slow at the time so the SQL was not responding quickly and therefore allowing me to get that extra hit in.
I'm using this session for a purchase, the final page displays an invoice and creates necessary records inside the database. I use a single session variable to let CF know if I require the code to post a payment request to PayPal, after which I delete that variable, but allow all others to remain, that way, if the user refreshes the page it can still display the info including their log in information. I did this with the thought that if there was a delay/pause in displaying the page and they hit submit again, it would not recharge them but it would also allow them to obtain their info and make it look like a successful order.
However, as described, I was able to rapidly hit the button, in such a way that the session variable which tells the CF to process not to post a payment did indeed seem to have been set to zero, but the other session variables obtained from the queries did not exist, which resulted in a problem because the refresh did not have the sessions available to display the page correctly.
The code which clears out the session to tell it not to post a payment/dbase write again had to go close to the top because the payment to PayPal was the first thing processed, then my local dbase queries follow, if I did not put the session variable removal right underneath the paypal post, it could potentially try to double bill them, althought paypal would reject the token (I would hope) even with rapid posting, I did not want to take the risk.
I can't have a log out option because they are not technically logged into an account.
Hope that explains
You could do it in a lock, like this
<cflock timeout = "10" scope = "Session" throwOnTimeout = "No" type = "exclusive">
<!--- validate form data --->
<!--- do any pre-transaction database business --->
<!--- assign value to session.doPayPal to enable PayPal transaction --->
<!--- do the PayPal transaction --->
<!--- do any post-transaction database business --->
<!--- reassign value to session.doPayPal to disable PayPal transaction --->
If the code is too long or too involved, you could split it up into blocks within locks that share the same name.
I'm going to have to go back and study the code. There are several dbase inserts which create an account for them, record the transaction and also create software license(s). My other concern was that if there was a crash along the way and it did not get to the payment, that they could potentially still have their records written, which would amount to them getting a license at no charge.
Normal practice would be to start a session BEFORE you start processing form results, where does the form come from, is it NOT on your server?
If it IS on your server, start a session once the form page is requested and process session form submit only once, i.e. in processing page check if the session is already flagged as processing/processed and set processing flag in a session if it is NOT...
Once the payment confirmation is received set the processed flag too...
Or did I misunderstand something?
that is pretty much how I did it, set a session variable to say that the payment session was active, and then once I processed it, set it to zero, but retain all of the other info, so I could display their invoice etc, therefore if they hit the button twice, real quick they would not know the difference
The only problem I see now is that if PayPal does not connect for any reason I need to catch the error and warn the user, or retry, I tried various CF methods but could not get my brain around them