Un peu d'histoire
J'utilise le Gem Appartement pour exécuter une application multi-locataire depuis des années. Récemment, le besoin de faire évoluer la base de données sur des hôtes séparés est arrivé, le serveur db ne peut tout simplement plus suivre (les lectures et les écritures deviennent trop) - et oui, j'ai dimensionné le matériel au maximum (dédié matériel, 64 cœurs, 12 disques Nvm-e en raid 10, 384 Go de RAM, etc.).
number-of-tenants
J'envisageais de faire cela par locataire (1 locataire = 1 configuration / pool de connexion à la base de données) car ce serait un moyen "simple" et efficace pour obtenir jusqu'à -toujours plus de capacité sans effectuer de charges de changements de code d'application.
Maintenant, j'utilise des rails 4.2 atm., Je passerai bientôt à 5.2. Je peux voir que rails 6 ajoute la prise en charge des définitions de connexion par modèle, mais ce n'est pas vraiment ce dont j'ai besoin, car j'ai un schéma de base de données complètement en miroir pour chacun de mes 20 locataires. En général, je change de "base de données" par demande (dans le middleware) ou par tâche d'arrière-plan (middlekiq middleware), mais cela est actuellement trivial et géré par la gemme Appartement, car il définit simplement le search_path
dans Postgresql et ne change pas vraiment la connexion réelle. Lors du passage à une stratégie d'hébergement par locataire, je devrai changer la connexion entière par demande.
Des questions:
- Je comprends que je pourrais faire un travail
ActiveRecord::Base.establish_connection(config)
par demande / en arrière-plan - cependant, comme je le comprends aussi, cela déclenche une nouvelle négociation de connexion à la base de données et un nouveau pool de bases de données à apparaître dans les rails - non? Je suppose que ce serait un suicide de performance de faire ce genre de frais généraux sur chaque demande unique à ma demande. - Je me demande donc si quelqu'un peut voir l'option avec des rails par exemple de préétablir plusieurs (totaux 20) connexions / pools de base de données depuis le début (par exemple au démarrage de l'application), puis basculer entre ces pools par demande? Pour que les connexions DB soient déjà établies et prêtes à être utilisées.
- Tout cela n'est-il qu'une mauvaise mauvaise idée, et devrais-je plutôt rechercher une approche différente? Par exemple, 1 instance d'application = une connexion spécifique à un locataire spécifique. Ou autre chose.
la source
master
branche Rails actuelle . L'exécution de Rails Egde serait-elle une option ou une sauvegarde de cette fonctionnalité dans votre version actuelle de Rails?ActiveRecord::Base.connected_to(shard: :shard_one) do ... end
signifie que le pool sera (ré) utilisé, au lieu de créer une nouvelle connexion à chaque fois?Réponses:
Si je comprends bien, il existe 4 modèles pour une application multi-locataire:
1. Modèle dédié / environnements de production multiples
Chaque instance ou instance de base de données héberge entièrement une application de locataire différente et rien n'est partagé entre les locataires.
Il s'agit d'une application d'instance et d'une base de données pour 1 locataire. Le développement serait facile comme si vous ne serviez qu'un seul locataire. Mais ce sera un cauchemar pour les devops si vous avez, disons, 100 locataires.
2. Séparation physique des locataires
1 application d'instance pour tous les locataires, mais 1 base de données pour 1 locataire. C'est ce que vous recherchez. Vous pouvez utiliser
ActiveRecord::Base.establish_connection(config)
, ou utiliser des gemmes, ou mettre à jour vers Rails 6 comme d'autres suggèrent. Voir la réponse pour (2) ci-dessous.3. Modèle de schéma isolé / ségrégations logiques
Dans un schéma isolé, les tables de locataires ou les composants de base de données sont regroupés sous un schéma logique ou un espace de noms et séparés des autres schémas de locataires, mais le schéma est hébergé dans la même instance de base de données.
1 application d'instance et 1 base de données pour tous les locataires, comme vous le faites avec gem d'appartement.
4. Composant partiellement isolé
Dans ce modèle, les composants qui ont des fonctionnalités communes sont partagés entre les locataires tandis que les composants avec des fonctions uniques ou non liées sont isolés. Au niveau de la couche de données, les données courantes telles que les données qui identifient les locataires sont regroupées ou conservées dans une seule table tandis que les données spécifiques au locataire sont isolées au niveau de la table ou de la couche d'instance.
Quant à (1),
ActiveRecord::Base.establish_connection(config)
ne pas établir de liaison avec db par requête si vous l'utilisez correctement. Vous pouvez vérifier ici et lire tous les commentaires ici .Quant à (2), si vous ne voulez pas utiliser
establish_connection
, vous pouvez utiliser le multivers gem (il fonctionne pour les rails 4.2), ou d'autres gemmes. Ou, comme d'autres le suggèrent, vous pouvez mettre à jour vers Rails 6.Edit: la gemme multivers utilise
establish_connection
. Il ajoutera ledatabase.yml
et créera une classe de base afin que chaque sous-classe partage la même connexion / pool. Fondamentalement, cela réduit nos efforts d'utilisationestablish_connection
.Quant à (3), la réponse:
Si vous n'avez pas autant de locataires et que votre application est assez complexe, je vous suggère d'utiliser le modèle de modèle dédié. Donc, vous optez pour 1 instance d'application = une connexion spécifique à un locataire spécifique. Vous n'avez pas à rendre vos applications plus complexes en ajoutant plusieurs connexions à la base de données.
Mais si vous avez plusieurs locataires, je vous suggère d'utiliser la séparation physique des locataires ou le composant partiellement isolé en fonction de votre processus métier.
Dans tous les cas, vous devez mettre à jour / réécrire votre application pour vous conformer à la nouvelle architecture.
la source
establish_connection
dans le modèle comme celui-ciclass SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); end
:, et que vous avez 5 modèles, vous créez 5 pools de connexions au DB_SECOND_TENANT. Et chaque piscine est traitée de manière égale. Donc, vous ne créez pas de pool par demande, mais parestablish_connection
.D'après ce que je comprends, (2) devrait être possible avec une commutation de connexion manuelle dans Rails 6.
la source
Il y a quelques jours à peine, un sharding horizontal a été ajouté à la
master
branche Ruby on Rails sur GitHub. Actuellement, cette fonctionnalité n'est pas officiellement publiée, mais en fonction de la version de Rails de votre application, vous pouvez envisager d'utiliser Railsmaster
en l'ajoutant à votreGemfile
:Avec cette nouvelle fonctionnalité, vous pouvez profiter du pool de connexions à la base de données de Rails et basculer la base de données en fonction des conditions.
Je n'ai pas utilisé cette nouvelle fonctionnalité, mais cela semble assez simple:
Vous n'avez pas ajouté beaucoup de détails sur la façon dont vous déterminez le numéro de locataire ou sur la façon dont l'autorisation est effectuée dans votre demande. Mais je voudrais essayer de déterminer le nombre de locataires le plus tôt possible dans le
application_controller
dans unaround_action
. Quelque chose comme ça pourrait être un point de départ:la source
ActiveRecord::Base.connected_to ... do
bloc, il utilise à nouveau la connexion par défaut.master
branche Rails actuelle .