quarta-feira, 6 de fevereiro de 2013

Lumosity - You have the power to improve your brain

Hoje vou falar do site lumosity.com

O Lumosity é um site com vários jogos feitos para treinar o cérebro.

Segundo eles, os jogos treinam 5 diferentes áreas:

  • velocidade
  • memória
  • atenção
  • flexibilidade
  • resolução de problemas

Ao criar uma conta, você escolhe quais áreas deseja melhorar e quais tem preferência no treinamento.

Todos os dias, são 5 jogos selecionados de acordo com seu perfil. Eles dizem que treinar o cérebro deve ser um costume como escovar os dentes, deve ser feito todos os dias e assim terá resultados no futuro.

Uma das partes mais legais é acompanhar o gráfico de evolução das suas habilidades e como você se compara com as outras pessoas da sua mesma faixa etária. A seguir coloquei um print dessa tela:


 Para experimentar é possível criar uma conta trial e jogar por 3 dias.

Eu não consegui resistir e estou pagando um plano mensal para poder continuar, até minha esposa está treinando e gostando!

A dica é fazer um plano familiar, fica mais barato por pessoa, inclusive tenho vagas no meu plano, se alguem tiver interesse me avisem!

sexta-feira, 18 de janeiro de 2013

GEM para consumir API do Emailmarketing da Locaweb

Pessoal,

fiz uma GEM para consumir a API do Emailmarketing da Locaweb.

O Github é esse: https://github.com/fabioperrella/locaweb-emailmarketing
Está publicado no rubygems: https://rubygems.org/gems/locaweb-emailmarketing

Sugestões serão bem vindas!

sexta-feira, 13 de abril de 2012

Importação de dados em massa no MongoDB com Mongoimport

No mysql quando precisamos inserir grande quantidade de registros podemos utilizar o comando LOAD DATA, no MongoDB temos o comando mongoimport, vou falar de algumas coisas que percebemos ao utilizá-lo
obs: Nossa implementação foi feita em Rails + Mongoid

Utilização básica


Após alguns testes, decidimos utilizar a importação com algumas opções, como abaixo:

mongoimport -d [DB_NAME] --upsert --stopOnError -c [COLLECTION] [IMPORT_FILE]
onde:
  • DB_NAME: nome do database
  • --upsert: atualiza documentos que já existirem (abaixo falarei mais sobre isso);
  • --stopOnError: interrompe a importação caso ocorra algum erro em alguma linha do arquivo de importação;
  • COLLECTION: nome da collection;
  • IMPORT_FILE: arquivo com os os dados a serem importados (falarei mais sobre isso)

Opção --upsert


Quando utilizada a opção --upsert, ao importar uma linha, o mongo irá procurar se existe algum documento com esse ID no banco e se existir sobrescreverá esse pelo que está no arquivo de importação, mas atenção, ele não faz e não tem como fazer um MERGE, que é uma feature request no mongodb, ele irá sobrescrever todos atributos desse documento!

Se não utilizada a opção --upsert, as linhas que já existirem no mongo serão ignoradas pela importação

Arquivo de importação


Para rodar o mongoimport, é preciso gerar um arquivo de importação seguindo o exemplo na documentação do mongoimport

É preciso tomar cuidado ao gerar esse arquivo, por exemplo alguns campos como created_at e updated_at que são criados automaticamente ao inserir um novo documento, não são criados se não forem colocados nesse arquivo de importação. Também precisamos tomar cuidado com os campos de relacionamento, mesmo que esse documento não esteja relacionado, é preciso colocar no json o campo com o valor vazio.

Na nossa implementação para geração desse arquivo, fizemos um metodo to_mongo_json no modelo a ser importado, para que gerasse o json esperado pelo mongoimport de cada documento.

Para isso utilizamos o método attributes para pegar todos atributos do documento e gerar o json, além disso precisamos converter alguns valores (data, object_ids, etc..) de acordo com essa página

Velocidade de importação


Ao rodar o comando mongoimport, é impresso a taxa de importação. Nos testes que fizemos chegamos a taxas de até 9mil documentos por segundo, muito rapido! Se fizéssemos o create de cada documento individualmente no Rails, demoraria muito mais.

Atualização de documentos existentes


Como no mongoimport não existe a opção de merge dos dados, como mencionado acima, a solução seria gerar o json desse documento com todos os dados existentes e rodar o mongoimport com a opção --upsert. Assim poderíamos ter problemas, pois se enquanto a importação estivesse sendo processada, se um documento fosse atualizado pela aplicação, e ele já tivesse seu json gravado no arquivo de importação, ao rodar o mongoimport, os dados atualizados nesse meio tempo seriam perdidos.

O ideal nesse caso seria atualizar somente os novos dados com uma opção de merge que não existe, como falamos acima. Nossa solução foi separar os documentos que já existem no mongo dos que são novos. Para os novos utilizamos o mongoimport normalmente, já para os existentes, não colocamos no arquivo de importação e simplesmente rodamos a query de update direto no mongo, como o exemplo abaixo:

MODEL.collection.update({_id: MODEL.id}, { "$set" => { lala: "popo" }, "$addToSet" => { list_ids: {"$each" => lists.map(&:id)} } })

O método collection.update é o que o mongoid utiliza internamente para executar as queries no mongodb e pelos testes que fizemos é muito mais rápido do que se utilizássemos o método save do objeto.

* Créditos também ao @marciotrindade e Claudio Bruno Martins

quinta-feira, 5 de abril de 2012

Campos customizáveis com Rails, MongoDB e Mongoid

Em algumas situações queremos ter um modelo onde alguns campos podem ser customizáveis pelo cliente, vou dar um exemplo de como implementar isso com Rails, MongoDB e Mongoid

Onde guardar os campos customizáveis

