domingo, 10 de outubro de 2010

bulkload (Carga de dados) e consumo de CPU no GAE

Buenas!

Este post é só pra mostrar algumas estatísticas do GAE com relação ao processamento. Estou fazendo uns testes de carga pro datastore e já comecei a receber alguns erros... Mas calma! estes erros são plausíveis e explicáveis, não se preocupe... rs

Bom, seguinte... Imagine seu desktop bombando de threads de upload de dados... Não consegue? Seria algo assim:

Figura 1: Upload de dados

Pois é... Você já viu aí uma pancada de erros né? Mais especificamente o erro 503 do protocolo http. Você então, como administrador da aplicação, vai checar o dashboard pra ver que WTF tá acontecendo:

Figura 2: Dashboard da aplicação.


É, cpu bombando né? Neste ponto tenho 3 assuntos a abordar:

  1. Bulkload de dados. Nada mais é que carregar seus dados para sua aplicação no cloud do google. É justamente o que a figura 1 demonstra.
  2. Gerenciamento da aplicação. Reparem nos ícones de warning na figura 2. Porque eles estão ali?
  3. Consumo de CPU. Porque a minha aplicação, que está em pleno desenvolvimento, consumiu esta quantidade toda de CPU em menos de 2 horas?
Nos próximos posts pretendo explicar cada um dos itens acima. Stay tuned! :)

Até!

terça-feira, 5 de outubro de 2010

Utilizando o operador like em consultas ao datastore do GAE

É uma tendência natural e inevitável quando migramos de tecnologia fazer comparações entre elas. Recentemente comecei um projeto utilizando a estrutura Cloud do Google e, consequentemente, utilizando um banco NoSQL, o BigTable.

Em projetos normais costumamos utilizar um banco de dados relacional, onde estamos acostumados a utilizar comandos SQL para efetuar consultas no nosso banco. Porém, quando migramos para uma plataforma cloud e bancos NoSQL, não temos mais uma base relacional e consequentemente nossa estrutura de armazenamento muda um pouco (ou muito!). Não vou me aprofundar neste assunto por haver muitas discussões interessantes pela internet.

Em bancos NoSQL temos sérias restrições a consultas que estamos acostumados a fazer em bancos relacionais. Agrupamentos como counts e sums, operadores like e alguns joins são bem restritos. Neste post irei focar no uso do operador like para consultas a campos String.

Bem, chega de papo e vamos ao que interessa. Nestes exemplos, farei analogias entre os bancos relacionais para facilitar o entendimento. Utilizarei também como framework de persistência da minha aplicação o Objectify (existem outros frameworks focados em bancos NoSQL, como o twig e o SimpleDS, mas ainda não os testei). Antes que perguntem, não, não é possível utilizar Hibernate. Além da diferença conceitual pelo fato do Hibernate ser um framework ORM, o próprio google disponibizou a sua lista de compatibilidade. Vale ressaltar que o GAE suporta JPA, portanto é possível fazer "algumas coisas" bem parecidas de como estamos acostumados. Entretanto já ouvi, li e concordo que o conceito destes bancos é diferente, portanto é um "workaround" utilizar JPA ou JDO com estes bancos, visto que algumas funcionalidades não são suportadas.

Na prática, vamos supor que você tem um requisito no qual seu cliente quer consultar uma cidade pelo nome. Precisamos então da nossa entidade Cidade:











Instantâneamente na sua cabeça lógica de desenvolvedor aparece a sentença:

"select nome, uf from Cidade where nome like 'paulo%'"

Certo? Num banco relacional sim. Porém, o foco aqui é o nosso BigTable. Análogamente utilizando o Objectify faríamos:









Ótimo. Porém o requisito diz que devemos buscar as cidades não importando a ordem das palavras. Ou seja, se o usuário digitar "paulo", a lista deve conter Paulo Jacinto, Paulo Afonso, São Paulo, Monsenhor Paulo, etc. Fácil. É só colocar mais um %, não é? Ficaria:

"select nome, uf from Cidade where nome like '%paulo%'"

#ihcomplicou. Uma das restrições no uso de bancos NoSQL que citei no início é justamente esta. A solução? Quem sabe indexar os termos de busca da sua entidade? Parece uma boa...

Precisamos então adicionar um novo atributo na nossa entidade Cidade. Criei então um List do tipo string chamado indexedName, como segue:



Agora, precisamos preencher esta lista de termos com os termos relevantes à pesquisa. No meu caso, criei um "IndexBuilder" responsável por elencar os termos pesquisáveis. Esta classe tem a função de "limpar" a string que estou passando à ela, removendo acentos, pontos, vírgulas e passando tudo pra lowercase (selects com lowercase e uppercase também não são possíveis no BigTable). Ele me retorna um List contendo cada palavra quebrada pelo " " (espaço, via split) entre uma palavra e outra do termo. Caso necessário, é perfeitamente possível componentizar este "builder" para tipos de indexadores específicos para o seu domínio. Basta interfacear este cara e invocá-lo por um container IoC ou uma factory qualquer. Use a imaginação!

Ótimo. Já temos o atributo de indexação de pesquisa e também o componente responsável por lidar com estes termos. Agora precisamos de algo que ligue os pontos. Para isto, o Objectify provê anotações para métodos das nossas entidades em 2 momentos. @PostLoad (logo após recuperar o objeto do datastore) e @PrePersist (antes de gravar no datastore). Utilizei a anotação @PrePersist no método onSave que irá chamar o nosso componente indexador e preencher o atributo indexedName da nossa entidade. Falei também para o Objectify não indexar os campos nome e uf (uma vez que não vou pesquisar por eles), e explicitamente disse que o indexedName é indexado (por padrão, todos os campos são indexados):



Feito isto, basta agora buscar a cidade pelo nosso atributo indexedName, ao invés do atributo nome. Algo como:



Desta forma o usuário terá a experiência de estar utilizando uma busca "full text", ou seja, o termo digitado será utilizado independente de maiúsculas, minúsculas, acento e formatação (de acordo com o seu componente de indexação). Caso o usuário busque por "paulo", o sistema retornará São Paulo, São Paulo da Fartura, Pedro Paulo Diniz, Padaria do Paulo, Seu Paulo da Esquina, etc...


O resultado é algo como: 




Até! :)


referências: http://groups.google.com/group/objectify-appengine/browse_thread/thread/be46b724c5176f61