nov 2007 11

Ontem reencontrei os amigos Sandro e Mariana que passam o fim de semana em Brasília. Combinamos uma sessão de “filme horrível” na casa do nosso querido Mateus Tormin e, acreditem, o filme que Mateus escolheu era deveras horrível. Bem, acontece que Sandro e eu fundamos o Perl Mongers Brasilia em 1999, que era na verdade uma desculpa pra tomar cerveja e falar de Perl, e ontem o Sandro chamou minha atencão porque há tempos neste blog, segundo ele, “você só fala em Hugo Chavez e CPMF e onde diabos está o Perl!!??”. Claro que ele está com a razão e hoje decidí tentar recomecar a secão Perl aqui do blog que tem apenas uns 3 posts….

O assunto deste post é o operador de teste de arquivo -M

Ao trabalhar com o operador -M $ARQUIVO para obter o número de dias desde que $ARQUIVO foi alterado, deve-se observar que a forma como Perl trabalha com esse operador não é exatamente intuitiva.

Quando você testa um arquivo com -M, espera obter o tempo desde que foi alterado até o instante em que o operador -M é utilizado. Mas não é isso que acontece.

Perl irá lhe retornar o tempo de modificacão do arquivo até o momento em que o programa foi executado.

Se o seu programa utiliza o operador -M e é um servidor de rede, ou daemon local, ou qualquer processo que permaneca em funcionamento durante longos períodos, o resultado pode ser um desastre.

Veja o seguinte exemplo:


#!/usr/bin/perl

$data = -M "/etc/passwd";
print "$data\n";
sleep(30);

$data = -M "/etc/passwd";
print "$data\n";

Normalmente esperaríamos que a segunda chamada a -M, após 30 segundos, retornasse uma fracão do dia 30 segundos maior(lembre-se, -M não retorna segundos como time(), e sim o número de dias e a fracão do dia atual desde que o arquivo testado foi alterado).

Rodando o programa acima vemos que os 30 segundos não influenciam em nada o resultado de -M


root@clapton:~/perl# perl modificado.pl
514.301099537037
514.301099537037

Vejamos alguns casos em que isso pode causar problemas:

1) Voce utiliza -M para decidir se um arquivo de log deve ser arquivado e um novo criado(estilo logrotate). Neste caso o arquivo irá crescer infinitamente pois seu programa não verá qualquer diferenca na data de modificacão à partir do momento em que o programa é rodado.

2) Você utiliza -M para verificar se arquivos de sistema sofreram vandalismo, algo estilo tripwire. Também não vai funcionar, pois se o arquivo de sistema sendo monitorado for modificado após o nosso programa monitor ser rodado o tempo entre o retornado por -M e a data de alteracão será negativo! O que não faria sentido. Seria um bug difícil de encontrar e remover.

3) Você utiliza -M para decidir se deve, ou não, efetuar backup de um ou mais arquivos. Neste caso pode ocorrer o efeito da data negativa do exemplo 2 e seus backups de dados valiosos podem não ocorrer conforme planejado.

Então, o que fazer?

Claro que em programas quebra-galho, que rodam em minutos ou segundos, a não ser que se estejam organizando longas listas de arquivos por data de modificacão, provavelmente não será preciso se preocupar com isso.

Já em programas de longa duracão será preciso ter cuidado. Como todos os problemas em Perl, há sempre mais de uma solucão.

As duas solucões que conheco utilizando -M envolvem refrescar a data inicial à partir da qual -M efetua sua comparacão. E qual é essa data? É aquela gravada na variável especial $^T.

A terceira solucão que encontrei é deixar de usar -M em favor de stat().

Confira o seguinte programa:


#!/usr/bin/perl

print "$^T\n";
sleep(30);
print "$^T\n";

Rodando, temos o seguinte resultado:


root@clapton:~/perl# perl modificado2.pl
1194827682
1194827682

