Что может привести к созданию дубликатов записей Rails?

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

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

в настоящее время мы бежим Rails 3.2.2 за пассажиром 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

Web Сервер:

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 ответов


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

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

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

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

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


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