Multi-Tenant com VRaptor  XML
Índice dos Fóruns » Frameworks e Bibliotecas brasileiros
Autor Mensagem
RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

No primeiro sistema multi-tenant que desenvolvi cada requisição enviava junto o nome do tenant - o nome estava salvo no cliente.
Mas, isso polui muito o código...

Por isso, já havia pensado na solução melhor para um próximo sistema: Ao logar-se no sistema seria criada uma sessão para o usuário. Assim, todas requisições saberiam quem é o cliente que está fazendo...

Durante a leitura do material RR-79 da Caelum que contém explicações sobre REST e Restfulie. Em uma parte fala-se sobre a importância do sistema ser Stateless:
"Stateless systems that provide cacheable resources imply in lesser costs as bandwidth is saved, latency is
minimized, fault tolerancy is increased and processing costs is divided between all origins."

Puts... ai vi que minha idéia não era tão boa... devia pensar em algo diferente.

Ai pensei no seguinte: tenant.example.com.br
Pegar o nome do tenant do subdominio.

Em algumas horas cheguei no seguinte resultado, usando VRaptor.



Contexto 1: Aplicações que utilizam um banco de dados por cliente:




Contexto 2: O mesmo banco de dados para todos os clientes.

Não seria necessário sobrescrever o criador de session factory padrão do VRaptor. Uma vez que, só haveria um banco de dados.
A diferença está no TenantHibernateDao, no qual teria que injetar o Tenant e colocar um filtro para acessar somente os dados corretos.



Exemplo de uso em Dao (fica o mesmo em ambos os contextos).



É só injetar o DAO no Controller e seriam acessados apenas os dados corretos do Tenant.

Isso é só um protótipo. Vocês acham válida essa abordagem?

This message was edited 2 times. Last update was at 22/01/2011 15:35:48


Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
Lucas Cavalcanti
GUJ Hacker
[Avatar]

Membro desde: 08/07/2007 00:08:14
Mensagens: 6396
Offline

acho interessante a abordagem...

só alguns detalhes: CriadorDeSessionFactory deveria ser @ApplicationScoped e o Map é de <Tenant, SessionFactory>

depender do subdomínio para decidir qual é o tenant pode dificultar um pouco o desenvolvimento, pois vc não vai mais poder acessar o sistema a partir do localhost:8080 usual

essa discussão no tectura é bem legal para saber as vantagens e desvantagens dessas abordagens:
http://www.tectura.com.br/topics/abordagens_de_multitenant

--
Caelum
www.caelum.com.br

RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

Por que deve ser @ApplicationScoped?
@ApplicationScoped não quer dizer que será chamado apenas uma vez na aplicação?

Está correto. O Map deve ser de <Tenant, SessionFactory> e preciso implementar o equals e o hashcode no Tenant.
Não cheguei a testar essa parte em execução justamente por causa da dificuldade que é trabalhar com os subdominios (ao menos no inicio).
Essa prática de usar o subdominio é bem comum em aplicações Ruby on Rails. Pelo que eu vi é necessário configurar o servidor para redirecionar todos os requests de subdominio para a aplicação. - preciso ler mais sobre o assunto...

Essa discussão realmente é bem interessante mas não tem o mesmo enfoque. Pois, lá a discussão gira em torno da teoria:
uma instância por cliente x uma instância para todos os clientes. E aqui o enfoque é na implementação. Durante a discussão percebe-se que o maior receio é sobre a segurança dos dados (gerenciamento dos tenants). Inclusive é citado um plugin do Grails que faz esse gerenciamento.

Acredito que é possível obter um resultado 100% confiável com o uso do VRaptor. Continuarei meus testes depois... primeiro preciso aprender a usar o Restfulie rsrs

Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
davisnog
JavaEvangelist

Membro desde: 21/05/2007 21:05:18
Mensagens: 438
Localização: Blumenau/SC
Offline

Acho legal a forma que o Google AppEngine, faz o multi-tenant.

Um objeto Key como chave primaria, onde nele tem o id(sequencial), o namespace(que seria o nome do tenant), e mais algumas informações.

Nessa aplicação, tenho um interceptor onde seto o Namespace atual, nesse caso o id do usuário, dessa forma não preciso adicionar uma condição nos filtros para filtrar somente o daquele usuário, isso fica de forma automática, porque no objeto Key, já tem o namespace.

http://code.google.com/intl/pt-BR/appengine/docs/java/multitenancy/multitenancy.html

Será que não daria para fazer algo com o hibernate?

This message was edited 1 time. Last update was at 24/01/2011 10:57:47


- DAVI NOGUEIRA -
Assim como um pai se compadece de seus filhos, assim o SENHOR se compadece daqueles que o temem. Salmos 103:13

@davisnog
[MSN]
RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