Aí está o nosso culpado. -M utiliza $^T para determinar a data de modificacão dos arquivos mas $^T não muda durante o runtime de nosso programa. Assim, tenho em mente as seguintes solucões, crie a sua ou escolha a mais apropriada.

Solucão 1 – refrescar $^T toda vez que usar -M.
Exemplo:

#!/usr/bin/perl

$data = -M "/etc/passwd";
print "$data\n";

#..... PROCESSO DE LONGA DURACÃO .....

# mais tarde...
$^T = time();
$data = -M "/etc/passwd";
print "$data\n";

Solucão 2 – criar um timer com signal handler que refresca $^T para voce periodicamente. Assim há menos risco de você esquecer de refrescar $^T antes de uma chamada.

Exemplo:

#!/usr/bin/perl

# arma o relogio do SIGALARM
$SIG{ALRM} = sub { $^T = time(); alarm 5; };

# dispara a 1a vez, daqui a 5 segundos
alarm 5;

print "$^T\n";

#..... PROCESSO DE LONGA DURACÃO .....

# $^T deve estar atualizado, com um erro máximo de 5 segundos
# ajuste o valor do tempo do alarm de acordo com a necessidade
print "$^T\n";

Rodando o código acima, substituindo #….. PROCESSO DE LONGA DURACÃO ….. por um sleep(30), obtemos:


root@clapton:~/perl# perl modificado_alarm.pl
1194828811
1194828816

Percebeu que o tempo inicial não é o tempo final menos 30 segundos? Lembre-se que o sleep() acima é apenas para ilustrar um processo demorado. O sinal ALRM interrompe o sleep() aos 5 segundos(tempo que escolhemos para o alarm()) e retorna na linha após ele. Tratar corretamente de sinais UNIX é assunto para outro artigo. Lembre-se que o sinal interrompe tudo que está acontecendo e retorna no procedimento seguinte ao que foi interrompido. Saiba mais clicando aqui(link em inglês, se você conhecer em português por favor indique nos comentários deste post, obrigado).

Solucão 3 – use stat() no lugar de -M

stat() sempre retorna as datas de modificacão em segundos, assim basta subtrair de time() e teremos sempre preciso o periodo desde que o arquivo foi modificado.

Exemplo:

#!/usr/bin/perl

