Lançamento do django-importer

Read this post in English / Leia este post em Inglês

Eu tenho criado alguns importadores para models do Django e me vi repetindo um padrão, mesmo com origens de dados bem diferentes, pude encontrar um certo nível de abstração.

O resultado foi um novo projeto chamado django-importer, com a intenção de servir como base para qualquer importação no Django.

Está distribuído sob a licença BSD e hospedado no Google Code: http://code.google.com/p/django-importer/.

Estou escrevendo um projeto de exemplo para ajudar no uso. É um projeto novo, mas bastante funcional (pelo menos para mim), e há bastante a ser melhorado.

Colaboradores, comentários e sugestões são bem-vindos, é só entrar em contato aqui pelo blog ou pela página do projeto.

Anúncios

django-importer released!

Read this post in Portuguese / Leia este post em Português

I’ve been writing some importers for Django models and caught myself repeating a pattern, even though the data sources were very different, I could find some room for abstraction.

The result was a new project called django-importer, to be used as a base for any data importer on Django.

It’s released under BSD License and hosted on Google Code: http://code.google.com/p/django-importer/.

I’m writing a sample project to help in the usage. It’s a work in progress but already very useful (at least for me) and there’s plenty room for improvement.

Contributors, comments and suggestions are welcome, just leave a message here in the blog or in the project page.

Startup script (init.d) for nginx

Instalei o Nginx no Suse há algum tempo e fiz isso manualmente, não sei se havia um pacote pronto, ou eu queria instalar a última versão, só sei que instalei na munheca.

Recentemente percebi (do pior jeito) que não tinha um script de inicialização instalado, a máquina foi rebootada e o Nginx não subiu.

Procurei me entender com o funcionamento dos scripts de inicialização, procurei um script pronto para o Nginx e não achei.

Então peguei um script qualquer, seguindo o modelo de parâmetros “start/stop”, adaptei para a minha necessidade e saí com o seguinte:

#! /bin/sh

# Description: Startup script for nginx.
#
# Author:  Enrico B. da Luz 

DESC="nginx"
NGINX=/usr/sbin/nginx
PIDFILE=/usr/logs/nginx.pid
CONF=/usr/conf/nginx.conf

d_start() {
  $NGINX -c $CONF || echo -en "\n$DESC already running"
}

d_stop() {
  kill -QUIT `cat $PIDFILE` || echo -en "\n$DESC not running"
}

d_restart() {
  d_stop
  sleep 2
  d_start
}

d_reload() {
  kill -HUP `cat $PIDFILE` || echo -en "\nCan't reload $DESC"
}

case "$1" in
  start)
    echo -n "Starting $DESC: $NAME"
    d_start
        echo "."
  ;;
  stop)
    echo -n "Stopping $DESC: $NAME"
    d_stop
        echo "."
  ;;
  reload)
    echo -n "Reloading $DESC configuration..."
    d_reload
        echo "."
  ;;
  restart)
    echo -n "Restarting $DESC: $NAME"
    d_restart
    echo "."
  ;;
  *)
    echo "Usage: $0 {start|stop|restart|reload}" >&2
    exit 3
  ;;
esac

exit 0

Como fiz para o Suse, segui os funcionamento dos scripts de inicialização”>padrões de caminhos e coloquei o script no “/etc/rc.d/boot.local”.

Deve funcionar para outras distribuições também, mas aconselho que se utilize um bom gerenciador de pacotes para evitar dores-de-cabeça.

Scripts na inicialização (boot): Linux / FreeBSD / Suse

Parece que cada distribuição Linux / Unix / BSD tem a sua própria maneira de executar scripts na inicialização.

Eu gostaria de ver mais padronização entre os sistemas mas não posso reclamar, é dessa diversidade e experimentação típicas do código aberto que surgem as melhores idéias.

Em geral, um script é executado no fim da inicialização, você coloca os seus próprios comandos lá e pronto. Existe um esquema de runlevels, para determinar a ordem de execução de alguns comandos, mas acho difícil um usuário comum (como eu) precisar disso, então não vou entrar em detalhes, até porque não teria muito o que falar.

Fiz aqui um apanhado dos esquemas de inicialização das poucas distribuições que eu tive contato, assim posso voltar aqui sempre que precisar:

Suse:

/etc/rc.d/boot.local

CentOS:

