Um produto para muitos clientes: implementando multitenancy

São diversos as aplicações web disponíveis, como quadros eletrônicos, sistemas de tracking, email e aplicações para empresas, ou até mesmo controle de clientes e vendas. Eles até ganharam um pomposo nome dentro do cloud computing: Software as a Service (SAAS).

O que essas aplicações possuem em comum? Todas elas atendem diversos clientes sem que um tenha conhecimento da existência dos outros.

Em um post recente no Tectura.com.br foram discutidas vantagens e desvantagens de diversas abordagens para produtos com a necessidade de suportar mais de um tenant.

A Microsoft categoriza três tipos de abordagens dependendo do nível de compartilhamento de recursos entre os clientes e apresente um relatório onde analisa os custos contra a segurança.

Em um extremo, nada é compartilhado entre cada cliente: para cada nova conta criada dentro de seu serviço, é criada uma nova máquina na cloud e uma instalação limpa é executada, com seu próprio banco.

Dois servidores, dois bancos

Nessa abordagem o processo de autorização é automático, um cliente é criado através da instalação automática de uma nova máquina virtual e os recursos são compartilhados se o ambiente for uma cloud.

Na outra ponta, tudo é compartilhado: novos clientes são inseridos no mesmo conjunto de máquinas e a instalação é feita através de um simples insert no banco, adicionando um novo cliente. Qual abordagem escolher?

Um servidor, um banco

A autorização é feita programaticamente na aplicação, os recursos são compartilhados entre todas elas, escala-se tipicamente através do uso de load balancers e replicação master/slave e a cada novo cliente basta executar um insert no banco.

Em qualquer abordagem onde o banco seja compartilhado por diversas empresas, precisamos garantir a segurança dos dados de cada uma, para que nenhum acesso indevido ocorra, trazendo preocupações de autorização para dentro do código de nossa aplicação. Caso seja criado para cada cliente um banco em uma máquina no cloud, essa questão fica concentrada em um único ponto da arquitetura, a segurança está implícita.

Dois servidores, um banco

Diversos servidores e um banco implica em clientes não afetarem uns aos outros, um controle programático de autorização e novos clientes são criados a partir da instalação de um novo contexto web.

Ao mesmo tempo, o processo de customização de seu serviço por cliente também é afetada de acordo com o nível de compartilhamento de dados entre eles. Na abordagem onde os clientes compartilham o mesmo servidor web, a customização é uma preocupação de nossa aplicação, enquanto ao utilizarmos aplicações web distintas para cada cliente, podemos facilmente separar customizações por instância, por deploy efetuado.

A escalabilidade é afetada pois compartilhar recursos em memória na camada web, entre eles dados cacheados do banco ou filas, permitem diminuir o tempo de processamento ou de latência, aumentando o número de requisições suportados.

O processo de escalar também está diretamente ligado: quando uma máquina não aguentar mais as requisições, será levantada uma outra máquina que funcionará em cluster para um, n ou todos os clientes?

Outro fator importante é como limitar o uso do serviço de acordo com as regras contratadas pelo cliente e controlar o dano que um pode causar a outros. Em um pico de uso por parte dos usuários de um cliente, ativamos o chargeback, dependendo do tipo de serviço que é prestado. Implementações comuns de chargeback em cloud (baseados em virtualização) permitem um pico de consumo temporário ou até mesmo mover a aplicação de uma máquina para outra sem que cliente algum perçeba o que está acontecendo.

Dois servidores, um banco

Diversos servidores e um banco implica em clientes não afetarem uns aos outros, um controle programático de autorização e novos clientes são criados a partir da instalação de um novo contexto web.

Ao invés daqueles usuários atrapalharem a performance de outros clientes, é alocado memória e processador distintos dos atuais para ele.

A simplicidade do código é um dos fatores mais afetados pela escolha feita: um código que sabe da existência de múltiplos clientes está diretamente ligado a uma chave que identifica o cliente atual. E essa ligação se reflete por todos os lados do código, uma vez que o comportamento da aplicação em geral depende dele: uma chave estrangeira, um relacionamento, que vai permear toda a aplicação.

Caso a aplicação implemente o suporte a multi tenant através de instalções web distintas, com configurações para cada cliente apontando para um bancos distintos, o código fica mais simples e fácil de manter, uma vez que não é necessário se preocupar com a existência de outros clientes.