($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("/etc/passwd");

$modificado_segundos = time() - $mtime;
print "$modificado_segundos\n";

#..... PROCESSO DE LONGA DURACÃO .....
sleep(30);

$^T = time();
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("/etc/passwd");

$modificado_segundos = time() - $mtime;
print "$modificado_segundos\n";

Obtemos:


root@clapton:~/perl# perl modificado_stat.pl
44439213
44439243


Lembre-se que, utilizando stat(), você obtém o retorno em segundos. Se o seu programa utilizar o número de dias e fracão de dia retornada pelo -M você deve se lembrar de converter esse dado dividindo o valor retornado do time() – stat()[9] por 86400(número de segundos num dia).

Como em todos os artigos aqui do blog, se tiver sugestões e outras solucões para este problema, envie através do campo de comentários aqui do blog. As melhores sugestões serão publicadas juntamente com o devido crédito ao autor.

jan 2008 18

Larry Wall, criador da linguagem PerlHá 20 anos, Larry Wall dava ao mundo sua maior contribuicão em software : nascia a linguagem Perl. Quem programa sistemas Unix sabe que há um certo senso de comunidade nesse ramo. Os programadores Unix se unem em torno de projetos, mesmo que isso não lhes renda um centavo de lucro. O sistema operacional Linux, o servidor Apache(e outros projetos da fundacão Apache) e vários outros projetos nasceram desse grupo de hackers(no sentido correto da palavra hacker).

Larry Wall é mais que um hacker comúm vindo do mundo Unix.

Especialista em linguística e exímio programador, Wall decidiu pegar todas aquelas pequenas ferramentas de manipulacão de texto que ele utilizava no ambiente Unix e recriá-las organizadamente para terminar um trabalho de geracão de relatórios que lhe foi delegado por seu patrão na época.

O resultado foi um sistema para automatizar a saída de centenas, milhares de páginas listradas de relatórios em barulhentas impressoras matriciais. Para isso era preciso fixar as colunas, extrair e tabular os dados de alguma fonte, preparar o formato e jogar a saída no dispositivo correto. O resultado foi a Linguagem Prática de Extracão e Relatórios, no inglês PERL.

Diz a lenda que Larry Wall trabalhou no projeto Guerra nas Estrelas do governo de Ronald Reagan. Tenho certeza que o software desse projeto era impecável, porém não podemos dizer o mesmo do resto do SDI(o nome formal do Guerra nas Estrelas era “Strategic Defense Initiative”) visto que Reagan jogou fora trilhões de dólares e o sistema é incapaz de interceptar um mosquito de Febre Amarela. Esse tipo de projeto era onde havía bom dinheiro para programadores nos EUA nos anos 1980. Os melhores programadores da época eram ligados à area militar seja pelo trabalho com criptografía e área-fim do Pentágono ou para desenvolvimento de sistemas administrativos. Segundo diversos especialistas, era assim também na URSS.

Enfim, foi nesse contexto que nasceu a linguagem Perl. O espírito da comunidade Unix foi preservado, e configura a maior vantagem dessa linguagem sobre tantas outras. As bibliotecas Perl são todas abertas e disponíveis para qualquer desenvolvedor. São centenas de milhares de módulos que fazem de tudo o que você imaginar.

Nos Estados Unidos há mais de uma usina nuclear que utiliza Perl em seus sistemas de controle, o Yahoo e o Google utilizam Perl em diversos sistemas de grande porte, os livros de Perl aparecem entre os mais vendidos de informática de vez em quando na Amazon e por aí vai. Tudo isso comecou há 20 anos com o trabalho de um generoso programador que doou sua contribuicão ao mundo.

abr 2008 23

Todo programador sonha em criar uma linguagem de programação, e muitos assim o fazem. Algumas linguagens se mostram expressivas, práticas e, portanto, úteis. Essas linguagens se chamam Perl.

A seguinte tabela lógica(de verdades) resume o que há em termos de linguagens no mercado hoje.

Linguagem de Programação = Perl
C = pai do Perl, veterano de 65535 guerras(0 derrotas)
Máquina Virtual = Mustang 1966 conversível
C++ = C cheio de encrenca
[..]

mai 2008 27

Tem 25 horas que estou tentando descobrir por que o servidor Apache decidiu gerar falhas de segmentação e derrubar o serviço aparentemente sem motivos. Ontem, na maior tranquilidade, atualizei a minha biblioteca CPAN, rodei a rotina de sempre:
# cpan
cpan> install Bundle::CPAN
cpan> install Bundle::LWP
cpan> install DBI
cpan> install DBD::mysql
cpan> install Apache::SessionX

E por aí vai….são 20 módulos que atualizo sempre.

O que me passou desapercebido foi o upgrade do driver MySQL do 4.006 para 4.007. O próprio número de versão, lá na casa dos milésimos, indica uma versão de atualização simples, que não deveria causar maiores transtornos. Eu não poderia estar mais errado.

Logo que atualizei, reiniciei o Apache para reler as bibliotecas do Perl. Como atualizei centenas de módulos(nos Bundle::CPAN e Bundle::LWP), não desconfiei do DBD::mysql de início.

Verificava o log do httpd pai, e, desde ontem de manhã, eis o que encontrava:
[Tue May 27 06:01:16 2008] [notice] child pid 14013 exit signal Segmentation fault (11)
[Tue May 27 06:01:47 2008] [notice] child pid 13698 exit signal Segmentation fault (11)
[Tue May 27 06:02:04 2008] [notice] child pid 13678 exit signal Segmentation fault (11)
[Tue May 27 06:56:07 2008] [notice] child pid 13999 exit signal Segmentation fault (11)
[Tue May 27 06:57:28 2008] [notice] child pid 14000 exit signal Segmentation fault (11)
[Tue May 27 06:58:10 2008] [notice] child pid 14004 exit signal Segmentation fault (11)
[Tue May 27 06:59:48 2008] [notice] child pid 14008 exit signal Segmentation fault (11)
[Tue May 27 07:00:17 2008] [notice] child pid 14002 exit signal Segmentation fault (11)

Esses aí já são de hoje, à partir de 6 AM…. (Vida de programador não é mole…mais uma noite virada….)

Então comecei a luta pra encontrar a biblioteca que causava o SEGFAULT. Baixei o MySQL novo e compilei, em seguida recompilei o Apache com mod_perl 1.29 para buscar a nova versão de libmysqlclient.so.16 ao invés de .so.15, reinstalei o DBD::mysql para linkar com a nova versão, porém as falhas de segmentação continuavam. Volto tudo atrás, retorno ao MySQL 5.0.51a e reinstalo as bibliotecas. Recompilo o PHP para linkar com as .so.15 e também o DBD::mysql. Só nisso se foram 40 minutos, e a cada erro me custava quase uma hora.

Em seguida baixei o Apache 1.3.41, recompilei e nada de solucionar….
Recompilei o Apache com e sem o mod_gzip.c e nada…
Instalei o Apache com e sem o PHP 5.2.4 e nada….
Recompilei com e sem o mod_ssl e nada…
Atualizei as versões das bibliotecas linkadas ao PHP, Freetype, GD….nada…

Começou a bater o desespero….nunca tinha visto um caso desses. Cismei que era sistema de arquivos corrompido, me deu frio na barriga de pensar em atualizar a libc com apenas acesso SSH remoto….um erro e já era. Não arrisquei. Dei um reboot e torcí pra não ser problema de disco….

Coloquei um ping para monitorar quando o servidor voltasse à vida….em alguns instantes tenho o retorno:

64 bytes from ……

Maravilha…o reboot funcionou. Verifico os logs do Apache e os Segmentation Fault continuam lá….

Já eram 5 AM quando decidí começar tudo do zero, a lógica diz que se eu não errar nada no caminho então a pane é de hardware. Não deu pra consertar do modo rápido, então vamos pela força bruta!

Aumentei o nível de debug do Apache sem qualquer resultado, até que cheguei à conclusão que seria inevitável enfiar a mão na massa e ir atrás do stack trace do httpd até achar a chamada que estava acessando memória não autorizada. Lá vamos nós….

Primeiro precisamos compilar o Apache com os símbolos para debug. Só há vantagem em tirar os símbolos de binários compilados se você quiser ocultar o sistema de engenheiros reversos ou tornar o binário alguns bytes menor. No caso do Apache não estamos preocupados com isso, e hoje em dia esbanjamos discos rígidos de 1 Terabyte. Então precisamos adicionar a flag -g ao gcc e recompilar tudo….

# cd apache_1.3.41
# vi configure
Vá até as cercanias da linha 282:
:282
Antes da linha “for var in CFLAGS LDFLAGS LIBS INCLUDES DEPS; do”
Adicione
CFLAGS=-g

Rode o
# ./configure

No meu caso eu utilizo o Makefile.PL do mod_perl assim:

# cd ../mod_perl-1.29/
# perl Makefile.PL USE_APACI=1 APACHE_SRC=../apache_1.3.41/src DO_HTTPD=1 USE_APACI=1 EVERYTHING=1 APACI_ARGS=’–prefix=/usr/local/apache/ –activate-module=src/modules/php5/libphp5.a –enable-module=so –enable-module=ssl –enable-module=rewrite’

Compilar:
# make
# make install

OK, 2 minutos depois e temos um novo httpd em /usr/local/apache/bin/httpd

Agora que temos um httpd recheado de símbolos, vamos dizer ao Apache onde jogar os core dumps.

Edite o seu httpd.conf:
# vi /usr/local/apache/conf/httpd.conf

Adicione :
# 2008-05-27 problema com segmentation fault
CoreDumpDirectory /usr/local/apache/coredump/

Salve(no vi é com :wq!) e reinicie o Apache

# /usr/local/apache/bin/apachectl restart

( Veja a linha antes de CoreDumpDirectory, a deixei lá de propósito. É sempre bom comentar as alterações para, no futuro, você lembrar que nesse dia você apanhou um bocado ;) . Falando sério: hoje você lembra por que editou o httpd.conf, mas daqui a 6 meses você talvez fique na dúvida de por que a diretiva de core dumps está alí. Por via das dúvidas, comente seus arquivos de configuração. Eu utilizo sempre o mesmo formato de datas aaaa-mm-dd para poder fazer buscas no futuro por anos ou meses aproximados. )

