Что может привести к тому, что Rails создаст дубликаты записей? - PullRequest
6 голосов
/ 28 марта 2012

Мы замечаем, что в разных таблицах нашей базы данных создается много повторяющихся записей, но мы не понимаем, почему это происходит. Интересно, что в то время как записи дублируют друг друга (вплоть до отметок created_at!), В нашей таблице пользователей соль пароля и хэш различны для каждой записи - что наводит меня на мысль, что Rails каким-то образом выполняет транзакции / сохранить операции дважды. Очевидно, мы не вызываем save или create несколько раз в коде приложения.

Такое дублирование, по-видимому, не происходит с каждой записью, сохраненной в базе данных, и мы пока не можем вывести шаблон. Существует также проверка validates_uniqueness_of для модели User (хотя пока еще не является уникальным ключом в таблице; для этого нам нужно очистить все дубликаты), поэтому Rails должен остановить себя, если запись уже существует , но если запросы запускаются одновременно, это условие гонки.

В настоящее время мы запускаем Rails 3.2.2 за Passenger 3.0.11 / nginx на наших серверах приложений (в настоящее время их 2) и располагаем одним центральным веб-сервером nginx, который отправляет запросы на сервер приложений. Может ли эта установка как-то вызвать дублирование процессов или что-то в этом роде? Будет ли иметь значение то, что запросы не привязаны к одному вышестоящему серверу (т. Е. Если один пользователь запрашивает страницу, содержащую статический контент, например изображения, можно использовать один или оба сервера приложений)? (Я чувствую, что цепляюсь за соломинку, но хочу охватить все возможности)

Что еще могло вызвать это?

Обновление: Например, сегодня был создан пользователь, получивший повторяющиеся записи. Оба имеют штамп created_at 2012-03-28 16:48:11, и все столбцы, кроме hashed_password и salt, идентичны. Из журнала запросов я вижу следующее:

Сервер приложений 1:

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:47:19 -0400
[2012-03-28 12:47:19] INFO : Processing by ApplyController#create_user as HTML
[2012-03-28 12:47:20] INFO :   Rendered apply/new_user.html.erb within layouts/template (192.8ms)

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML
[2012-03-28 12:48:11] INFO : Redirected to apply/initialize_job_application/3517
[2012-03-28 12:48:11] INFO :  /app/controllers/apply_controller.rb:263:in `block (2 levels) in create_user'

Сервер приложений 2:

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML

Веб-сервер:

1.2.3.4 - - [28/Mar/2012:12:48:10 -0400] "POST /en/apply/create_user HTTP/1.1" 499 0 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-"
1.2.3.4 - - [28/Mar/2012:12:48:11 -0400] "POST /en/apply/create_user HTTP/1.1" 302 147 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-"

Таким образом, действие create было выполнено три раза (возвращаясь к форме в первый раз из-за ошибки, вероятно), и по крайней мере один раз на каждом сервере. Последние два регистрируются веб-сервером как отдельные запросы, но первый получает код состояния 499 Client Closed Request (расширение nginx согласно википедии), а второй получает 302, как и ожидалось. Могут ли 499 быть причиной проблем здесь?

Ответы [ 2 ]

5 голосов
/ 29 марта 2012

На ум приходят две возможности.

Первая - это странное (и против RFC) поведение Nginx при использовании в качестве балансировщика нагрузки.Он будет повторять любые неудачные запросы к следующему бэкэндуRFC допускает это только для безопасных методов (например, GET или HEAD).Результатом этого является то, что если ваш nginx по какой-то причине считает запрос неудачным, возможно, он повторно отправлен на следующий сервер.Если оба сервера завершают свою транзакцию, у вас есть дубликат записи.Судя по журналу вашего веб-сервера (и коду состояния 499, который Nginx использует для обозначения отказа пользователя в своем браузере), это выглядит как наиболее вероятная причина.

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

Чтобы убедиться, что ваши пользовательские записи действительно уникальны, вы должны создать уникальные индексы в своей базе данных.Затем они фактически гарантируются (хотя и с более худшим сообщением об ошибке по сравнению с проверкой ActiveRecord. Поэтому вы всегда должны определять ограничение уникальности как для схемы базы данных, так и для ваших моделей.

Кроме того, вы можете посмотреть назаменив свой внешний интерфейс nginx на более совместимый балансировщик нагрузки. Я бы порекомендовал haproxy для этого.

0 голосов
/ 29 марта 2012

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

...