Escalando sistemas com soluções NoSQL

Um 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.

10 Comentários

  1. lucas stephanou 07/06/2010 at 16:09 #

    esse cara ma pareceu extremanente perfomatico.

    mas e na questao de confiabilidade?
    entre usar ele ou uma solucao propria com rabbitmq?

  2. douglas.campos 07/06/2010 at 23:14 #

    @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.

  3. lucas stephanou 08/06/2010 at 10:58 #

    @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.

  4. Antonio Kantek 08/06/2010 at 23:33 #

    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.

  5. Marcio Duran 09/06/2010 at 19:32 #

    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-

  6. Otavio Angel Silva 11/06/2010 at 22:51 #

    resque bom valeu

  7. Arvin 16/06/2010 at 11:52 #

    Legal Douglas.

    Qual seria uma boa opção Java?

  8. Symon 16/06/2010 at 14:27 #

    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.

Deixe uma resposta