Agora aguarde o servidor cair! No meu caso foi quase instantâneo. Vou até o diretório de core dumps e não encontro qualquer arquivo core apesar das falhas repetidas do servidor…. São os efeitos da falta de sono, esquecí de permitir os core dumps aumentando o ulimit -c. Como ulimit -c 0 os core dumps ficam desligados….

# ulimit -c 10240

Damos um ulimit de 10 MBytes para tamanho máximo de arquivos core. Agora reiniciamos o Apache, e não demora encontro lá os arquivos core.nnnn. Vamos examinar o core.1035 para ver onde foi a pane:

# gdb ../bin/httpd -c core.1035
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i686-pc-linux-gnu”…

[... um apanhado de saída, até chegar em ....]

Program terminated with signal 11, Segmentation fault.
[New process 1045]
#0 0x084b739c in Perl_av_undef ()

OK, o que será que houve em Perl_av_undef() ??? Esse tempo todo desconfiando do MySQL e o culpado era o núcleo do Perl ???!

A julgar pelo nome, Perl_av_undef() parece ser uma rotina de limpeza(undef) de arrays(AV = Array Value). Sem mais chutes pesquiso no Google e descubro que não se trata de um bug do Perl e sim de uma limitação imposta pelo sistema através do ulimit. Quando o Perl tenta limpar uma estrutura de dados complexa, arrays de arrayrefs contendo referências a objetos ou hashrefs, por exemplo, ele pode gerar uma grande quantidade de chamadas recursivas. A recursividade é uma ferramenta poderosa, mas que às vezes deve ser substituída por outras técnicas para poupar a pilha. A recursividade é quase sempre uma saída elegante, porém nem sempre a mais eficiente. A cada chamada o Perl empilha mais valores na stack, eventualmente estourando a pilha e causando uma Segmentation Fault(sinal 11). Quem derruba o programa é o próprio kernel, e isso não é um bug, é uma característica de sistemas que impedem processos de levarem a máquina toda consigo no caso de perda de controle.

