Entendendo MapReduce

Sabemos que ter um bom modelo de banco de dados relacionais é importante, porém com a necessidade de aplicações mais escaláveis nos dias atuais, talvez você precise “desnormalisar” o seu banco de dados. O que adiantaria uma foreign key se você tem tabelas espalhadas em diversos data sources? Por questões de performance, dados podem ser distribuídos em data centers distintos, então como buscar pelo id se você não sabe onde está esse dado? Por isso é importantíssimo que a aplicação controle essa integridade, com um bom design OO, para não depender de constraints e stored procedures do banco de dados.

MapReduce é um modelo de programação, e framework introduzido pelo Google para suportar computações paralelas em grandes coleções de dados em clusters de computadores. Agora MapReduce é considerado um novo modelo computacional distribuído, inspirado pelas funções map e reduce usadas comumente em programação funcional. MapReduce é um “Data-Oriented” que processa dados em duas frases primárias: Map e Reduce. A filosofia por trás do MapReduce é: Diferentemente de data-stores centrais, como um banco de dados, você não pode assumir que todos os dados residem em um lugar central portanto você não pode executar uma query e esperar obter os resultados em uma operação síncrona. Em vez disso, você precisa executar a query em cada fonte de dados simultaneamente. O processo de mapear a requisição do originador para o data source é chamado de ‘Map’, e o processo de agregação do resultado em um resultado consolidado é chamado de ‘Reduce’.

Hoje existem diversas implementações de MapReduce, como : Hadoop, Disco, Skynet, FileMap e Greenplum. Hadoop é a implementação mais famosa implementada em Java como um projeto open source.

Se você quer contribuir nesse projeto, assista esse screencast.

Anúncios

Estratégias de Transação Baseadas nos Modelos de Transação Java

“É um erro comum confundir modelos de transações com estratégias de transações. Mark Richards discute três modelos de transações suportados pela plataforma Java (Transação Local, Transação Programática e Transação Declarativa) e quatro estratégias de transação (Orquestração Cliente, Camada API, Alta Concorrência, e Alta Velocidade de Processamento) que podem ser baseadas nesses modelos.”

Confiram a notícia aqui.

Criando uma linguagem para JVM (Ioke)

Apresentação de Ola Bini

Apresentação de Ola Bini

Durante a semana do QCon London, o Ola Bini fez uma apresentação aberta na ThoughtWorks e falou sobre sua linguagem de criação, chamada Ioke. Ficamos surpresos com a quantidade de pessoas que se interessou pelo assunto, pois muitos tiveram que assistir de pé. Estive lá e conferi essa excelente palestra.

Ola Bini trabalha na ThoughtWorks e é apaixonado por linguagens de programação e inteligência artificial. Ele faz parte do principal time de desenvolvimento do JRuby e publicou o livro Practical JRuby on Rails Projects.

Ele começou explicando o que é uma JVM, hotspot, gerenciamento de memória e compilador JIT. Depois falou de JRuby, sendo a melhor implementação da lingagem Ruby para muitos propósitos.

Sobre o Ioke:

  • É um experimento. Uma linguagem de programação.
  • Dinâmica e fortemente tipada
  • Orienteção a Objetos baseado em Prototype.
  • Roda na JVM.

Propósito Geral:

Scripting, Web, Rules, DSL, Game engine.

Paradigma

Orientado a Objetos, Lógico, Funcional, Metaprogramação.

Em seguida foi explicado detalhadamente sobre a sistaxe do Ioke, como foi construído o analisador léxico, o parsing da linguagem e a diferença para o JRuby.

Integração com Java

  • Chamada de métodos java. Até mesmo métodos estáticos.
  • Converte argumentos automaticamente para os tipos corretos.
  • Cria objetos Java.
  • Mantém referência a objetos Java.
  • Converte dados de tipo primitivo em Java.
  • Trabalha com arrays em Java.
  • Implementa Java Types

Ou seja, totalmente compatível com Java.

Diferenças entre os Modelos de Objetos:

