вторник, 30 августа 2011 г.

Оптимизация сайта (RLib)

С недавним обновлением RLib был оптимизирован поиск служебных классов "rload_controllers" во всех элементах страницы.
Приведу немножко статистики и результаты профилирования.
Чуть раньше поиск спец. классов "rload_controllers" выполнялось через проход по словарю известных rload_классов и поиском совпадений в DOM-е через jQuery('.rload_somecontroller') для каждого элемента словаря. Десятки раз вызывалась функция-обертка jQuery() и десятки тысяч jQuery.hasClass().
По результатам профилирования в Firebug на главной странице боевой rutube.ru, среднее число вызовов всех функций на сайте составляло 120-130 тыс. и среднее время выполнения скриптов 1.1-1.2 секунды.
После рефакторинга RLib в части метода dynamicLoad (в котором производится основная часть проходов по DOM), цикл проходов по словарю был заменен одни проходом по DOM посредством метода document.getElementsByTagName('*') и сравнением классов каждого элемента с элементами rload-словаря.
По результатам профилирования в бою, число вызовов функций составило 60-70 тыс.и время выполнения 470-550 мс.
Таким образом:
- суммарное число вызовов функции hasClass() сейчас 20-40 тыс вызовов, время работы 30-60мс;
- суммарное время функции dynamicLoad = 70-80мс.

У меня появилась идейка как можно еще больше оптимизировать это достаточно узкое место.
Например, если вынести спец. метки контроллеров в верстке (rload_controllers) из атрибута class в специальный атрибут например data-rload, тогда можно будет в одном проходе по всем элементам сразу ловить метки для подгрузки контроллеров, и избавиться от вызовов достаточно медленной функции hasClass в этом критичном месте.
Так как doctype документа XHTML, это не противоречит стандарту.
Плюсы:
1) избавляемся от вызовов hasClass и выигрывает 20-40 тысяч вызовов, и соответственно время загрузки.
2) перестаем быть сильно чувствительны к разрастанию DOM, т.к. сейчас чем больше элементов на странице, тем больше вызовов достаточно медленной hasClass()
Я подготовил тест производительности, который подтверждает что реализация с getAttribute() почти в 20 раз быстрее hasClass() который реализован у нас сейчас: http://jsperf.com/getattrvshasclass

Чем грозит:
1) переделка всех шаблонов, где подключение контроллеров выполняется в атрибутах class="" и страшный мердж в svn :)
2) соглашение в дальнейшем подключать js-контроллеры через атрибут data-rload

Скорее всего выигрыш составит не больше 80-100 мс, но так по крупицам сможем суммарно увеличить время загрузки.
Коллеги, интересно ваше мнение по этому поводу.

Вывод XML на сайте.


У базового приложения в сайте появился метод output_xml(). Enjoy!

пятница, 12 августа 2011 г.

Интерфейс балансера

  1. В транке появился клиент балансера - Rutube::VideoBalancer2. Из полезного функционала на текущий момент позволяет выполнять RPC-вызовы API балансера.
  2. После короткого обсжудения, пакет RPC::XML принят дефакто стандартом для использования в перловом коде. С его помощью можно создавать как клиентов, так и серверы XML-RPC.

среда, 10 августа 2011 г.

Думай, прежде чем начнёшь программировать!

Очень кстати под руку подвернулась старая статья

Before coding… Think! | Making Good Software

Если кратко, то она раскрывает смысл известной пословицы "Одна голова хорошо, а две лучше!". Но советую прочитать целиком, так как вся соль в деталях.

Всем хорошего дня.

понедельник, 8 августа 2011 г.

Бессмертная статья Кента Бека

Сегодня снова увидел а своём ридере и с удовольствием перечитал. Великолепный конспект!

Учись Работать / Иллюстрированный конспект лекции Кента Бека «Четыре стратегии отзывчивого дизайна» (с комментариями)