Então vamos verificar o tamanho atual da pilha. Vamos aproveitar e verificar outros valores-limite para sabermos em que chão estamos pisando.

#ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
pending signals (-i) 16253
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 16253
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Temos uma pilha de 10 MBytes, o que nem sempre é suficiente. Vamos exagerar um pouco para testar nossa tese, se aumentarmos para 100 MBytes teremos a chance de ver se os core dumps deixam de acontecer.

# ulimit -s 102400

Verificamos se deu certo com:

# ulimit -s
102400

OK, temos 100 MB autorizados pro Perl fazer a festa na pilha. Vamos reiniciar o Apache e ver se funciona.

# /usr/local/apache/bin/apachectl stop
# /usr/local/apache/bin/apachectl start

Prefiro o stop e start explícitos quando estou testando, para eliminar a possibilidade de alguma memória permanecer alocada do processo anterior. Pode ser só paranóia, mas vamos adiante. Temos que aguardar alguns instantes para testar a estabilidade do novo ambiente que demos ao Apache.

OK, após 5 minutos verifico que os Segmentation Faults continuam acontecendo…só que, conforme veremos, desta vez o culpado não é mais o Perl_av_undef()

Vamos examinar um dos novos arquivo core.nnnn para tentar descobrir quem é o culpado desta vez.