Java

  • Classes não são objetos, mas você pode chegar a ter uma representação para uma classe.
  • Métodos não são objetos, mas você pode chegar a uma representação para um método.
  • Atributos não são objetos, mas você pode chegar a uma representação para um atributo.
  • Um objeto é criado de uma classe.
  • Um objeto tem os mesmos métodos e atributos definidos na classe.
  • Métodos não podem ser mudados por Objetos
  • Atrubutos não podem ser mudados por objetos

Ruby

  • Tudo é um objeto. Classes e módulos são objetos, mas classes tem um método de alocação mágico
  • Qualquer objeto tem qualquer tipo de variáveis de instância.
  • Qualquer objeto tem qualquer número de métodos.
  • Um objeto pode ter uma classe associada. Essa classe pode ser nomeada ou uma classe singletom anônima, gerada quando o objeto faz mudanças específicas.

Ioke

  • Tudo é um objeto.
  • Qualquer objeto pode imitar zero ou mais outros objetos.
  • Um objeto tem zero ou mais células. Célula é um binding de um nome para m objeto do mesmo tipo.
  • Método é um objeto.
  • Um macro é um objeto.
  • Um pedaço de código é uma coleção de objetos.

Outros detalhes da apresentação:

Limitações da JVM, Representação de código, Representação Interna, Manipulação de erros, concorrência, manipulação de texto.

Os Slides da apresentação estão em:

http://olabini.com/presentations/CreatingALanguageForTheJVM.pdf.

O vídeo da apresentação está em:

http://skillsmatter.com/podcast/java-jee/language-on-jvm

Comendo pelas Beiradas (Rails)

Rails Summit Latin America

Antes de falar sobre o Rails eu quero falar do Java, porém a intenção desse post não é causar nenhuma flame sobre essas duas tecnologias, mas apenas fazer um breve comparativo e expressar minha opinião sobre o assunto.

O Java é uma plataforma muito poderosa não só por causa da linguagem. A linguagem permite Orientação a Objetos, porém ainda são desenvolvidos muitos sistemas completamente estruturados no Java. A Java Virtual Machine (JVM) é umas das melhores coisas da plataforma, pois por causa dela uma aplicação acaba se tornando mais rápida do que se fosse desenvolvida em outras linguagens, como por exemplo a linguagem C. Isso acontece graças ao JIT que, com a ajuda dos Hotspots, tem um algoritmo poderosíssimo que transforma códigos do java em linguagem de máquina em runtime. Isso não acontece no C pois ao ser gerado o executável não dá mais para alterá-lo em tempo de execução.

Depois que o Java virou moda muitas aplicações foram migradas para Web utilizando essa linguagem da Sun. Assim como outras aplicações Web, escritas em PHP, ASP, Perl, também foram migradas. E para todas as novas aplicações, sejam elas grandes ou pequenas, a única coisa que vinha na cabeça das pessoas era fazer tudo em Java.

“Com apenas uma ferramenta para trabalho, o martelo, todos os problemas viram pregos”

Porém em meados de 2000 a 2002 o java não tinha nenhuma padronização de trabalho para a Web. Não havia padronização de framework e nem de estrutura de pastas. Apenas os velhos conhecidos WEB-INF e META-INF e só. Aconteceu que cada projeto era uma aventura difetente, e se mudasse de projeto teria que reaprender tudo de novo. Por causa dessa falta de padronização surgiram framworks como o Struts e o Hibernate. Na época o Struts até que facilitou pois é melhor do que não usar nada. Porém hoje ninguém é louco de fazer algo com Struts pois já existem frameworks MVC muito mais simples e produtivos. Já o Hibernate é um excelente projeto e continua cada vez mais evoluindo.

Hoje existe a comunidade JCP onde muitos profissionais de renome do mundo todo discutem sobre o futuro do Java. Isso é bom por um lado pois eles entram em acordo para definir o que será padronizado, porém muito processo acaba que retardando o crescimento do Java.

