Sunday, August 16, 2009

Gtk-Message: Failed to load module "globalmenu-gnome"

Некогда пробовал gnome-globalmenu, не понравилось, убил.

С тех пор ловил при каждом запуске gtk приложения такую бяку:

Gtk-Message: Failed to load module "globalmenu-gnome": libglobalmenu-gnome.so: cannot open shared object file: No such file or directory
и


Решение тут:

http://magnus-k-karlsson.blogspot.com/2009/02/remove-gnome-globalmenu.html

Вкратце в gconf-editor /apps/gnome-settings-daemon/gtk-modules задизейблить ( убрать галочку) globalmenu-gnome

Wednesday, August 12, 2009

php и циклические ссылки

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

После беглого просмотра кода и сопутствущих классов, время было оптимизировано до минуты. Проблема была в том, что первоначальная версия была написана не очень хорошим программистом, а остальные ленились кардинально переписать приложение. Также для генерации отчета в циклах использовались классы, которые на многие действия совершали запросы к базе, а вызывались эти методы внутри цикла. Использовав упреждающее чтение всех необходимых данных в память и минимальную модификацию классов для использования получанных данных в случае их наличия, приложение стало работать гораздо быстрее (1 минута из которой 40 секунд происходит чтение всех необхомых данных в память, 20 секунд непосредственно обработка и запись данных). Ну а количество запросов сократилось до 3-х вместо 3*количество записей в обрабатываемых таблицах.

В идеале можно было бы обойтись без преобразования данных в коде, выполнив его на уровне базы данных, но от этого пришлось отказаться из за того, что на боевой базе данных, подобного рода запросы иногда вызывали проблемы в репликации данных.

Вторая проблема которую я увидел это то, что это приложение было по сути копипастом из одного часто используемого метода, в итоге переписав и то, и другое, я получил скрипт, который исправно работал, но в 10 раз медленнее минутной версии, но обладал замечательным свойством, что был готов для повторного использования, стал более гибким, и читать его стало проще. Однако возникла следующая проблема, уже на 200.000 записях он стал отжирать 4 гб памяти и операционной системе это очень не нравилось, впрочем как и мне. Почистив код, я убедился что происходит утечка памяти.

Вот код, который вольно воспроизводит сложившуюся ситуацию.

 

class DataSource // в моем случае это был итератор по таблице базы данных
{
protected $_counter = 1;
public function next()
{
return array('xxxxxxxxxxxxxxxxxxxxxxxx' => 'yyyyyyyyyyyyyyyyyyy');
}
}

class UserHelper // род классов, для получения данных связаных с пользователями, но выходящих за рамки этого объекта
{
protected $_u = null;
public function __construct(User $u)
{
$this->_user = $u;
}
public function getSomeData()
{
return array('x' => 10, 'y' => 20);
}

public function __desctruct()
{
$this->_u = null;
}
}

class User
{
protected $_dbRow = array();
protected $_helpers = array();
public function __construct(array $data )
{
$this->_dbRow = $data;
}

public function getHelper()// получения хелпера, в данном случае ситуация урезана до одного
{
if ( isset($this->_helpers['x'] ) )
return $this->_helpers['x'];
return $this->_helpers['x'] = new UserHelper($this); // автор посчитал это полезным
}

public function __destruct()
{
unset($this->_dbRow);
$this->_helpers['x'] = null;
}
}

// кусок кода вызвавший проблему.
$ds = new DataSource();
for($i= 1; $i++; $i< 1000000)
{
$u = new User($ds->next()); // загружаем пользвоателя напрямую по данным
$helper = $u->getHelper();
$helperData = $helper->getSomeData();// получаем данные хелпера
//делаем то что нужно с данными
unset($helperData);
unset($helper);
unset($u); // думаем что убили пользователя
}


При запуске этого скрипта и запущенном топе, видим как память отжирается без остановки.

Проблема в циклических ссылках. Не охота это описывать можно почитать ТУТ

После того как я нашел утечку. Код переписан в таком виде