# gdb /usr/local/apache/bin/httpd -c core.1047
[... cortado ...]
Program terminated with signal 11, Segmentation fault.
[New process 1047]
#0 0x00b9e46b in mysql_ping () from /usr/local/mysql//lib/mysql/libmysqlclient.so.15

Opa! mysql_ping() ….estamos voltando ao principal suspeito, desde o início o servidor caía perto de alguma chamada ao MySQL no código Perl. Vamos ver o que ocorre logo antes do mysql_ping() usando o comando where do gdb.

(gdb) where
#0 0x00b9e46b in mysql_ping () from /usr/local/mysql//lib/mysql/libmysqlclient.so.15
#1 0x004d99f2 in XS_DBD__mysql__db_ping (cv=0x94d2680) at mysql.xs:519
#2 0x001edbf4 in XS_DBI_dispatch () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/DBI/DBI.so
#3 0x084bfa43 in Perl_pp_entersub ()
#4 0x084be32e in Perl_runops_standard ()
#5 0x084ba15d in Perl_call_sv ()
#6 0x0043ae8f in EMBPERL2_CallStoredCV () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#7 0x00453cf0 in embperl_Execute () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#8 0x0045a008 in ProviderEpRun_GetContentIndex () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#9 0x0045891a in Cache_GetContentIndex () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#10 0x00434a63 in ProcessFile () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#11 0x004355da in embperl_RunRequest () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#12 0x00435b8c in embperl_ExecuteRequest () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#13 0x0043233f in XS_Embperl__Req_ExecuteRequest () from /usr/lib/perl5/site_perl/5.10.0/i686-linux/auto/Embperl/Embperl.so
#14 0x084bfa43 in Perl_pp_entersub ()
#15 0x084be32e in Perl_runops_standard ()
#16 0x084ba15d in Perl_call_sv ()
#17 0x08371d3b in perl_call_handler (sv=0x96a8d20, r=0x9541c14, args=0×0) at mod_perl.c:1668
#18 0x083723f9 in perl_run_stacked_handlers (hook=0x85ef03b “PerlHandler”, r=0x9541c14, handlers=0x96a8cb0)
at mod_perl.c:1381
#19 0×08373861 in perl_handler (r=0x9541c14) at mod_perl.c:904
#20 0×08395497 in ap_invoke_handler (r=0x9541c14) at http_config.c:476
#21 0x083acd2f in process_request_internal (r=0x9541c14) at http_request.c:1299
#22 0x083ad1a0 in ap_internal_redirect (new_uri=0x951427c “/index.html”, r=0x9512ae4)
at http_request.c:1440
#23 0x00386fe4 in mod_gzip_redir1_handler () from /usr/local/apache/modules/mod_gzip.so
#24 0×00385219 in mod_gzip_handler () from /usr/local/apache/modules/mod_gzip.so
#25 0×08395497 in ap_invoke_handler (r=0x9512ae4) at http_config.c:476
#26 0x083acd2f in process_request_internal (r=0x9512ae4) at http_request.c:1299
#27 0x083acd8c in ap_process_request (r=0x9512ae4) at http_request.c:1315
#28 0x083a2ef6 in child_main (child_num_arg=12) at http_main.c:4971
#29 0x083a3227 in make_child (s=0x87be05c, slot=12, now=1211894763) at http_main.c:5150
#30 0x083a32c2 in startup_children (number_to_start=23) at http_main.c:5178
#31 0x083a3a66 in standalone_main (argc=3, argv=0xbfe95934) at http_main.c:5525
#32 0x083a434f in main (argc=3, argv=0xbfe95934) at http_main.c:5883

