O documento discute abordagens de multitenancy em Rails, incluindo usar uma única instância e banco de dados, usar várias instâncias e bancos de dados separados, e como os autores implementaram multitenancy em um e-commerce usando Rails. Eles desenvolveram um middleware Rack que mapeia domínios para tenants e troca configurações como banco de dados ao processar requisições, permitindo escalar horizontalmente. Testes mostraram que a abordagem permite 300 requisições por segundo com consumo estável de memória.
13. ABORDAGENS
uma instância e uma base de dados
• facilidade para novo tenant
14. ABORDAGENS
uma instância e uma base de dados
• facilidade para novo tenant
• deploy simples
15. ABORDAGENS
uma instância e uma base de dados
• facilidade para novo tenant
• deploy simples
• arquivos centralizados
16. ABORDAGENS
uma instância e uma base de dados
• facilidade para novo tenant
• deploy simples
• arquivos centralizados
• fácil gerenciamento dos dados compartilhados
17. ABORDAGENS
uma instância e uma base de dados
• facilidade para novo tenant
• deploy simples
• arquivos centralizados
• fácil gerenciamento dos dados compartilhados
• interferências na performance
20. ABORDAGENS
uma instância com vários schemas
• facilidade de customização
21. ABORDAGENS
uma instância com vários schemas
• facilidade de customização
• gerenciamento das migrations
22. ABORDAGENS
uma instância com vários schemas
• facilidade de customização
• gerenciamento das migrations
• dificuldade no gerenciamento dos schemas
23. ABORDAGENS
uma instância com vários schemas
• facilidade de customização
• gerenciamento das migrations
• dificuldade no gerenciamento dos schemas
• postgresql, oracle
30. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
31. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
• escalabilidade de um único tenant
32. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
• escalabilidade de um único tenant
• dificuldade no gerenciamento das bases
33. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
• escalabilidade de um único tenant
• dificuldade no gerenciamento das bases
• dificuldade de deploys
34. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
• escalabilidade de um único tenant
• dificuldade no gerenciamento das bases
• dificuldade de deploys
• alto custo de infra
35. ABORDAGENS
várias instâncias com várias bases
• separação total de dados
• escalabilidade de um único tenant
• dificuldade no gerenciamento das bases
• dificuldade de deploys
• alto custo de infra
(multi-instância)
95. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end
96. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end
97. RACK MIDDLEWARE
module MultiTenant
class TenantAddressMap
...
def get_tenant(options = {})
tenant_hash = @manager.get_tenant(options[:address])
tenant_hash ? Tenant.new(tenant_hash) : nil
end
...
end
end
98. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end
99. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end
100. RACK MIDDLEWARE
module MultiTenant
class Tenant
...
def current!
database_switcher.switch!(self)
log_switcher.switch!(self)
assets_switcher.switch!(self)
end
...
end
end
101. RACK MIDDLEWARE
module MultiTenant
class Tenant
...
def current!
database_switcher.switch!(self)
log_switcher.switch!(self)
assets_switcher.switch!(self)
end
...
end
end
102. RACK MIDDLEWARE
module MultiTenant
class DatabaseSwitcher
...
def switch!(tenant)
if ActiveRecord::Base.connection.current_database !=
tenant.config['database']
ActiveRecord::Base.establish_connection(tenant.config)
end
end
...
end
end
103. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end
104. RACK MIDDLEWARE
def call(env)
begin
tenant = map.get_tenant(:address => address(env))
return respond_302 unless tenant
return respond_403 if tenant.suspended?
tenant.current!
@status, @headers, @response = @app.call(env)
[@status, @headers, @response]
rescue Exception => ex
@logger.error ex.message + "n" + ex.backtrace.join("n")
respond_500
end
end