Comendo pelas beiradas, surgiu o Ruby on Rails. Um framework totalmente pensado para o desenvolvimento Web, através da linguagem Ruby. O Rails juntou a padronização e produtividade com a simplicidade e expressividade do Ruby. O Ruby é uma linguagem dinamicamente tipada, totalmente Orientada a Objetos e também pode ser usada como lingagem de “scripting” (não gosto muito desse termo). O Rails, ao contrário do Java, vem crescendo com uma velocidade espantosa. Quem quiser ficar por dentro desse crescimento o blog do Carlos Brando é um ótimo caminho.

Hoje em dia muitas Empresas estão adotando Metodologias Ágeis, e se tratando de Time-to-Market, o Rails sai muito na frente do Java. Por isso um time que trabalha com Rails + Git tem muito para ser verdadeiramente ágil. As empresas no Brasil que usam Rails ainda são muito poucas comparado à quantidade de empresas exterior. E por que não usar Rails na sua Empresa contando que o Rails também é Enterprise? Em Abril de 2008 e Junho de 2008, Phusion Passenger e Ruby Enterprise Edition, respectivamente, foram liberados. Phusion Passenger é um servidor de aplicação para Ruby on Rails no Apache e quando combinado ao Ruby Enterprise Edition, permitem que o servidor faça muitas técnicas de otimização e possibilitando a aplicação escalar melhor.

A linguagem Ruby pode ser usada junto ao Java utilizando o JRuby, unindo a produtividade do Ruby com todo o poder da plataforma Java. O Fábio Kung escreveu esse post e vai falar sobre JRuby on Rails no Evento Rails Summit.

E para nossa diversão o pessoal da Rails Envy fez esse screencast aqui:

Publicado em rails. Tags: , , . 2 Comments »

Escalabilidade != Performance

A Escalabilidade é um requisito não funcional de arquitetura que deve ser considerado em todos os projetos porém, dependendo das necessidades do cliente (requisito funcional) e da quantidade de usuários, a escalabilidade pode um peso maior ou menor dentro do projeto.

Muita gente confunde escalabilidade com desempenho (performance) mas são duas coisas muito diferentes, apesar se um influenciar o outro. Performance é a capacidade de atender requisições dos usuários em tempo de processamento e consumo de memória aceitáveis conforme as necessidades do cliente. Já a escalabilidade é a capacidade de manter a disponibilidade e o desempenho a medida que a carga transacional aumenta. Você pode ter um sistema altamente performático em uma máquina mas que é impossível de escalar em uma arquitetura de cluster, por exemplo. Pensar em escalabilidade não é somente pensar em aumentar máquina. É pensar em IO, memória, requisição, banco de dados e assim por diante. Por exemplo, uma aplicação web que guarda muitos dados na sessão pode ser bem performática, mas se essa aplicação passar a ser usada como um web site, com milhares de usuários, a quantidade de informação na sessão (memória do servidor) tornará o sistema lento! Um HttpSession é muito difícil de escalar se for fazer uma estratégia na mão. Nesses casos uma estratégia de cache distribuído, como o memcached, poderia resolver o problema.

Tive uma experiência recente com uma aplicação web que estava apresentando problema de performance, então precisei fazer uma análise para descobrir a causa. Infelizmente essa aplicação também tem problema de manutenabilidade e nessas horas os autores nunca trabalham mais no projeto e não deixaram o endereço da casa deles para quem for dar manutenção! 🙂

Analisando o código, de cara já vi que teria problemas pois o sistema não tem testes unitários. Com certeza haveria a necessidade de fazer refatorações e nem ao menos posso garantir que não vou “quebrar” o software. Os testes de unidade e/ou de integração são tão importantes quanto o projeto em si pois são eles que garantem a qualidade do software. Um programador profissional deve escrever testes, como já disse Phillip Calçado aqui! Como não há testes, a refatoração foi dividida em sprints bem curtos de no máximo uma semana. Já que o projeto está em produção, é interessante construir testes de falha e depois testes de sucesso, pelo menos para as funcionalidades mais importantes. O ideal seria que a equipe tivesse trabalhado com TDD.

