Безопасный способ перебора нескольких баз данных в Rails - PullRequest
5 голосов
/ 24 февраля 2011

У меня есть несколько приложений Rails, работающих на одном сервере MySQL. Все они запускают одно и то же приложение, и все базы данных имеют одинаковую схему, но каждая база данных принадлежит отдельному клиенту.

Концептуально, вот что я хочу сделать:

   Customer.all.each do |customer|
      connection.execute("use #{customer.database}")
      customer.do_some_complex_stuff_with_multiple_models
   end

Этот подход не работает, потому что, когда он выполняется в веб-запросе, базовые классы модели кэшируют разные соединения с базой данных из пула соединений A / R. Таким образом, соединение, для которого я выполняю оператор «use», может не быть тем соединением, которое использует модель, и в этом случае она запрашивает неверную базу данных.

Я прочитал код Rails A / R (версия 3.0.3) и придумал этот код для выполнения в цикле вместо оператора «use»:

ActiveRecord::Base.clear_active_connections!
ActiveRecord::Base.establish_connection(each_customer_database_config)

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

Безопасно ли это делать в работающем веб-приложении? Есть ли другой способ сделать это?

Ответы [ 4 ]

1 голос
/ 24 февраля 2011

Переключение IMO на новое соединение с базой данных для разных запросов - очень дорогая операция. AR поддерживает ограниченный пул соединений.

Полагаю, вам следует перейти на PostgreSQL, где у вас есть концепция схем.

В идеальном мире SQL это структура базы данных

database --> schemas --> tables

В MYSQL базы данных и схемы - это одно и то же. Postgres имеет отдельные схемы, которые могут содержать таблицы для разных клиентов. Вы можете переключать схему на лету, не меняя соединение AR, установив

ActiveRecord::Base.connection.set_schema_search_path("CUSTOMER's SCHEMA")

Для его разработки нужно немного взломать.

1 голос
/ 16 мая 2011

Переключение базы данных путем подключения / отключения очень медленно, и не будет работать из-за пулов соединений AR и внутренних кешей.Попробуйте использовать ActiveRecord::Base.table_name_prefix = "customer_" и держите базу данных постоянной.

0 голосов
/ 15 июня 2011

Почему бы не сохранить одну и ту же базу данных и таблицы и просто иметь каждую вашу модель принадлежащей клиенту?Тогда вы можете найти все модели для этого клиента:

Customer.all.each do |customer|
  customer.widgets
  customer.wodgets
  # etc
end
0 голосов
/ 24 февраля 2011

Прямо сейчас у вас соединения в ActiveRecord могут быть на уровне класса.Он выглядит на основе потока, потому что до 1,9 рубиновых потоков засасывается, так что реализации использовали процесс вместо потока, но это может быть неверно долго.

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

Код будет выглядеть примерно так.(Я не проверял это)

Customer.all.each do |customer|
     c_class = Class.new(ActiveRecord::Base)
     c_class.establish_connection(each_customer_database_config)
     c_class.table_name = customor.table_name()
     c_class.do_something_on_diff_models_using_cutomer_from_diff_conn(customer.id)
     c_class.clear_active_connections!
end
...