Lendo de baixo pra cima, o where nos dá um trace de pilha completo desde a função main() até o mysql_ping().

Sem mais demora, jogo a consulta no Google e o resultado é que mais alguém acaba de ter o mesmo problema há apenas 1 semana!

E o culpado é???? O DBD::mysql v. 4.007, que foi um dos primeiros módulos que atualizamos no servidor de produção ainda ontem! Confira o report do bug MySQL clicando aqui.

Solução: regredir para a versão 4.006. Como fazer isso se o CPAN sempre busca a última versão? É simples.

Vá até a página do DBD::mysql no endereço http://search.cpan.org/dist/DBD-mysql/.

Na caixa “Other Releases” escolha a versão anterior(4.006) e clique em Goto.

Quando a página recarregar, o link Download apontará para a versão escolhida. Baixe-a, extraia com

# tar xzvf DBD-mysql-4.006.tar.gz
# cd DBD-mysql-4.006
# perl Makefile.PL
# make
# make install
Reinicie o Apache para ler o DBD::mysql antigo
# /usr/local/apache/bin/apachectl restart

E, pelo menos até o momento, os problemas de segmentação acabaram.

Conclusões

1) Os módulos de um servidor que se propõe a ser padrão industrial tem que ser melhor testados. O bug é crasso, é primário. O módulo tenta executar um mysql_ping() e derruba o processo todo. A MySQL, recém comprada pela Sun, vai ter que efetuar um controle de qualidade mais eficiente.

2) O apache 1.3 está sendo lentamente abandonado, porém ele poderia ter um sistema de gerenciamento de erros mais “civilizado”. Ninguém tem o luxo de passar 20 horas no GNU Debugger verificando stack traces, recompilando programas gigantescos e tentando montar quebra-cabeças.

3) Não confie cegamente em updates. Efetue um update de cada vez e teste o sistema novamente. Se eu tivesse testado o sistema após o update do DBD::mysql, ao invés de atualizar o CPAN todo, eu saberia que o culpado era o DBD::mysql. Nunca tinha tido problemas com um módulo do CPAN, por isso deduzí logo se tratar de algum conflito de bibliotecas compartilhadas.

4) Aumente a pilha para processos que irão rodar durante longos períodos. Apenas 10 MB de pilha para servidores Apache, MySQL ou Oracle é muito pouco. Vimos que o DBD::mysql não era o único culpado pelos Segmentation Faults do Apache. Aumentei o ulimit da pilha para 204800 KBytes(200 Megas) para garantir.

5) Problemas entrelaçados, como a stack pequena e o bug do MySQL combinados, podem causar muita confusão. Quem imaginaria que os 2 ocorreriam ao mesmo tempo e se manifestariam da mesma forma?! As mensagens no log são idênticas, não há diferença. Se não tivesse me dado o trabalho de compilar o Apache com símbolos e estudado o stack trace no gdb, eu pensaria que os primeiros core eram problema do MySQL. Aliás, durante boa parte da resolução do problema eu achava que as bibliotecas MySQL eram as únicas culpadas.

Já faz 1 hora e 30 minutos que restabelecí o serviço e não vejo qualquer SEGFAULT registrado. Agora vem a parte difícil, explicar pra minha chefe tudo isso…..

fev 2009 17

A seção de programação do Reddit.com costuma ser a mais útil entre os sites de notícias sociais. Todos os dias procuro algo novo e interessante para aprender por lá.

Eis que hoje me deparei com uma verdadeira pérola: uma tabela periódica dos operadores Perl 6, inspirada numa palestra ministrada por Larry Wall(criador da linguagem).

Tabela periódica de operadores Perl 6

Tabela periódica de operadores Perl 6

É uma verdadeira obra de arte, clique aqui para conferir.

Page 2 of 41234