2011-02-24 13 views
5

Tengo varias aplicaciones Rails ejecutándose en un solo servidor MySQL. Todos ejecutan la misma aplicación, y todas las bases de datos tienen el mismo esquema, pero cada base de datos pertenece a un cliente diferente.Manera segura de iterar sobre múltiples bases de datos en Rails

Conceptualmente, esto es lo que quiero hacer:

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

Este enfoque no funciona porque, cuando este se ejecuta en una solicitud web, las clases del modelo subyacentes caché diferentes conexiones de bases de datos de la conexión de A/R piscina. Entonces, la conexión en la que ejecuto la instrucción "use" puede no ser la conexión que utiliza el modelo, en cuyo caso consulta la base de datos incorrecta.

He leído a través del código de Rails A/R (versión 3.0.3), y se acercó con este código a ejecutar en el circuito, en lugar de la "utilización" declaración:

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

creo que el grupo de conexiones es por subproceso, por lo que parece que esto afectaría al grupo de conexiones y lo restablecería solo para el único subproceso en el que está activada la solicitud web. Pero si las conexiones se comparten de alguna manera que no veo, no me gustaría que ese código cause estragos con otras solicitudes web activas en la misma aplicación.

¿Es seguro hacerlo en una aplicación web en ejecución? Hay alguna otra manera de hacer esto?

Respuesta

0

Ahora que tiene conexiones en ActiveRecord puede ser por nivel de clase. Se ve por subproceso porque está antes de los subprocesos de 1,9 rubíes, por lo que las implementaciones usaban proceso en lugar de subproceso, pero puede que no sea así por mucho tiempo.

Pero como AR usa un hilo por modelo. Puede crear diferentes modelos simulados para cada base de datos que tenga. Entonces, usando la respuesta dada en this question.

El código se verá algo como esto. (No lo he probado)

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 
1

IMO cambiar a una nueva conexión de base de datos para diferentes solicitudes es una operación muy costosa. AR mantiene un grupo limitado de conexiones.

Supongo que deberías pasar a PostgreSQL, donde tienes el concepto de esquemas.

En un mundo SQL ideales Esta es la estructura de una base de datos

database --> schemas --> tables 

en MySQL, base de datos y los esquemas son la misma cosa. Postgres tiene esquemas separados, que pueden contener tablas para diferentes clientes. Se puede cambiar el esquema sobre la marcha sin necesidad de cambiar la conexión AR mediante el establecimiento de

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

desarrollo que requiere un poco de piratería sin embargo.

+1

Solo para aclarar, soporte de MySQL _does_ esquemas SQL, pero originalmente y infelicitously los llamó "bases de datos". La palabra clave SQL "SCHEMA" se reconoce desde la versión 5.0.2 (diciembre de 2004). Decir "USE foo" para MySQL es análogo a establecer una 'ruta_búsqueda' de la conexión PostgreSQL a un solo espacio de nombres" foo ". – pilcrow

+0

Absolutamente. Incluso puede escribir "create schema abc" en lugar de "create database abc" en MYSQL. – Jagira

1

Cambiar la base de datos mediante la conexión/desconexión es realmente lento, y no va a funcionar debido a que las agrupaciones de conexiones AR son cachés internos. Intente usar ActiveRecord::Base.table_name_prefix = "customer_" y mantenga la base de datos constante.

0

¿Por qué no mantener el mismo db y tablas y simplemente tener cada uno de sus modelos pertenecientes a un cliente?A continuación, puede encontrar todos los modelos para ese cliente con:

Customer.all.each do |customer| 
    customer.widgets 
    customer.wodgets 
    # etc 
end 
Cuestiones relacionadas