Версионная миграция cron-таблиц и БД

Коллеги, я накидал пару описаний

Версионная миграция cron-таблиц

Версионная миграция БД

и создал соотвествующие структуры в SVN в транке.

Это очень сырой вариант процедур, но мы их начнём обкатывать  на DRM и ПРД. Очень надеюсь, что все примут активное участие в их улучшении.

Лёша Л. тебе нужно это внедрить в свой процесс релиз менеджмента и возможно автоматизировать рутинные операции.

Прошу конкретные замечания и предложения кидать в конфлюенс. А флудить лучше в блог.

среда, 3 августа 2011 г.

Баг с memcached

Всем хеллоу!

    Во время тестирования trackinfo обнаружили баг связанный с тем что обрывается соединение с сервером. Причина, предположительно, модуль Cache::Memcached::Fast считает что во время первого соединения произошла ошибка и лочит сервер на failure_timeout секунд. Лочить или нет устанавливает свойство max_failures.  Эта ошибка воспроизводится только во время работы в окружении fastcgi. Сейчас из конфига мемкешей убрали опцию


max_failures (если это значение не указано либа ставит его в 0). 

К сожалению у этой библиотеки нет debug режима =(. Ещё одно замечание, если поменять базовый класс в Rutube::Memcached c Cache::Memcached::Fast на Cache::Memcached то всё отлично! WTF? Oo

Отсюда делаем предположительный вывод, что либа засекает ошибку при первом обращении по ключу (оно кстати возвращается), затем вырубает сервак и после этого не может найти ключ в остальном пуле серваков.

Воспроизвести подобное поведение в скрипте не удалось =(

use Perl or die(); гы-гы =)

вторник, 2 августа 2011 г.

Атомарная операция test-and-set на мемкеше

Из доки на мемкеш-клиент:
  # Do atomic test-and-set operations.   my $cas_val = $memd->gets('nkey');   $$cas_val[1] = 0 if $$cas_val[1] == 12;   if ($memd->cas('nkey', @$cas_val)) {       print "OK, value updated\n";   } else {       print "Update failed, probably another client"           . " has updated the value\n";   }

Баг ОРМ'а

Вкратце, баг касается переиспользования объекта, созданного с использованием параметра obj_new =1. Актуально при маппинге на таблицы с первичным ключом без auto_inctement. В этом случае после использования $obj->save(), значение ключа "убивается" и $obj->getID() выдает значение 0. Соответственно, при следующей попытке сохранения, в базе создается новая запись, соответствующая данному значению ключа (id=0), вместо ожидаемого апдейта существующей.

Ниже приведен пример, позволяющий убедиться в данной неприятности.

1) Таблица в БД:

CREATE TABLE `test` (
`id` int(11) NOT NULL,
`some_value` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=koi8r;

2) ОРМ:

package Rutube::Obj::Test;

use warnings;
use strict;

use Rutube::Object;
use base qw[Rutube::Object];


mapper( '-table' => 'test', '-key' => 'id' );
set_property_type('db');

property('id');
property('some_value');


1;

__END__

3) Тестовый скрипт:

#!/usr/bin/perl -w

use strict;
use warnings;

use lib qw|../lib|;
use Rutube::Config;
use Rutube::ObjectRegistry;

use Rutube::Obj::Track;

my $conf = Rutube::Config->new('../conf/.config');
Rutube::ObjectRegistry->setNamespace('offline');

use Rutube::Obj::Test;

my $obj = Rutube::Obj::Test->new({id => 123, some_value => 'asdf'}, {obj_new => 1});
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id=123|some_value=asdf]

$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=asdf]

$obj->setSomeValue("qwerty");
$obj->save();
printf "[id=%3d|some_value=%s]\n", $obj->getID(), $obj->getSomeValue();
# [id= 0|some_value=qwerty]

exit 1;

4) Имеем в результате:

idsome_value
0qwerty
123asdf