Por fim, uma preocupação que surge para a empresa provedora dos serviços é de como agrupar os dados existentes em todos os clientes e gerar relatórios de administração e estatísticas que permitam a melhora do serviço prestado. Quando possuimos um único banco, escalado através de master slave ou múltiplos nós, basta executarmos queries longas (sql ou não), mas se o sistema estiver distribuido entre diversos bancos sem ligação entre si, um processo de batch deve rodar para agrupar os dados e permitir o consumo posterior pelas ferramentas de BI.

É importante analisar todos esses pontos antes de tomar a decisão se o controle de espaço de aplicação por cliente será feito no nosso código, na camada web, no banco ou em algum outro ponto. Como discutimos bastante no curso de arquitetura, toda e qualquer decisão implica em um tradeoff: o importante é saber o que está sendo trocado e qual é o impacto disso.

7 Comentários

  1. Antonio Anderson Souza 23/08/2010 at 18:30 #

    Guilherme,

    Belo post, já arquitetei dois serviços distintos todos multi-tenant o primeiro foi um serviço de PABX Virtual (o Basix http://www.basix.com.br), e o outro estou no meio do desenvolvimento é o Vizir (http://www.vizir.com.br) um serviço de monitorção de marcas em midias sociais.

    Nas duas situações citadas acima a decisão foi implementar o multi-tenant na aplicação, mesmo conhecendo todos os tradeoffs, mas o Basix tivemos uma necessidade especifica que gerou um ambiente híbrido, e acho que vale a pena compartilhar com vocês. Com o evoluir do produto (novas versões), precisamos criar 2 ambientes com versões distintas, pois haviam clientes early adopters que podiam ser migrados para novas versões rapidamente, e clientes que necssitavam de um tratamento diferenciado, e isto resultava em mais tempo para ter uma nova versão do software, desta forma apesar do Basix ter sido desenvolvido com multi-tenant na aplicação, com todos os clientes compartilhando o mesmo DB, e mesma instancia de aplicação, fomos obrigados a criar 2 instancias do Basix(banco de dados, e aplicação), assim mantemos sempre 2 versões rodando, e pudemos atender a estas demandas especificas.

    Abraços,

    Antonio Anderson Souza
    @antonioams

  2. Rafael de F. Ferreira 27/08/2010 at 13:33 #

    No caso de banco compartilhado, uma ferramenta muito útil são os Filters do hibernate (ou equivalente do seu ORM) para automáticamente discriminar as entidades pelo tenant.

    http://docs.jboss.org/hibernate/core/3.3/reference/en/html/filters.html
    http://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#entity-hibspec-filters

  3. Guilherme Silveira 28/08/2010 at 10:17 #

    Oi Antonio,

    Essa abordagem de distinguir os tipos de clientes por algum motivo (no caso os early adopters) e por isso atualizar máquinas distintas costuma estar ligado com o blue-green deployment também.

    Ferreira, o Rails possui um similar também. Gosto dos filtros mais do que interceptors pois deixam explícito que está acontecendo algo. Ao mesmo tempo não fugimos do problema do esquecimento das queries.

    Abraço

  4. Fabio Kung 02/09/2010 at 00:28 #

    Excelente post. Fico contente de ver o bom uso de Cloud IaaS 🙂

  5. Rafael Viana 02/09/2010 at 17:35 #

    Ótimo assunto, deveria ser discutido com uma frequencia maior.

    @Rafael de F. Ferreira

    Tem um porém ao utilizar filtros do Hibernate, pois eles não são inicializados por padrão.Então deve haver um cuidado, já que se esquecer de ativar o filtro em algum lugar é problema na certa.

    Eu acredito que a abordagem ideal é ter uma verificação na camada do DAO ao realizar consultas, e SEMPRE fazer esse acesso dos dados pelo DAO. Qual abordagem vocês aconselham?

  6. Flávio R. C. Sousa 03/09/2010 at 16:27 #

    Ótimo post. Um artigo recente relacionado com o assunto no contexto geral de Cloud “Who’s Driving this Cloud? Towards Efficient Migration for Elastic and Autonomic Multitenant Databases” http://bit.ly/dCT8ta

Deixe uma resposta