A demonstração de MagLev na RailsConf 2008 demonstrou a tecnologia de VM distribuída pela Gemstone, que permite compartilhar o mesmo objeto em memória transparentemente entre múltiplas VMs Gemstone.
Terracotta é uma tecnologia Java que permite algo similar. Fabio Kung começou agora a experimentar fazer o JRuby tirar vantagem do Terracotta. Projetos similares foram tentados no passado. A Gemstone também experimentou suportar JRuby sobre o produto baseado em Java deles e teve tentativas de JRuby com Terracotta, embora não tenha sido atualizado em algum tempo.
Também conversamos com Fabio Kung sobre seu projeto, que ele se refere como "JMaglev", sobre o que é necessário para fazer o JRuby e o Terracotta trabalhar juntos e quais problemas precisam ser resolvidos antes disso funcionar.
Primeiro, Fabio explica sua implementação e como ele modificou o JRuby para fazer isso funcionar:
Estou usando a clusterização de POJO do Terracotta para tornar o interior do JRuby compartilhado entre todos os nós do cluster. De fato, cada runtime tem uma coleção de variáveis globais, algo como:
public class Ruby { // ... private GlobalVariables globalVariables = new GlobalVariables(); }e:public class GlobalVariables { // ... private Listvalues = new ArrayList ();
}
O Terracotta apenas clusteriza essa lista global de variáveis. Qualquer mudança a esta lista é replicada a todos os runtimes JRuby no cluster. A beleza é que você pode adicionar _qualquer_ objeto ruby nessa lista, mesmo objetos complicados como regexps, hashes e procs. Todas as variáveis globais são automaticamente compartilhadas e qualquer objeto referenciado por uma variável global é instrumentado pelo Terracotta para ser clusterizado.
Para suportar isso, eu tive que fazer um patch no JRuby, tornando-o "clusterizável". De fato, cada objeto ruby no JRuby mantém uma referência permanente ao runtime ruby. Objetos compartilhados precisam ser usados em muitos runtimes diferentes, então JRuby precisa suportar algum tipo de operação em runtime de anexar/desanexar, aqui vai algum:
- identidades globais de objeto: o object_id deve ser o mesmo em todos os nós? - metaclasses compartilhadas: o que acontece quando a classe de objeto, super-classes ou módulos inclusos são modificados em diferentes nós? - suporte a múltiplos runtimes em uma JVM
Eu fiz soluções simples para cada um desses problemas, mas cada solução precisaria de um post de blog inteiro :-)
Fabio explica os casos de uso que ele tem em mente para o JRuby e Terracotta:
Em conjunto com o modo de Alta Disponibilidade do Terracotta, eu acho que o "JMaglev" (talvez eu precise de um nome melhor) pode de fato ser uma alternativa confiável ao memcached sem ser intrusivo no código Ruby. Mas existe muito mais trabalho a ser feito. Essa é a razão que eu tornei público; quem quiser pode contribuir.
http://github.com/fabiokung/clustered-jruby/
Muitos servidores podem ser configurados no Terracotta, com um sendo o "master" (ou servidor ativo) e os outros em modo hot-standby. Aqui vai onde as coisas começam a ficar interessantes, porque se o servidor ativo cai, outros automaticamente tomam seu lugar para aumentar a disponibilidade. Existe até um modo disponível na versão Enterprise do Terracotta que permite múltiplos servidores ativos, similar ao que se consegue com memcached, mas o memcached não torna os objetos persistentes.
Terracotta pode agir como um cache distribuído e não usa serialização do Java: ele apenas replica o que muda. Você apenas precisa tornar os objetos que você pega do banco de dados compartilhados entre todos os nós do cluster. Com o JMaglev, isso significa que você apenas precisa colocá-los em variáveis globais - $shared = Person.find(:all).
Outros casos de uso possíveis é compartilhar o HttpSession entre muitos processos e máquinas em aplicações Rails. As pessoas que instalam aplicações Rails com JRuby poderiam usar objetos clusterizados transparentemente para manter HttpSession compartilhado em todos os nós do cluster.
De fato, qualquer caso de uso do Terracotta é um caso de uso do JMaglev. Honestamente, eu fiz isso apenas porque podia ser feito. Muito similar ao caso do Maglev do Avi Bryant: ele disse que seria possível usar VMs SmallTalk para rodar código Ruby, então os caras da Gemstone o chamaram para provar que de fato poderia ser feito ;-)
Estou esperando pessoas mais criativas do que eu chegar com mais casos de uso criativos para o "JMaglev".
Memória de Objetos Distribuídos é apenas uma das funcionalidades do Gemstone/S (e MagLev); outra funcionalidade importante é persistência. Como o Monty Williams, da Gemstone, explicou em um recente episódio do Rails Podcast, o Gemstone/S suporta persistir os objetos em memória, o que significa que não é necessário um ORM ou mesmo um banco de dados RDBMS.
Quando perguntamos sobre se "JMaglev" poderia suportar algo similar, Fabio explica:
Todos os objetos compartilhados ruby vivem no Servidor Terracotta, que pode automaticamente persistir esses objetos, mesmo quando eles não são serializáveis. Os clientes seguram stubs aos objetos reais compartilhados. Você apenas precisa configurar o servidor para estar em modo persistente. Eu não testei isso ainda, mas deve ser uma linha de configuração XML.
Eu acho que o Terracotta poderia ser usado como um OODB para persistir objetos JRuby, embora eu não ache que esse seja um dos objetivos. Terracotta já está persistindo objetos compartilhados através de seu modo de Alta Disponibilidade que existe para instalações altamente disponíveis e resistentes a falhas.
http://www.terracotta.org/web/display/docs/Configuring+Terracotta+For+High+Availability.
O website do Terracotta lista muitos Módulos de Integração Terracotta (TIM), alguns deles populares com soluções ORM. Quando perguntamos se isso poderia ajudar na persistência, Fabio explica que essas TIMs servem um propósito diferente:
Esses Módulos de Integração Terracotta (TIM) não estão relacionados com persistência automática para objetos compartilhados. Eles estão apenas ajudando o Terracotta para ser usado em conjunto com esses frameworks de ORM. O TIM de Hibernate, por exemplo, não tem nada a ver com persistência. Ele apena habilita o Hibernate para usar um EhCache (e outros) clusterizado (distribuído) com pouco esforço, sem precisar usar caches verdadeiramente distribuídos como o JBoss TreeCache e memcached.
Fabio fez um screencast mostrando como o compartilhamento com JRuby e Terracotta funciona. Para tentar, veja o repositório de clustered-jruby do Fabio no Github que lhe dá tudo que é necessário.