Um pouco de fundo
Uso a jóia Apartment para executar um aplicativo de multilocação por anos. Agora, recentemente, chegou a necessidade de dimensionar o banco de dados em hosts separados, o servidor db simplesmente não aguenta mais (as leituras e gravações estão ficando demais) - e sim, eu dimensionei o hardware ao máximo (dedicado hardware, 64 núcleos, 12 unidades Nvm-e no RAID 10, 384 GB de RAM etc.).
Eu estava pensando em fazer isso por inquilino (1 inquilino = 1 configuração / pool de conexão com o banco de dados), pois seria uma maneira "simples" e eficiente de obter até number-of-tenants
mais vezes mais capacidade sem fazer muitas alterações no código do aplicativo.
Agora, estou executando o rails 4.2 atm., Em breve atualizando para o 5.2. Eu posso ver que o Rails 6 adiciona suporte para definições de conexão por modelo, no entanto, isso não é realmente o que eu preciso, pois tenho um esquema de banco de dados completamente espelhado para cada um dos meus 20 inquilinos. Normalmente eu alterno "banco de dados" por solicitação (no middleware) ou por trabalho em segundo plano (sidekiq middleware), no entanto, isso atualmente é trivial e tratado pela jóia Apartment, pois apenas define o search_path
Postgresql e realmente não altera a conexão real. Ao mudar para uma estratégia de hospedagem por inquilino, precisarei mudar toda a conexão por solicitação.
Questões:
- Entendo que eu poderia fazer um trabalho
ActiveRecord::Base.establish_connection(config)
por solicitação / plano de fundo - no entanto, como também entendo, isso aciona um handshake de conexão de banco de dados totalmente novo a ser feito e um novo pool de banco de dados a ser gerado nos trilhos - certo? Eu acho que seria um suicídio de desempenho fazer esse tipo de sobrecarga em cada solicitação do meu aplicativo. - Portanto, eu estou querendo saber se alguém pode ver a opção com trilhos, por exemplo, pré-estabelecendo várias (total de 20) conexões / pools de banco de dados desde o início (por exemplo, na inicialização do aplicativo) e depois alternar entre esses pools por solicitação? Para que as conexões db já estejam feitas e prontas para serem usadas.
- Tudo isso é apenas uma péssima idéia, e devo procurar uma abordagem diferente? Por exemplo, 1 instância do aplicativo = uma conexão específica com um inquilino específico. Ou alguma outra coisa.
fonte
master
ramificação atual do Rails . A execução do Rails Egde seria uma opção ou back-prting desse recurso para sua versão atual do Rails?ActiveRecord::Base.connected_to(shard: :shard_one) do ... end
significa que o pool será (re) usado, em vez de criar uma conexão totalmente nova toda vez?Respostas:
Pelo que entendi, existem 4 padrões para aplicativos de multilocação:
1. Modelo dedicado / múltiplos ambientes de produção
Cada instância ou instância de banco de dados hospeda completamente diferentes aplicativos de inquilino e nada é compartilhado entre os inquilinos.
Este é um aplicativo de instância e um banco de dados para 1 inquilino. O desenvolvimento seria fácil como se você atendesse apenas 1 inquilino. Mas será um pesadelo para os devops, se você tiver, digamos, 100 inquilinos.
2. Segregação física de inquilinos
1 aplicativo de instância para todos os inquilinos, mas 1 banco de dados para 1 inquilino. É isso que você está procurando. Você pode usar
ActiveRecord::Base.establish_connection(config)
, ou usar gemas, ou atualizar para o Rails 6, como outro sugere. Veja a resposta para (2) abaixo.3. Modelo de esquema isolado / Segregações lógicas
Em um esquema isolado, as tabelas de inquilino ou os componentes do banco de dados são agrupados em um esquema lógico ou espaço de nome e separados de outros esquemas de inquilino; no entanto, o esquema é hospedado na mesma instância de banco de dados.
Um aplicativo de instância e um banco de dados para todos os inquilinos, como você faz com a jóia do apartamento.
4. Componente parcialmente isolado
Nesse modelo, os componentes que possuem funcionalidades comuns são compartilhados entre os inquilinos, enquanto os componentes com funções exclusivas ou não relacionadas são isolados. Na camada de dados, dados comuns, como os que identificam os inquilinos, são agrupados ou mantidos em uma única tabela, enquanto os dados específicos do inquilino são isolados na camada da tabela ou da instância.
Quanto ao (1),
ActiveRecord::Base.establish_connection(config)
não use handshake para db por solicitação, se você usá-lo corretamente. Você pode conferir aqui e ler todos os comentários aqui .Quanto a (2), se você não quiser usar
establish_connection
, poderá usar o multiverso gem (funciona para o rails 4.2) ou outras gemas. Ou, como outro sugere, você pode atualizar para o Rails 6.Edit: Multiverse gem está usando
establish_connection
. Anexará oedatabase.yml
criará uma classe base para que cada subclasse compartilhe a mesma conexão / pool. Basicamente, isso reduz nosso esforço de usoestablish_connection
.Quanto a (3), a resposta:
Se você não possui tantos inquilinos e seu aplicativo é bastante complexo, sugiro que você use o padrão Modelo Dedicado. Então você escolhe uma instância de aplicativo = uma conexão específica com um inquilino específico. Você não precisa tornar seus aplicativos mais complexos adicionando várias conexões ao banco de dados.
Mas se você tiver muitos inquilinos, sugiro que você use a Segregação física de inquilinos ou o componente parcialmente isolado depende do seu processo de negócios.
De qualquer forma, você deve atualizar / reescrever seu aplicativo para estar em conformidade com a nova arquitetura.
fonte
establish_connection
um modelo como este:class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); end
e dizer que possui 5 modelo, cria 5 conjuntos de conexões para o DB_SECOND_TENANT. E cada piscina é tratada igualmente. Portanto, você não cria um pool por solicitação, mas porestablish_connection
.Pelo que entendi, (2) deve ser possível com a comutação de conexão manual no Rails 6.
fonte
Apenas alguns dias atrás, o sharding horizontal foi adicionado à
master
ramificação do Ruby on Rails no GitHub. Atualmente, esse recurso não é lançado oficialmente, mas dependendo da versão do Rails do seu aplicativo, você pode considerar o uso do Railsmaster
adicionando isso ao seuGemfile
:Com esse novo recurso, você pode tirar proveito do pool de conexão de banco de dados do Rails e alternar o banco de dados com base nas condições.
Não usei esse novo recurso, mas parece bem direto:
Você não adicionou muitos detalhes sobre como determinar o número do inquilino ou como a autorização é feita no seu aplicativo. Mas eu gostaria de tentar determinar o número inquilino o mais cedo possível na
application_controller
em umaaround_action
. Algo assim pode ser um ponto de partida:fonte
ActiveRecord::Base.connected_to ... do
bloco, ele usaria a conexão padrão novamente.master
ramo atual do Rails .