A primeira idéia seria se aproveitar do fato do Mongo não ter uma estrutura definida para as collections (tabelas) e criar os campos customizáveis dinamicamente no modelo. No mongoid existe a configuração allow_dynamic_fields que permite que sejam inseridos campos dinamicos no modelo. Mas nessa solução temos alguns problemas, entre eles:
  1. perdemos o controle dos campos customizados que foram criados, pois teriamos que varrer cada documento e ver quais campos existem
  2. nao temos o controle do tipo de campo customizado que foi criado, por exemplo não saberíamos se ele deve se comportar como uma string, data ou inteiro
  3. temos que nos preocupar em proteger os campos default, por exemplo state, id, created_at, etc.. e não permitir que o usuário consiga apagar ou editar esses campos
  4. teríamos que implementar getters e setters no modelo para esses campos, pois não conseguiríamos definir os fields que é o jeito padrão do mongoid
A sugestão é criar um campo no modelo chamado custom_fields (ou algo parecido) e externamente um cadastro de campos customizáveis, desse jeito temos algumas vantagens:
  1. temos a lista dos campos customizáveis que existem e seus tipos (data, string, inteiro, etc..)
  2. só precisamos liberar o campo custom_fields para alteração e os campos default podem ficar protegidos no modelo
  3. podemos fazer buscas tipadas, por exemplo se um campo for do tipo data, buscar por um range de datas
Utilizando mongoid, a implementação ficaria desse jeito:

field :custom_fields, type: Hash   , default: {}

Desse jeito, no mongodb, os campos customizáveis ficam armazenados do mesmo jeito que se fossem embed documents

Criando índice de campos customizáveis

Porém quando implementamos essa solução, tivemos o receio de que não conseguiriamos criar um índice no mongo para otimizar as buscas por esses campos customizáveis, aí veio a grande surpresa do Mongo!

O Mongo permite que sejam criados índices desses campos customizáveis mesmo que alguns documentos dessa collection não tenham esse campo, e melhor ainda, mesmo que nenhum documento tenha esse campo customizável. Desse jeito podemos por exemplo, criar um índice em "custom_fields.empresa" mesmo que esse campo ainda não exista, mas quando ele for criado já terá um índice!

No exemplo abaixo temos uma busca feita no mongo sem o índice e logo após a criação do índice e a nova busca. Podemos ver que na primeira, foram percorridos os 1002 documentos e na 2a, somente os 482 do sexo "fem"



*Créditos também ao @marciotrindade e Claudio Bruno Martins por esse conteúdo


segunda-feira, 19 de março de 2012

Init script para Mongos no Debian

Procurei em vários lugares um init script para subir o serviço MONGOS do MONGODB e não achei, resolvi fazer um baseado no init script do mongodb:

segunda-feira, 19 de dezembro de 2011

Protegendo linhas do crontab com FLOCK

Em algumas situações queremos colocar uma linha no crontab que rode de tempos em tempos, mas queremos garantir de algum modo, que esta não ira rodar em paralelo caso a execução anterior não tenha sido finalizada, por exemplo:

*/1 * * * * root /sbin/exemplo/processa_relatorios.asp -t diario

Se as 18:23 o comando processa_relatorios.asp começar e as 18:24 não tiver terminado, teremos 2 execuções em paralelo deste, que pode gerar resultados monstruosos.

Várias pessoas (inclusive eu) acabam implementando algum mecanismo de lock na aplicação (ou no comando) para que ele consiga saber que já existe outra instância rodando e que não poderá rodar novamente caso o crontab dispare, mas descobri que existe o comando FLOCK, disponivel no Debian e em algumas outras versões de linux, que pode resolver isso com mais facilidade!

A linha de cron para esse exemplo ficaria assim:

*/1 * * * * root flock -w 0 /tmp/lock_relatorios  -c "/sbin/exemplo/processa_relatorios.asp -t diario"

onde:
"-w 0" indica que deve esperar 0 segundos caso o comando esteja em execução (isto é, vai desistir de rodar o comando pois não há tempo de espera)
"-c" é o comando a ser executado
"/tmp/lock_relatorios" é o arquivo que será lockado para a execução desse comando

Outro exemplo:

- arquivo /etc/cron.d/teste

*/1 * * * * root flock -w 0 /tmp/test -c "date && sleep 70" >> /tmp/log.txt

- a saida do log será:


Mon Dec 19 09:36:01 BRST 2011
Mon Dec 19 09:38:01 BRST 2011
Mon Dec 19 09:40:01 BRST 2011

Nesse caso podemos ver que não rodou a cada minuto pois havia um "sleep 70" que estava segurando a execução do comando.

quarta-feira, 14 de setembro de 2011

Git push para remote diferente do origin - Everything up-to-date

Em vários projetos, precisamos trabalhar em um fork do repositório oficial para futuramente fazermos um merge request (ou pull request) nele.

O que costumo fazer para isso é clonar o repositorio principal e criar um branch local (work, por exemplo), como abaixo:

git clone https://github.com/tinymce/tinymce.git
git remote add perrella git@github.com:fabioperrella/tinymce.git
git fetch
git checkout -b work


Com isso faço meus commits no branch work e depois envio os commits para o branch remoto perrella/master assim:


git push perrella master


Mas ocorre esta mensagem: Everything up-to-date
Você pode ter certeza que nada foi enviado olhando nos commits deste branch no github ou vendo no gitk --all ou no gitx por exemplo.


A solução para isso é simples (apesar de que não é simples entender na documentação do git push):


git push perrella work:master


Onde work é o nome do branch local que você quer enviar para o branch remoto master.


Agradecimentos ao @marciotrindade pela ajuda com isso!


Se alguém tiver um jeito melhor de fazer isso, comente ai!

vlw!