Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Criando aplicações multi-tenant com bancos sepa...

Criando aplicações multi-tenant com bancos separados

Palestra feita no GuruSC 2011

Avatar for Gabriel Sobrinho

Gabriel Sobrinho

October 02, 2011
Tweet

More Decks by Gabriel Sobrinho

Other Decks in Programming

Transcript

  1. MULTI-INSTANCE • Para cada novo cliente, uma nova instância da

    aplicação; • Alto custo de infra-estrutura; • Ambientes totalmente isolados;
  2. MULTI-TENANT • Apenas uma instância para atender todos os clientes;

    • Baixo custo de infra-estrutura; • Aumenta a complexidade para isolar os dados;
  3. PRINCIPAIS ABORDAGENS MULTI-TENANT • Separação lógica usando o cliente como

    referência; • Schemas separados para cada cliente; • Bancos totalmente isolados;
  4. SEPARAÇÃO LÓGICA • Responsabilidade da aplicação; • Banco de dados

    cresce conforme quantidade de clientes; • Podemos escalar usando master/slave ou sharding; • Compartilhamento de dados entre os clientes;
  5. SCHEMAS • Atualmente somente no PostgreSQL; • Migrations devem ser

    escritas em SQL; • Controle de versão do banco insiste em ficar no schema public; • Desconheço o uso desta abordagem em produção;
  6. BANCOS ISOLADOS • Dados totalmente isolados! • Bancos menores e

    possibilidade de backups incrementais; • Cada cliente pode definir qual banco de dados quer utilizar; • Bancos separados geograficamente;
  7. BANCOS ISOLADOS • Permite instalar o banco de dados no

    próprio cliente; • Permite uso de recursos específicos do banco como procedures; • Permite o acesso ao banco de dados pelo cliente;
  8. ACTIVERECORD CONNECTIONS • Projeto recente, ainda não está maduro; •

    Ainda não trabalhei na parte onde todos os clientes são migrados ao mesmo tempo; • Entra em produção no próximo mês (Novembro 2011);
  9. ACTIVERECORD CONNECTIONS • Pool de conexões para cada cliente; •

    Cuidado ao usar o mesmo servidor para todos os bancos! • Altera somente dois métodos do activerecord usando um proxy;
  10. def using_connection(connection_name, connection_spec) self.proxy_connection = ConnectionProxy.new(connection_name, connection_spec) def self.connection_pool connection_handler.retrieve_connection_pool(proxy_connection)

    end def self.retrieve_connection connection_handler.retrieve_connection(proxy_connection) end yield ensure self.proxy_connection = nil def self.connection_pool connection_handler.retrieve_connection_pool(self) end def self.retrieve_connection connection_handler.retrieve_connection(self) end end
  11. APLICANDO NA PRÁTICA class Customer < ActiveRecord::Base attr_accessible :name, :domain,

    :database serialize :database validates :name, :domain, :database, presence: true validates :name, :domain, uniqueness: { allow_blank: true } def using_connection(&block) ActiveRecord::Base.using_connection(id, database, &block) end end
  12. APLICANDO NA PRÁTICA class ApplicationController < ActionController::Base protect_from_forgery around_filter :handle_customer

    protected def handle_customer(&block) customer = Customer.find_by_domain!(request.host) customer.using_connection(&block) end end
  13. APLICANDO NA PRÁTICA namespace :db do desc "Migrate each customer's

    database" task :migrate => :environment do verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil ActiveRecord::Migration.verbose = verbose Customer.find_each do |customer| puts "migrating customer #{customer.domain}" if verbose customer.using_connection do ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, version) end end end end