Acredito que sim. Não sei se de forma genérica e automática.. mas tem...

É necessária uma tabela que armazena todas as tenants. E toda tabela deve ter um ligação com essa.

O hibernate permite colocar um filter na session.
http://www.java2s.com/Code/Java/Hibernate/HibernateFilterDemo.htm

Então, é possível utilizar o ComponentFactory do Vraptor para que toda sessão criada automaticamente use esse filtro. Nesse componente é possível ter injetado o Tenant como mencionei acima para pegar da URL o tenant.

Deveria haver duas classes separadas uma que implementa o tenant com banco de dados compartilhado e outra que não. Pois, no caso que usa banco de dados compartilhado (como GAE). Você precisa saber o id desse tenant no banco e não o nome.

Então, deveria ter um map que armazena <Tenant, Long> (tenant, id).

No caso do GAE onde fica esse ID do usuário? na sessão?

Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
Lucas Cavalcanti
GUJ Hacker
[Avatar]

Membro desde: 08/07/2007 00:08:14
Mensagens: 6396
Offline

RafaelViana wrote:Por que deve ser @ApplicationScoped?
@ApplicationScoped não quer dizer que será chamado apenas uma vez na aplicação?


@ApplicationScoped é o mesmo que singleton, ou seja, só vai ter uma instância dessa classe em toda a aplicação,
mas o método getInstance() será chamado toda vez que alguém precisar de uma SessionFactory.
Precisa ser assim senão o mapa será criado toda requisição, então não adianta nada usá-lo.

--
Caelum
www.caelum.com.br

RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

Ok. Obrigado, entendido.

Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
davisnog
JavaEvangelist

Membro desde: 21/05/2007 21:05:18
Mensagens: 438
Localização: Blumenau/SC
Offline

Isso mesmo @RafaelViana, o id vem da Sessão do Usuario.

- DAVI NOGUEIRA -
Assim como um pai se compadece de seus filhos, assim o SENHOR se compadece daqueles que o temem. Salmos 103:13

@davisnog
[MSN]
garcia-jj
JWizard

Membro desde: 13/04/2009 22:11:50
Mensagens: 2715
Localização: Porto Alegre
Offline

Hmm, muito boa a sugestão. Isso poderia talvez virar um cookbook?

Há algum tempo eu fiz um sistema multi-tenant que usava o dominio para verificar qual o cliente, e era algo parecido com a tua implementação. A base de dados era a mesma para cada cliente, mas em catalog diferentes. Assim eu criei um query interceptor no JPA que transformava cada consulta a alguma entidade em tenant.entidade. Algo como



para


http://github.com/garcia-jj
Não respondo dúvidas via MP. Use o fórum.
RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

Isso. A ideia é fornecer um ponto de referencia. Do mesmo modo que há o vraptor-blank-project para iniciar novos projetos. Poderia existir um projeto base com o multi-tenant já implementado.

Estou criando um projeto aqui. Assim que tiver uma base coloco github.

Mudando de assunto...

garcia-jj ou Lucas Cavalcanti vocês que são os 'caras' do Vraptor rsrs achei um 'probleminha' em uma das implementações:

Ao utilizar a abordagem de bancos de dados separados. O CriadorDeSessionFactory precisa saber qual banco de dados criar, porem é @ApplicationScoped. Não tenho como injetar o Tenant que é @RequestScope (ele não pode ser @ApplicationScoped, pois precisa ter injetado o HttpSession se buscar o tenant da sessão ou HttpServletRequest se buscar o tenant da url).

E agora? Há como utilizar uma assinatura diferente para o método getInstance? (unica forma possivel que pensei...)






Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
garcia-jj
JWizard

Membro desde: 13/04/2009 22:11:50
Mensagens: 2715
Localização: Porto Alegre
Offline

Meio off-topic... eu acompanho o Steve Ebersole no github e no blog dele, e tenho notado uma movimentação para suporte nativo no hibernate para multi-tenant. Se não me engano havia uma issue para suporte a partir da familia 4.x. Vai demorar um pouco, mas creio que vai ficar bem legal.

Haverá também um webminar da JBoss dia 16/fev se não me engano. Ele será apresentado pelo Steve, que vai apresentar uns exemplos com multi-tenant usando um banco por usuário, schema/catalog e com particionamento.

Rafael, creio que vocẽ deve é trabalhar com Session, e não com a SessionFactory (do hibernate). A tua Session é request-scoped, mas a SessionFactory (do hibernate) é application. Eu faria uma fabrica para a SessionFactory (do Hibernate) e uma para a Session que seria reques-scope.

http://github.com/garcia-jj
Não respondo dúvidas via MP. Use o fórum.
RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

