Codebits
that rock!

Jun 15, 2013

WWW::Mechanize and keeping tracks of the performed requests

posted by varnie • Tags: perlShow comments

Столкнулся с проблемой при использовании WWW:Mechanize - после многократной отправки POST запросов с большими данными Perl падал с <p class="terminal">Out of memory</p>. Вот этот очень простой пример:

#!/usr/bin/perl

use strict;
use warnings;
use WWW::Mechanize;
use 5.010;

$| = 1;

my $mech = WWW::Mechanize->new(autocheck => 1);

foreach (1..1000) {
    $mech->post('http://SOME_URL_HERE', [some_action => 'SOME_ACTION_HERE',  some_data => join ('', map { int rand 2} (0..63)) x (16*1024*50)]);
    say $mech->response->decoded_content;
}

То бишь, 1000 раз шлем POST запрос со строчкой-мусором длиной в 50 мегабайт. Жирная строчка, да, но не в этом вся соль. К примеру, на моей машине программка падала ближе к 50-ой итерации.

Нелогично и непонятно - по-хорошему память должна подчищаться после каждой итерации.

Первая мысль - на каждой итерации создавать новый инстанс WWW::Mechanize, но зачем городить огород? Давайте разберемся. На помощь придет хорошо всем знакомый Data::Dumper. Итак, дебажный код:

#!/usr/bin/perl

use strict;
use warnings;
use WWW::Mechanize;
use 5.010;
use Data::Dumper;

$| = 1;

my $mech = WWW::Mechanize->new(autocheck => 1);

foreach (1..1000) {
    $mech->post('http://SOME_URL_HERE', [some_action => 'SOME_ACTION_HERE',  some_data => join ('', map { int rand 2} (0..63)) x (16*1024*50)]);
    say Dumper($mech);
    say $mech->response->decoded_content;
}

Запускаем в консоле и наблюдаем: чем дальше в лес - тем толще партизаны, т.е. чем больше прогнано итераций в цикле, тем длиннее вывод генерит дампер:). Узкое место - поле “page_stack” (массив хранящий инфу о предыдущих прогнанных запросах)!!! Оно растёт с каждой новой итерацией.

Для нашего случая хранить эти данные абсолютно не нужно, поэтому идём на сайт WWW::Mechanize и читаем:

stack_depth => $value Sets the depth of the page stack that keeps track of all the downloaded pages. Default is effectively infinite stack size. If the stack is eating up your memory, then set this to a smaller number, say 5 or 10. Setting this to zero means Mech will keep no history.

Итоговый, рабочий вариант, который не жрёт память аки конь:

#!/usr/bin/perl

use strict;
use warnings;
use WWW::Mechanize;
use 5.010;

$| = 1;

my $mech = WWW::Mechanize->new(autocheck => 1);
$mech->stack_depth(0); #do not keep track of the performed requests!

foreach (1..1000) {
    $mech->post('http://SOME_URL_HERE', [some_action => 'SOME_ACTION_HERE',  some_data => join ('', map { int rand 2} (0..63)) x (16*1024*50)]);
    say $mech->response->decoded_content;
}

Мораль - читать доки об используемых либах.


blog comments powered by Disqus
Fork me on GitHub