Un poco de historia
He estado usando la gema del apartamento para ejecutar una aplicación de arrendamiento múltiple durante años. Recientemente, ha llegado la necesidad de escalar la base de datos en hosts separados, el servidor de db simplemente no puede seguir más (tanto las lecturas como las escrituras se están volviendo demasiado), y sí, amplié el hardware al máximo (dedicado hardware, 64 núcleos, 12 unidades Nvm-e en raid 10, 384 Gb de ram, etc.).
Estaba considerando hacer esto por inquilino (1 inquilino = 1 configuración / grupo de conexión de base de datos) ya que esa sería una forma "simple" y eficiente de obtener hasta number-of-tenants
veces más capacidad sin hacer un montón de cambios en el código de la aplicación.
Ahora estoy ejecutando rails 4.2 atm., Pronto actualizo a 5.2. Puedo ver que rails 6 agrega soporte para definiciones de conexión por modelo, sin embargo, eso no es realmente lo que necesito, ya que tengo un esquema de base de datos completamente reflejado para cada uno de mis 20 inquilinos. Por lo general, cambio la "base de datos" por solicitud (en middleware) o por trabajo en segundo plano (middleware sidekiq), sin embargo, esto es actualmente trivial y se maneja en la gema del apartamento, ya que solo establece el search_path
Postgresql y realmente no cambia la conexión real. Al cambiar a una estrategia de alojamiento por inquilino, tendré que cambiar toda la conexión por solicitud.
Preguntas:
- Entiendo que podría hacer un trabajo
ActiveRecord::Base.establish_connection(config)
por solicitud / en segundo plano; sin embargo, como también entiendo, eso desencadena un protocolo de enlace de conexión de base de datos completamente nuevo y un nuevo grupo de bases de datos para generar en rieles, ¿verdad? Supongo que sería un suicidio de rendimiento hacer ese tipo de sobrecarga en cada solicitud a mi solicitud. - Por lo tanto, me pregunto si alguien puede ver la opción con rieles de, por ejemplo, preestablecer múltiples (un total de 20) conexiones / grupos de bases de datos desde el principio (por ejemplo, al iniciar la aplicación), y luego simplemente cambiar entre esos grupos por solicitud. Para que las conexiones db ya estén hechas y listas para ser utilizadas.
- ¿Es todo esto solo una pobre y pobre idea, y debería buscar un enfoque diferente? Por ejemplo, 1 instancia de aplicación = una conexión específica a un inquilino específico. O algo mas.
fuente
master
rama actual de Rails . ¿Ejecutar Rails Egde sería una opción o retroceder esa característica a su versión actual de Rails?ActiveRecord::Base.connected_to(shard: :shard_one) do ... end
significa que el grupo será (re) utilizado, en lugar de crear una conexión completamente nueva cada vez?Respuestas:
Según tengo entendido, hay 4 patrones para la aplicación multicliente:
1. Modelo dedicado / entornos de producción múltiple
Cada instancia o instancia de base de datos aloja completamente una aplicación de inquilino diferente y nada se comparte entre los inquilinos.
Esta es 1 aplicación de instancia y 1 base de datos para 1 inquilino. El desarrollo sería fácil como si sirviera solo a 1 inquilino. Pero será una pesadilla para los devops si tienes, por ejemplo, 100 inquilinos.
2. Segregación física de inquilinos
1 aplicación de instancia para todos los inquilinos pero 1 base de datos para 1 inquilino. Esto es lo que estás buscando. Puede usar
ActiveRecord::Base.establish_connection(config)
, o usar gemas, o actualizar a Rails 6 como otros sugieren. Vea la respuesta para (2) a continuación.3. Modelo de esquema aislado / segregaciones lógicas
En un esquema aislado, las tablas de inquilinos o los componentes de la base de datos están agrupados bajo un esquema lógico o espacio de nombres y separados de otros esquemas de inquilinos, sin embargo, el esquema está alojado en la misma instancia de la base de datos.
1 aplicación de instancia y 1 base de datos para todos los inquilinos, como lo hace con la joya de apartamentos.
4. Componente parcialmente aislado
En este modelo, los componentes que tienen funcionalidades comunes se comparten entre los inquilinos, mientras que los componentes con funciones únicas o no relacionadas están aislados. En la capa de datos, los datos comunes, como los datos que identifican a los inquilinos, se agrupan o mantienen en una sola tabla, mientras que los datos específicos del inquilino se aíslan en la tabla o en la capa de instancia.
En cuanto a (1),
ActiveRecord::Base.establish_connection(config)
no apretón de manos a db por solicitud si lo usa correctamente. Puedes consultar aquí y leer todos los comentarios aquí .En cuanto a (2), si no desea usar
establish_connection
, puede usar gemas multiverso (funciona para rieles 4.2) u otras gemas. O, como otros sugieren, puede actualizar a Rails 6.Editar: gema multiverso está utilizando
establish_connection
. Anexarádatabase.yml
y creará una clase base para que cada subclase comparta la misma conexión / grupo. Básicamente, reduce nuestro esfuerzo de usoestablish_connection
.En cuanto a (3), la respuesta:
Si no tiene tantos inquilinos y su aplicación es bastante compleja, le sugiero que use un patrón de modelo dedicado. Entonces, elige 1 instancia de aplicación = una conexión específica para un inquilino específico. No tiene que hacer que sus aplicaciones sean más complejas agregando múltiples conexiones de base de datos.
Pero si tiene muchos inquilinos, le sugiero que utilice la segregación física de inquilinos o el componente parcialmente aislado depende de su proceso comercial.
De cualquier manera, debe actualizar / reescribir su aplicación para cumplir con la nueva arquitectura.
fuente
establish_connection
un modelo como este:class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); end
y dice que tiene 5 modelos, crea 5 grupos de conexiones para DB_SECOND_TENANT. Y cada grupo se trata por igual. Por lo tanto, no crea un grupo por solicitud, sino porestablish_connection
.Por lo que entiendo, (2) debería ser posible con el cambio de conexión manual en Rails 6.
fuente
Hace solo un par de días se agregó el fragmentación horizontal a la rama de Ruby on Rails
master
en GitHub. Actualmente, esta característica no se ha lanzado oficialmente, pero dependiendo de la versión de Rails de su aplicación, puede considerar usar Railsmaster
agregando esto a suGemfile
:Con esta nueva característica, puede aprovechar el conjunto de conexiones de la base de datos de Rails y cambiar la base de datos según las condiciones.
No he usado esta nueva función, pero parece bastante sencillo:
No agregó muchos detalles sobre cómo determina el número de inquilino o cómo se realiza la autorización en su solicitud. Pero me gustaría tratar de determinar el número de empresa tan pronto como sea posible en el
application_controller
en unaaround_action
. Algo como esto podría ser un punto de partida:fuente
ActiveRecord::Base.connected_to ... do
bloque, volverá a utilizar la conexión predeterminada.master
rama actual de Rails .