$ds = new DataSource(); // получаем итератор
for($i= 1; $i++; $i< 1000000)
{
$u = new User($ds->next()); // загружаем пользвоателя напрямую по данным
//$helper = $u->getHelper(); вот тут и была проблема.
$helper = new UserHelper($u);
$helperData = $helper->getSomeData();// получаем данные хелпера
//делаем то что нужно с данными
unset($helper);
unset($u); // думаем что убили пользователя.
}



После этого, время выполнения приложения стало чуть больше 20 % от первоначальной минуты, ну а раход памяти стал фиксированным ~300-400 MB

Вывод: Консольные приложения обычно пишутся для больших объемов данных, и то что прощается при выполнении маложивущей веб-страницы, выходит боком при долгой работе приложения.

Sunday, May 3, 2009

Неоднозначности PHP

За три месяца молчания накопились "прелести" PHP с которыми можно и поделиться.
Меньше слов больше консоли.

Для начала кусок докуметации о сравнении массивов


Для массивов $a и $b
$a == $b Равно TRUE в случае, если $a и $b содержат одни и те же элементы



php -a
php > $a = array(1,2);
php > $b = array(2,1);
php > var_dump($a == $b);
bool(false)


Для меня это оказалось несколько неожиданным изходя из документации, однако следущий код неожиданно вносит ясность.


php > class X {};
php > $x1 = new X();
php > $x2 = new X();
php > $a = array($x1, $x2);
php > $b = array($x2, $x1);
php > var_dump($a == $b);
bool(true)


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

Еще замечательный пример с которым я столкнулся в практике.

Во время рефакторинга, значение метода в случае ошибки (false) было изменено на (null) проверку понятное дело я забыл сделать, а по дефолту метод возвращал объект известного типа. В итоге код вида


php > $x = $object->getResult();
php > $x->value = 'smth';
php > $x->callSmth();

Возвращал Fatal error: Call to undefined method stdClass::callSmth() in php ... некоторое время я тупил удивляясь откуда взялся stdClass, (и сразу пошел в неверном направлении, греша на serialize при работе с memcache), однако быстро понял что дело не в этом.

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

php > $a = null
php > ;
php > $a->x = 10;
php > var_dump($a);
object(stdClass)#1 (1) {
["x"]=>
int(10)
}


Если провести тесты не с null, а скажем с boolean значением, получем тот же результат, по идее все логично, тогда код со строкой, должен отрабатывать эквивалентно.


php > $b = 'sdf';
php > $b->x = 10;

Warning: Attempt to assign property of non-object in php shell code on line 1


Все страньше и страньше. Ну и на последок забавный код.


php > class null{};#я думал так нельзя
php > class false extends null{};# можно и так
php > class true extends false { function __toString() { return 'false'; } } ; #это уже понятно
php > $false = 'false';
php > $true = new True();
php > var_dump($$true);#ну это уже стеб.
string(5) "false"


Велики возможности PHP !!!

Friday, February 20, 2009

Размышения о php, его применении

Статья о моих метаниях в процессе разработки и правильном использовании PHP.

Для затравки желательно прочитать статью 10 принципов PHP специалистов.

В принципе ничего нового. Теперь что думаю я.

1. PHP медленный язык. И не надо рассказывать о том, что большую часть времени занимает работа с базой, мемкэшем, файлами и т.д.
Даже вызов PHP фукнции достаточно дорогая операция. А уж подключения библиотеки - кошмар.

2. Фреймворки - монструозны, а если это сложить с пунктом один, получаем, что они еще и тормозны по определению.

Исходя из этих требования, а также под угрозами бешеных нагрузок я участвовал в написании проекта, который был минималистичен в плане порождения сущностей. На удобстве и скорости разработки это мало сказалось, т..к была изобретен минимальный фундамент (велосипед), который не давал скатиться в написание портянок. Зато время генерации при небольшой и средней нагрузках было 0.01-0.03 сек. Но т.к. администратор выбрал распределённую фс, это выразилось в подскакивании времени генерации до 0.1 сек.