garcia-jj wrote:Meio off-topic... eu acompanho o Steve Ebersole no github e no blog dele, e tenho notado uma movimentação para suporte nativo no hibernate para multi-tenant. Se não me engano havia uma issue para suporte a partir da familia 4.x. Vai demorar um pouco, mas creio que vai ficar bem legal.

Haverá também um webminar da JBoss dia 16/fev se não me engano. Ele será apresentado pelo Steve, que vai apresentar uns exemplos com multi-tenant usando um banco por usuário, schema/catalog e com particionamento.

Rafael, creio que vocẽ deve é trabalhar com Session, e não com a SessionFactory (do hibernate). A tua Session é request-scoped, mas a SessionFactory (do hibernate) é application. Eu faria uma fabrica para a SessionFactory (do Hibernate) e uma para a Session que seria reques-scope.


Bom saber... esse demorar um pouco seria meio ano? um ano?

A palestra dele dia 16 é 9:00 de San Francisco seria 3h da manhã no Brasil ?

Achei o post que você deve ter visto: http://relation.to/Bloggers/MultitenancyInHibernate
Eu atualmente trabalho com bancos de dados separados e session factories separados, mas já percebi que são objetos caros (como ele comenta no post). Vi uma vez uma implementação do ConnetionProvider mas não achei muito local de pesquisa. Agora vi no blog dele.. vou correr atrás desses termos para ver se consigo implementar. Ficaria legal...

Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
garcia-jj
JWizard

Membro desde: 13/04/2009 22:11:50
Mensagens: 2715
Localização: Porto Alegre
Offline

O e-mail que eu recebi dizia Wednesday, February 16, 2011 | 17:00 UTC, ou seja, 15h aqui no Brasil, tendo em relação o GMT-2 de Porto Alegre.

Você não pode criar dblink entre as bases? No meu atual projeto precisei fazer isso, então foram criados dblink para os bancos de cada tenant (filiais da empresa), e então eu criei um interceptor no JPa que intercepta cada query e altera para algo como meu_tenant.minha_tabela.

http://github.com/garcia-jj
Não respondo dúvidas via MP. Use o fórum.
Lucas Cavalcanti
GUJ Hacker
[Avatar]

Membro desde: 08/07/2007 00:08:14
Mensagens: 6396
Offline

RafaelViana wrote:
Ao utilizar a abordagem de bancos de dados separados. O CriadorDeSessionFactory precisa saber qual banco de dados criar, porem é @ApplicationScoped. Não tenho como injetar o Tenant que é @RequestScope (ele não pode ser @ApplicationScoped, pois precisa ter injetado o HttpSession se buscar o tenant da sessão ou HttpServletRequest se buscar o tenant da url).

E agora? Há como utilizar uma assinatura diferente para o método getInstance? (unica forma possivel que pensei...)


Jeito feio de fazer:

crie um atributo estático na classe CriadorDeSessionFactory que é um Map<Tenant, SessionFactory>, transforme-a em @RequestScoped
e selecione o tenant pelo request.

jeito mais elegante:

deixe o CriadorDeSessionFactory @RequestScoped, e crie um outro component @ApplicationScoped - SessionFactoryRepository por exemplo -
e o use para selecionar a SessionFactory correta

--
Caelum
www.caelum.com.br

RafaelViana
GUJ Master

Membro desde: 23/03/2008 18:56:02
Mensagens: 1257
Localização: Venâncio Aires/RS
Offline

Perfeito! Ficou bem elegante essa implementação.

Agora surgiu outra dúvida. Tentei resolver aqui, mas não consegui. Qualquer controller precisa ter injetado um Dao. Ou TenantHibernateDao (que seria como um dao genérico para eliminar a necessidade de ter um dao para cada entidade) ou injetar o dao da sua entidade.

No entanto, qualquer um desses dao tem como dependencia a Session do Hibernate. Que tem como dependencia a SessionFactory. (a factory do request é criada antes de chamar algum método do resource). Desse modo, não será possível definir a tenant dentro de qualquer Controller que tenha como dependencia um DAO.

A solução seria criar um @Resource sem dependencia do Session responsável apenas por setar a Tenant. Isso deveria ser chamado antes do login (pois para logar necessito saber a tenant correta para procurar as informações no banco de dados).

Alguma solução mais elegante e eficaz?

garcia-jj

Realmente é às 15h diminui 6h em vez somar 6h nos cálculos rsrs Estou confirmado no webminar. O conteudo vai ser interessante.

dblink pelo que vi é do Oracle? Nunca trabalhei com Oracle nem havia ouvido falar sobre dblink. Irei dar uma olhada.

Rafael Rodrigues Viana
Estudando Java e Flex
Blog: http://www.cauirs.com.br/rafael/

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
[Email] [MSN]
 
Índice dos Fóruns » Frameworks e Bibliotecas brasileiros
Ir para:   
Powered by JForum 2.1.8 © JForum Team