/etc/rc.d/rc.local

FreeBSD:

/etc/rc.conf
/etc/rc.d/*
/usr/local/etc/rc.d/*

O Suse e o CentOS usam scripts com nomes diferentes, você adiciona os seus comandos e eles serão executados no próximo boot.

No FreeBSD o rc.conf não é um script, mas sim um arquivo de configuração, você habilita os serviços que quer rodar e o sistema executa o script relacionado, que deve estar em “/etc/rc.d/” ou “/usr/local/etc/rc.d/” (ainda não entendi quando tá em um lugar ou outro).

Google procura o Santo Graal da performance no Python

Li no Ars Technica que o Google tem um projeto para melhorar a performance do Python e resolver de vez os famosos problemas de multithreading no Python, entre outras otimizações.

Em poucas palavras, a idéia é trocar a virtual machine do Python por um compilador em tempo real (just-in-time ou JIT), tem uma descrição mais completa no artigo: Google searches for holy grail of Python performance.

Acho muito bacana este tipo de iniciativa, muitas vezes o Python é atacado pela performance, ou não compete com o multithreading de outras linguagens. Eu gosto muito do Python, é produtivo e muito fácil de aprender, gostaria muito de vê-lo com estes problemas corrigidos.

py2exe: múltiplos executáveis compartilhando recursos

Tenho feito algumas experiências com o py2exe e percebi que os meus executáveis estavam ficando muito grandes. Cada vez que se compila um executável, o runtime do python é anexado, juntamente com várias bibliotecas.

Não satisfeito, resolvi buscar uma maneira melhor de empacotar tudo e descobri que é possível compartilhar recursos entre vários executáveis. A dica é compilar tudo de uma só vez, usando apenas um zipfile.

Aqui vai o exemplo resumido de um arquivo de compilação (ex.: setup.py) para converter os scripts para console sample_1.py e sample_2.py em executáveis separados compartilhando recursos:

from distutils.core import setup
import py2exe, sys

options = dict(
    console=[dict(script='sample_1.py'),
             dict(script='sample_2.py')],
    options=dict(
        py2exe=dict(
            compressed = 1,
            optimize = 2,
            bundle_files = 1,
        ),
    ),
    zipfile='shared.zip',
)

setup(name='sample', **options)

E então, para compilar:

python setup.py py2exe

Achei pouca informação sobre isso e resolvi escrever aqui.

Consumo de memória em processos longos no Django

Há pouco tempo criei um script para importação offline de XML no Django.

O tamanho dos arquivos era próximo a 100Mb, pelas minhas contas são mais de 60 mil registros, argh.

Durante os testes, reparei que o processo crescia absurdamente e achei bom dar uma revisada antes de colocar em produção.

Tentei me entender com o módulo gc (Garbage Collector) mas não deu muito certo, encerrou o expediente e fui pra casa sem resolver o problema.

Mas nada como o ócio criativo para nos trazer idéias espontâneas. Pensei:

O que estou fazendo que pode estar estourando a memória?
Para cada item, verifico se existe, se existir atualizo, senão eu crio, são pelo menos duas consultas pra isso, mais as chaves estrangeiras, …..
Ei, isso tudo vai para o log de consultas do Django!! Diacho.

Pronto, achei o culpado! Em modo de debug, o Django mantém um histórico de todas as consultas feitas ao banco, então os 100Mb do XML vão facilmente virando 200Mb, 300Mb, o meu limite de paciência foi quando o processo chegou aos 400Mb.

Estes logs são mantidos no django.db.connection.queries, aqui vai um exemplo para testar:

from django.db import connection
print connection.queries

Para provar o meu ponto, fiz o teste mais tosco, a cada item processado limpava a lista de consultas. O processo crescia, mas muito pouco, nada que fosse crítico.

from django.db import connection
connection.queries = []

Por fim, fiz outro teste, com o debug desligado, e o processo também se manteve enxuto, sem limpar o log de consultas.

Probleminha chato, que demorou pra ser encontrado. Quando eu ia pensar em desligar o debug pra testar alguma coisa? Geralmente é o contrário: teste + debug / produção – debug.

Então fica a dica, quando estiver trabalhando com processos pesados ou de longa duração, é aconselhável desligar o modo de debug, principalmente se é algo que roda offline, agendado no cron ou longe dos “olhos dos usuários”.