В ходе этого я выяснил.

3. Проблемы описанные в пп 1,2 неприятны, но решаемы, в основном кэшированием и настройками серверов, или отказом от PHP.

Скажем так, оптимизация веб-сервера (отключение модулей апача, extensions php) дало прирост 10-20%, (смена апача на lighttpd +fcgi) дало прирост еще на 30%. Я бы не смог настолько оптимизировать код по всему проекту.

Затем оказалось что нагрузки миф, политика партии поменялась, и нам нужно внедрять кучу странной логики, во главе встало удобство поддержки проекта, которая в свою очередь требует другого подхода к разработке.

В итоге я ударяюсь (контролируемо) в другую крайность, в правильную разработку. Т.е. играемся с проектированием на всю катушку. Начинаем смотреть в сторону ОРМ уже в качестве необходимости.


Прошло время я наигрался и тем, и с другим. Сразу условлюсь что проект я предлагал делать на java, но руководство выбрало php по понятным всем причинам.

А теперь пример, который просто меня убил.

Задача : написать ладдерную систему для неких игр. Грубо говоря чем с более крутым человеком ты играешь, тем больше очков ты получишь, тем выше будет твой рейтинг.

Итак, есть игрушки, которые потенциально кидают много запросов. Есть рейтинг игроков который нужно пересчитывать по определенным формулам.

Реализация на php:

скрипт сохранения результатов, пишет их в файл. В ночное время, результаты пересчитываются, сбрасывается кэш. При просмотре результатов, страница кэшируется на сутки. Получилось дешево ( в плане ресурсов), недорого. Это решение было написано нормально, но из-за кучи модификаций ее стало трудно поддерживать, причем не по причине кривости кода, а по причине сложной логики вокруг него.


Реализация на python:

Twisted server, который в памяти хранит пользователей, отсортированных по результатам. При новых результатах, пользователь удаляется из отсортированного листа, ему начисляется новый скоринг, и он вставляется в список. Сам сервер по xmlrpc умеет отдавать результаты - веб серверу.
Веб сервер работает на старом движке, вместо запросов к базе - запросы к xmlrpc-server.

Реализация - быстрее чем на php(по времени разработки и отладки), скорость работы - выше, удобство использования - выше.

Самый большой плюс, система работает online!

Из недостатков - новая сложность системы в виде нового сервера.

Конечно, можно было бы написать такой сервер и на PHP, но не думаю чтобы мы добились его нормальной работы за приемлемое для нас время.


Также могу привести еще несколько подобных вещей. В общем мое мнение ниша PHP - это все-таки fron-end, который отображает информацию от кучи разнородных сервисов, где собственно и будет реализована вся логика.

Sunday, February 8, 2009

PHPUnit, DBUnit, тестирование или Class PHPUnit_Extensions_Database_TestCase could not be found

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

Вместе с внедрением тестирования, пытаюсь при необходимости покрывать тестами более старые участки кода. В качестве framework был выбран PHPUnit за интеграцию с Zend Studio + некоторые приятные вещи.


В процессе написания тестов возникла необходимость в инструменте для работы с fixtures + тестирования изменений в базе данных, DBUnit как extension к PHPUnit подошел замечательно.

Далее грустная история о нескольких часах дебага. И об ошибке "Class PHPUnit_Extensions_Database_TestCase could not be found"

1. Изначально был тест XXX_Test


require_once 'PHPUnit/Framework/TestCase.php';
class XXX_Test extends PHPUnit_Framework_TestCase
{

public function testSmth()
{
$this->assertTrue(true);
}
}


Файл назван xxxTest.php

За некоторое время работы с phpunit я отвык пользоваться формой запуска

$phpunit TestClassName path/to/test/file.php


И пишу просто


$phpunit path/xxxTest.php
PHPUnit 3.3.10 by Sebastian Bergmann.

..

Time: 0 seconds

OK (1 test, 1 assertion)


