I had a problem with people double clicking my form submit button and it submitting multiple times, so I wanted to allow only one submission. My first thought was javascript, but that's not absolutely instantaneous and I wanted something 100% guaranteed to work.
My solution was to make submissions idempotent, by giving each form load its own hash, and on each submission, checking whether that hash already exists, and if it does, not doing anything.
So this is the gist of my code:
#form
@hash = SecureRandom.urlsafe_base64(20)
f.hidden_field :hash, value: @hash
f.submit "submit"
#controller
existing = Submission.find_by(hash: params[:hash])
if existing.nil?
#enter new Submission into the database with hash: params[:hash]
This works if I allow some time between submissions, but when I do a double click on the submit button, two records are entered into my database, with the same hash value.
I'm using a simple Puma 3.7 server on localhost. I was under the impression that most servers would receive a request, execute it, then move on to the next request, but it's almost like there's some type of parallelism going on.
My question is: how is this possible? Each subsequent record has an ID value one greater than the previous record, so it's not like the server didn't know about the previous record. So how is it that if the requests are sent very rapidly, the unique hash requirement is ignored? Again, if I try again later with the same hash, nothing happens, as expected.