Avoiding duplicated form submissions
Note: this entry has moved.
Fast Joe
Last week I was asked to add some support to our customized ASP.NET framework to help developers with the common Fast Joe scenario: an impatient user clicking the submit button 3 times in less than a second thus causing multiple submissions of the same form which is usually a very bad thing (i.e. duplicate processing, etc).
Googling around
Having not approached this problem before I started googling around to learn how others were dealing with this. That’s how I first got to this post from Nicole Calinoiu (don’t know if she has a weblog) summarizing common approaches along with their pros/cons. From there I got to some more advices from Nicole, this time commenting a post from Andy Smith. And finally, a later post from Andy about a custom control he wrote -named OneClick- apparently inspired by Nicole’s comments.
Flawed OneClick?
By quickly looking at OneClick’s source code I’m noticing what I believe is a big flaw. The control offers a boolean IsValid property whose description in the docs read: “…true if the user only submitted the form once, false if more submittals were received…” and it recommends that you write your code this way:
if (OneClick1.IsValid) {
// Code here is important to happen only once, and might take substantial amounts of time to complete
// For example purposes, the thread will be put to sleep for 3 seconds.
System.Threading.Thread.Sleep( TimeSpan.FromSeconds(3) );
}
in order to avoid running the code multiple times if the first request is still processing.
OneClick’s main logic consists in generating a key (Guid) and sending it down to the client in a hidden form field. Then, on postbacks, the control will look for this key (at control initialization time) and store it into Cache (yes… this will break webfarm scenarios) if present. At control unloading time the added key is removed from Cache. Note how this key is being used as a flag to signal the ongoing execution of a request. The IsValid property will be set accordingly: if the key is present it means the original request has not completed processing yet, so IsValid should be set to false indicating we’re dealing with a duplicate submission. Although this may sound well it’s missing an essential ASP.NET architecture fact: execution of pages accessing the same session state data is serialized, meaning IsValid property will never get a chance to be false in pages having session state enabled. Ouch!.
Now, let’s suppose it was designed for use in pages not accessing session state. The first submission will cause the flag to be set and then will start processing the code guarded by the IsValid check. Meanwhile the user resubmits the form, this time causing the new request to be processed concurrently. Now IsValid will be properly set to false so code guarded by it won’t execute again for this 2nd submission. Sounds good so far? Wait… Fast Joe decides to resubmit again while the original submission is still taking place… and for this 3rd submission IsValid will be… true! Why? Because OneClick’s unloading code has already cleared up the flag when it had a chance to run for the 2nd submission. Ouch!
As I said I’ve just browsed through the code so I need to find some time to compile it and play with it a bit to check my assertions.
It looks to me like a client-side approach would be the best solution (based on the specific requirements I’m dealing with). The only problem is that our framework must also support mobile (read non javascript capable) devices which I guess calls for a hybrid solution. When I find the time to tackle this (it’s not currently on my 50-TO-DO list…) I will blog again detailing the approach taken. Meanwhile, your feedback on the topic is much appreciated :--)