Т.е. все хорошо, мой класс несколько шире, и мне хочется тестировать его на основе PHPUnit_Extensions_Database_TestCase, для чего пользуюсь туториалом отсюда

В итоге класс становится таким.


require_once 'PHPUnit/Extensions/Database/TestCase.php';
class XXX_Test extends PHPUnit_Extensions_Database_TestCase
{

protected function getConnection()
{
return null;// $this->createDefaultDBConnection($this->_dbObject,'sqlite');
}

protected function getDataSet()
{
return '';//$this->createFlatXMLDataSet('path/to/fixtures/dummy.xml');
}

public function testSmth()
{
$this->assertTrue(true);
}
}


Функции коннекта и загрузки данных, преднамеренно реализованы криво.

Запускаем.

$phpunit path/xxxTest.php

О***ваем

PHPUnit 3.3.10 by Sebastian Bergmann.

Class PHPUnit_Extensions_Database_TestCase could not be found in /var/www/toox.com.lottery/core/classes/db/query/Rapid_Db_Query_IteratorTest.php


Я повяз в дебаге на пару часов, т.к. это был мой первый опыт работы с DBUnit + также приведенный пример сильно упрощен по сравнению с тем файлом, с которым я работал.


В итоге оказалось, что при работе с DBUnit обязательно использовать полную форму запуска phpunit

Т.е. при запуске нужно было указать имя тестируемого класса.


$phpunit XXX_Test path/xxxTest.php

PHPUnit 3.3.10 by Sebastian Bergmann.

E

Time: 0 seconds

There was 1 error:

1) testSmth(XXX_Test)
Argument 1 passed to PHPUnit_Extensions_Database_DefaultTester::__construct() must implement interface PHPUnit_Extensions_Database_DB_IDatabaseConnection, null given, called in /usr/share/php/PHPUnit/Extensions/Database/TestCase.php on line 146 and defined



Т.е. теперь уже посыпались человеческие ошибки, которые говорят что я не реализовал необходимый для работы интерфейс. Ну оно и понятно...

Thursday, February 5, 2009

перенос svn репозитория.

Три раза менял vds. Было жалко свой svn репозиторий, и озадачился вопросом его переноса на другую машину.
Оказалось все просто как всегда.

Сначала скидываем репозиторий в dump file.

#svnadmin dump /home/svn/repo/ > /tmp/mysvn.dump


Т.е. svnadmin dump выплевывает dumpfile-formatted строки в stdout, который перенаправляется в наш файл /tmp/mysvn.dump


На другой машине

#scp login@host:/tmp/mysvn.dump /tmp
#svnadmin create /new/repo/path
#svnadmin load /new/repo/path < /tmp/mysvn.dump


Копируем при помощи scp файл себе локально, затем создаем новый репозиторий при помощи svnadmin create , далее командой svnadmin load, которая как раз читает из stdin dumpfile-formatted строки, переносим сдампленные данные.

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

Wednesday, January 28, 2009

svn+ssh на другом порту

поломали хостинг по моей безалаберности, сижу перенастраиваю.

В работе часто использую svn, т.к. по сути он на 90% single мне хватает svn+ssh протокола (++ в том, что не нужно ничего настраивать).

Перенес sshd порт с дефолтного на другой, svn+ssh://host:port/path не работает. Решение нашел достаточно быстро вот здесь

Ниже копипаст одного из решений.



Существуют два варианта решения. Первый - добавить в конфигурационный файл ~/.subversion/config строчки:
[tunnels]
tunnel_name = /usr/bin/ssh -p port

И в дальнейшем использовать вызов svn co snv+tunnel_name://hostname/path. Например,
[tunnels]
foobar = /usr/bin/ssh -p 8022

Тогда вызов будет выглядеть как:
svn co snv+foobar://hostname/path

Monday, January 26, 2009

Perl как замена sed, или perl однострочные скрипты.

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

Теперь по делу.