Essa aplicação, escrita em Java, apresentava problemas arquiteturais de performance mesmo com uma infra poderosa com vários clusters e load balancing, e consequentemente afetou a escalabilidade pois o aumento transacional causou indisponibilidade do sistema. Portanto, com o aumento de usuários, entrada de novas funcionalidades e crescimento de registros no banco de dados, a aplicação começou a travar e apresentar muitos problema de lentidão.

Antes de começar a fazer qualquer alteração precipitada eu baixei o JProfiler para tirar uma primeira análise. A ferramenta é boa mesmo! Monitorar consumo de memória, tempo de processamento, hotspot, trabalho do garbage collection, consultas demoradas no banco de dados etc. É uma boa prática monitorar frequentemente as aplicações com um profiler para saber como está indo o desenvolvimento, evitando gargalos desnecessários. Essa prática também evitaria otimizações prematuras, como disse Joshua Bloch aqui. Por exemplo, muita gente altera concatenação de String para StringBuffer, mas nem sabe que internamente a concatenação de string seria transformada em StringBuilder pelo java.

Voltando à aplicação, de nada adiantaria sair alterando o código se o problema estiver no banco de dados. Precisei criar índices das tabelas e com a ajuda do JProfiler isso ficou bem fácil. Já na parte da aplicação, a ferramenta também me ajudou a encontrar problemas como:

  • Excesso de instanciação de objetos dentro de loops bastante grandes. Muitos desses objetos são apenas para trafegar dados dentro da aplicação. Eles não tem inteligência nenhuma, são como fantoches. Mas o que o JProfiler aponta é que o garbage collector está precisando fazer Full GC toda hora, e isso gasta tempo!
  • Muito uso de Reflection do java dentro de loops bastante grandes. Esse recurso é muito poderoso, mas deve-se tomar algumas precauções. O desempenho da aplicação é melhor quando a chamada de um método é feito da maneira convencional. Se for para dar produtividade e o overhead não for tão grande, vale a pena. Por outro lado o uso de reflection pode deixar o código difícil de entender. No caso da aplicação em análise uma solução mais Orientada a Objetos eliminaria esses impulsos por querer dificultar problemas que seriam simples. Só para uma curiosidade, o Reflection foi rescrito no java 1.4 para melhorias de performance.
  • Comparação de strings para buscar um objeto em uma lista. Essa busca de objetos pode ser otimizada pela própria linguagem utilizando, por exemplo, o método contains de um collection. Um loop utilizando Java.util.Vector pode ser transformado em um Java.util.HashSet para buscar o objeto mais rapidamente se no caso não importar a ordem dos objetos. Porém se a intenção for fazer cálculos com atributos de uma classe, como ocorre em algums pontos da aplicação, a idéia seria que a própria classe (entidade) faça o cálculo a partir de seus atributos. Isso, além de ser mais Orientado a Objetos, eliminaria a necessidade de fazer loops para buscar o objeto e setar um valor nele.
  • Loops desnecessários. Alguns loops podem ser eliminados apenas com uma solução mais orientada a objetos. Outros podem ser eliminados apenas alterando a consulta no banco de dados para já trazer um resultado calculado, por exemplo.
  • Problemas em aberturas e tempo de vida de transações de banco de dados. Esse com certeza é um dos piores mas não vale a pena eu comentar pois a solução depende de cada aplicação.
Aplicações em Ruby on Rails sofrem com a falta de thread safety, o que pode prejudicar a escalabilidade. Mais o pessoal está escalando uma grande aplicação em Rails fazendo um uso extensivo de cache de fragmentos. Que tal o GUJ rodar em Rails 7 vezes mais rápido do que roda em java? Segue o link do bate-papo.
Conclusão:

Tem muita gente dizendo que Ruby on Rails não escala por causa do case do twitter mas tá aí uma prova que mesmo uma aplicação em Java, com recursos como GC, JIT e hotspot pode não escalar, pois não é a linguagem que não escala, e sim o modelo que não foi pensado para escalar.