Форма представления теряют идемпотентность при быстрой подаче подряд - PullRequest
1 голос
/ 15 февраля 2020

У меня была проблема с людьми, дважды щелкающими мою кнопку отправки формы, и она отправлялась несколько раз, поэтому я хотел разрешить только одну отправку. Моей первой мыслью было javascript, но это не совсем мгновенно, и я хотел, чтобы что-то гарантировано работало на 100%.

Мое решение состояло в том, чтобы сделать представления идемпотентными, дав каждой форме загрузку своей собственной га sh, и при каждом представлении проверяю, существует ли этот ha sh, и, если это так, ничего не делает.

Итак, в этом суть моего кода:

#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]

Это работает, если Я разрешаю некоторое время между отправками, но когда я делаю двойной щелчок по кнопке отправки, в мою базу данных заносятся две записи с одинаковым значением ha sh.

Я использую простую Puma 3.7 сервер на локальном хосте. У меня сложилось впечатление, что большинство серверов получат запрос, выполнят его, а затем перейдут к следующему запросу, но похоже, что происходит какой-то тип параллелизма.

Мой вопрос: как это возможно? ? Каждая последующая запись имеет значение идентификатора на единицу больше, чем предыдущая запись, поэтому сервер не знает о предыдущей записи. Так почему же, если запросы отправляются очень быстро, уникальное требование ha sh игнорируется? Опять же, если я попробую позже с тем же ha sh, ничего не произойдет, как ожидалось.

1 Ответ

1 голос
/ 15 февраля 2020

Просто используйте Rack :: Throttle вместо изобретения колеса.

# config/application.rb
require 'rack/throttle'

class Application < Rails::Application
  config.middleware.use Rack::Throttle::Interval
end

Это применяет минимальный интервал в 1 секунду между запросами от одного и того же клиента. Вы можете настроить это с помощью опции min:.

Это можно сочетать с javascript отладкой / регулированием , что еще больше уменьшит нагрузку на ваш сервер (и клиент).

Я использую простой сервер Puma 3.7 на localhost. У меня сложилось впечатление, что большинство серверов получат запрос, выполнят его, а затем перейдут к следующему запросу, но похоже, что происходит какой-то тип параллелизма.

Puma является многопоточным , Хотя вы можете настроить его как однопоточный, Ruby в основном блокирует ввод-вывод, поэтому однопоточный веб-сервер плохо работает под нагрузкой.

Мой вопрос: как это возможно? Каждая последующая запись имеет значение идентификатора на единицу больше, чем предыдущая запись, поэтому сервер не знает о предыдущей записи. Так почему же, если запросы отправляются очень быстро, уникальное требование ha sh игнорируется?

Условия гонки . То, что вы делаете, это проверка уникальности уровня приложения, когда вы запускаете запрос к базе данных, а затем вставляете строку в базу данных.

Если два (или более) процесса отвечают на дублирующиеся запросы в одно и то же время, оба получат зеленый свет по запросу Submission.find_by(hash: params[:hash]), а затем перейдут к вставке записи.

Это хорошо известная проблема с проверками приложений на любом языке / каркасе и может быть решена путем добавления уникального индекса в базу данных, что приведет к отклонению дублирующим оператором вставки базы данных. Это вызовет ошибку драйвера базы данных в Rails.

Но я все еще считаю, что это должно быть обработано на уровне промежуточного программного обеспечения.

...