Операционная система у меня ubuntu ++ постоянно по работе приходится сидеть в консоли. Очень часто возникает задача, быстро по шаблону поменять что то в файле. Ответ на вскидку = sed. В свое время я читал про него, но понял что при помощи perl могу делать то же самое. Прошло время, sed я забыл совершенно, а вот perl тоже стал забываться, и я постоянно не могу найти пример однострочных скриптов.

Теперь по порядку.

Задача:


Дано.
Есть файл xx.txt в котором лежат числа от 1-го до 5-ти, каждое число, новая строка.


cd@laptop:~/data/tmp$ cat xx.xx
1
2
3
4
5


Нужно:

Хочется заменить все вхождения строки 3 на что то свое, пусть это будет строка "hello"


Решение:

cd@laptop:~/data/tmp$ perl -ig -ne 's/3/hello/;print $_; ' xx.xx
cd@laptop:~/data/tmp$ cat xx.xx
1
2
hello
4
5


Или


cd@laptop:~/data/tmp$ perl -ig -pe 's/3/hello/; ' xx.xx
cd@laptop:~/data/tmp$ cat xx.xx
1
2
hello
4
5
cd@cd-acer:~/data/tmp$ ls *g
9.jpg xx.xxg




Объяснение.

perl позволяет запускать однострочные скрипты, не создавая файл с кодом скрипта.

Пример:

cd@laptop:~/data/tmp$ perl -e 'print "test\n"'
test
cd@laptop:~/data/tmp$



За это отвечает опция -e после которой идет код для выполнения.

Однострочные скрипты, после кода, могут принимать параметром файл, а также, можно заставить выполнять код указанный -e для каждой строки.

Пример:

cd@laptop:~/data/tmp$ perl -ne "print ;" xx.xx
1
2
hello
4
5


Т.е. в данном случае мы попросили перл, выполнить распечатку строки по умолчанию, а т.к. была указана опция -n то это команда применялась к каждой строке файла xx.xx, т.е. получили cat

Также существует параметр -p, который после выполнения кода распечатывает строку. Т.е. логично что команда вида


perl -ne "dosmth; print $_;" file


будет аналогична

perl -pe "dosmth" file


Ну а теперь завершая. Мы добились того, что можем выполнять любые операции над каждой строкой в файле, и печатать это в stdout, однако первоначальная постановка задачи, звучала как замена строк в файле на лету. Именно для этого и существует опция -i

т.е.


perl -i -pe "dosmth" file

выполнит код dosmth для отдельно взятой строки и заменит ее в указанном файле.

опция -i может принимать необязательный параметр, и тогда, скрипт перед началом работы сделает backup копию файла

Пример.


cd@laptop:~/data/tmp/1$ perl -ibak -ne 'print;' 1.txt
cd@laptop:~/data/tmp/1$ ls
1.txt 1.txtbak
cd@laptop:~/data/tmp/1$ cat 1.txt
1
2
3
cdlaptop:~/data/tmp/1$ cat 1.txtbak
1
2
3
cd@laptop:~/data/tmp/1$


Теперь само решение должно быть понятно, а также понятно почему я привел два варианта.


Upd:



Появилась простая задачка, в одном из конфигов нужно увеличивать на единицу одно из значений, пусть условно это будет номер билда.




Возьмем пример файла конфига



cd:$ cat config
Key1 Value2
build=projectname.005
one more line



Допустим нам нужно получить такой же конфиг но с build=projectnam.006



perl -i -pe 's/(projectname\.)(\d+)/sprintf("%s%03d", $1, $2+1)/e' config

Пояснения.

-i -pe объяснялось выше.

Что изменилось? В конце регулярного выражения добавился флаг e, который означает что правая часть выражения будет выполняться как код perl и только после выполнения результат подставится для замены.




Можно было бы конечно сделать что то вроде


perl -pe 's/(projectname\.)(\d+)/$1 . ($2+1)/e' config
Key1 Value2
build=projectname.6
one more line

! опция -i была убрана, я хотел чтобы файл не менялся, а результат был в output



Как видим пропадают два ведущих нуля, именно поэтому была использована функция sprintf

 
Каталог сайтов, Добавить сайт