Escalando sistemas com soluções NoSQL
Por douglas.campos em 07/06/10Um dos grandes desafios enfrentados no dia a dia do desenvolvedor eficaz é o de cumprir requisitos não-funcionais de uma aplicação, principalmente os relacionados a performance e escalabilidade. Uma das alternativas mais conhecidas para escalar horizontalmente é a de dividir as tarefas que não necessitam de retorno imediato ao cliente em processos batch. Para tanto, podemos usar diversas ferramentas, desde soluções caseiras até diversos frameworks, tanto em ruby como em java.
Quando se trata de processos batch, é comum buscar um controle mais fino sobre o resultado da execução (sucesso, falha, pendente, etc). Normalmente, usaríamos bancos relacionais para isso, tanto pela sua popularidade como pela performance. Supondo que eu precise gerenciar 1000 pequenas tarefas simultâneas, pagaríamos um preço bem alto pela concorrência, visto que o banco começaria a demorar para obter o lock, fazer o insert, refazer o lock e atualizar o status da tarefa. Muitos consideram o uso de banco de dados relacionais um erro para casos como esse. Buscando eficiência, poderíamos usar uma solução NoSQL para persistência e controle das tarefas, onde evitaríamos os locks, graças ao modelo de concorrência simplificado.

Uma opção em ruby é o resque, um framework de processamento batch criado pelo github que utiliza o redis, um banco de dados não-relacional, tanto para persistir as tarefas, como para coordenar os diversos processos executores de tarefas, mais conhecidos como workers.
Agendar um processo é bem simples – basta que eu tenha uma classe ruby com um método perform, com o processamento a ser executado e um atributo de instância @queue define em qual fila a tarefa será colocada:
# uma classe qualquer, poderia ser um model
class Relatorio
@queue = :relatorio_anual
def self.perform(ano)
gera_relatorio_anual(ano)
end
end
e finalmente, faço o agendamento:
#método recebe a classe da tarefa, e os argumentos Resque.enqueue(Relatorio, 2009)
Essa chamada faz uso bem eficiente de metaprogramação, de maneira que um worker posteriormente executará um código semelhante a esse:
classe, argumentos = Resque.reserve(:relatorio_anual) classe.perform(*argumentos) if class.respond_to? :perform
posteriormente iniciamos um worker usando as tasks do rake que vem no próprio resque, indicando qual fila o worker que estamos criando vai atender
$ QUEUE=relatorio_anual rake resque:work
assim que o worker subir ele já irá executar Resque.reserve, que o colocará em espera por novos trabalhos. opcionalmente, poderíamos executar um worker para atender todas as filas:
$ QUEUE=* rake resque:work
O principal fator que contribui para que resque e redis funcionem como um relógio é o fato de ambos estarem focados em alta performance e, ao mesmo tempo, extrema simplicidade sem ser muito invasivo, já que permitem que você mantenha sua estrutura de persistência relacional inalterada.
Cada vez mais sistemas vem utilizando solucões NoSQL para algumas funcionalidades específicas, como foi o caso recente do Large Hadron Collider com o Mongo DB. Parece que a combinação de bancos relacionais e não-relacionais no mesmo sistema, cada um com seu propósito, tem sido a direção tomada pelo mercado.
esse cara ma pareceu extremanente perfomatico.
mas e na questao de confiabilidade?
entre usar ele ou uma solucao propria com rabbitmq?
Comment by lucas stephanou — June 7, 2010 @ 4:09 pm
@lucas
Realmente, o resque é muito rápido, fiz alguns “benchmarks caseiros”, onde ele se comportou muito bem. Quanto à confiabilidade e a viabilidade de usá-lo, como quase toda solução arquitetural, depende da avaliação de vários fatores do seu sistema, equipe e processos da empresa, e de uma boa bateria de testes durante uma prova de conceito.
Comment by douglas.campos — June 7, 2010 @ 11:14 pm
@douglas.campos sem duvida, neste momento é o que tenho feitos. Testes com infinitas soluçoes.
Como tu bem pontuou, é arquitetura, e como tal tem muitas soluçoes, e é sempre bom ler sobre a experiencia alheia.
Comment by lucas stephanou — June 8, 2010 @ 10:58 am
Na minha opiniao, o JavaSpace pode ser considerado um banco de dados tipo NoSQL. Acho que o model mestre/escravo (ahahah gostaram dessa) se encaixa perfeito nesse exemplo.
Comment by Antonio Kantek — June 8, 2010 @ 11:33 pm
O assunto NoSQL é interessante, pode também ser consultado no IBM DeveloperWork.
Fonte abaixo:
http://www.ibm.com/developerworks/br/java/library/j-javadev2-8/index.html?ca=drs-
Comment by Marcio Duran — June 9, 2010 @ 7:32 pm
resque bom valeu
Comment by Otavio Angel Silva — June 11, 2010 @ 10:51 pm
Legal Douglas.
Qual seria uma boa opção Java?
Comment by Arvin — June 16, 2010 @ 11:52 am
Tem algo parecido com isso em Java?
Eu já havia imaginado algo parecido com isso: criei uma classe chamada TaskXYZ que herdava de Task e tinha um método execute() – tipo um command.
Dai você pode mandar isso pra uma fila, persistir, recuperar em caso de desligamento do equipamento, e processar a fila denovo. Lindo isso.
Comment by Symon — June 16, 2010 @ 2:27 pm
[...] 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 [...]
Pingback by Um produto para muitos clientes: implementando multitenancy | blog.caelum.com.br — August 23, 2010 @ 4:19 pm