<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Записки искателей &#187; Development</title>
	<atom:link href="http://voituk.kiev.ua/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://voituk.kiev.ua</link>
	<description>while ( isAlive() ) {doCode(); doFun();}</description>
	<lastBuildDate>Wed, 02 Mar 2011 10:38:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>JSON Lint tool using Node.js</title>
		<link>http://voituk.kiev.ua/2011/02/08/json-lint-tool-using-node-js/</link>
		<comments>http://voituk.kiev.ua/2011/02/08/json-lint-tool-using-node-js/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 20:19:02 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[JavaScript/Ajax]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=2423</guid>
		<description><![CDATA[История одной занозы в &#8230; или небольшая заметка о том, как быстро написать что-то полезное на node.js В последнее время много и часто приходится работать с HTTP-REST-JSON  API, в результате чего назрела необходимость в нормальном client tool для этого API. И если на роль полноценного HTTP-REST-клиента претендовать не приходится (первенство тут отдаем старому доброму консольному curl), [...]]]></description>
			<content:encoded><![CDATA[<p><em>История одной занозы в &#8230; или небольшая заметка о том, как быстро написать что-то полезное на node.js</em></p>
<p>В последнее время много и часто приходится работать с HTTP-REST-JSON  API, в результате чего назрела необходимость в нормальном client tool для этого API. И если на роль полноценного HTTP-REST-клиента претендовать не приходится (первенство тут отдаем старому доброму консольному <a href="http://curl.haxx.se/">curl</a>), то вот с &#8220;красивым&#8221; форматированием результата вызова API &#8211; приходилось повозиться.<br />
Порою доходило до того, что ввиду сложности структуры результата, приходилось запускать developers console <a href="http://webkit.org/">WebKit</a>-a, копировать в буфер обмена строку-результат запроса, и выполнять  что-то вроде:</p>
<pre><code lang="js">console.log(/*...long...long...long...string...goes...here..*/)</code></pre>
<p>Во процессе борьбы с таким неудобством, в какой-то момент в недрах сети была найдена утилита jsonlint, которая умела структурировано выводить на экран JSON-обьект (такой себе аналог <a href="http://xmlsoft.org/xmllint.html">xmllint</a>), но и с ней не сложилось &#8211; какие-то ее зависимости категорично отказывались устанавливаться посредством  MacPorts.</p>
<p>Вконце концов было принято решение набросать аналогичный tool самостоятельно.<br />
Среди кандидатов на язык разработки подобной мелочи были Groovy, <a href="http://code.google.com/p/groovypptest/">Groovy++</a>, php и Node.JS).<br />
Ввиду JavaScript-nature поставленной задачи грех было не воспользоваться Node.JS – и выбор оправдал мои ожидания.<br />
Всего 30 минут &#8220;курения&#8221; node.js Manual &amp; Documentation&#8221;, 10 строк кода, и нужнейшая утилита &#8220;в кармане&#8221;.</p>
<p>Исходный код приводить тут не буду – его можно <a href="https://github.com/voituk/Misc/blob/master/nodejs/jsonlint">поглядеть на github</a>, покажу лишь несколько примеров использования полученной мини-утилиты на примере Twitter API (команды вводить unix-консоли):</p>
<p>1. Информации о Twitter-аккаунте конкретного пользователя:<br />
<code>curl -s "http://api.twitter.com/1/users/show/voituk.json" | jsonlint  | less</code></p>
<p>2. Поиск в Twitter:<br />
<code>curl -s "http://search.twitter.com/search.json?q=@voituk" | jsonlint  | less</code></p>
<p>Установить или обновить такую полезную &#8220;тулзу&#8221; можно простой консольной командой:<br />
<code>curl https://github.com/voituk/Misc/raw/master/nodejs/jsonlint &gt; ~/bin/jsonlint &amp;&amp; chmod a+x !#:3</code><br />
<em>(как это сделать на Windows &#8211; не спрашивайте ибо я даже не представляю как установить на Windows сам Node.JS)</em></p>
<p>Вот так вот неожиданно все получилось легко и просто посредством Node.JS.<br />
Критика и пожелания приветствуются в коментариях. </p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2011/02/08/json-lint-tool-using-node-js/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>MemcacheDB Quick Benchmarks</title>
		<link>http://voituk.kiev.ua/2010/03/03/memcachedb-quick-benchmarks/</link>
		<comments>http://voituk.kiev.ua/2010/03/03/memcachedb-quick-benchmarks/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 16:43:20 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1923</guid>
		<description><![CDATA[Эта заметка увидела свет в результате небольшого исследования, проведенного мною в процессе работы на оптимизацией проекта www.savevid.com и призвана поделиться моим скромным опытом миграции на key-value базы дынных (БД) . Предистория состоит в том, что в процессе естественной эволюции кода и оптимизации запросов к MySQL RDBMS,  работа с &#8220;центральной&#8221; таблицей всей схемы БД (как несложно [...]]]></description>
			<content:encoded><![CDATA[<p>Эта заметка увидела свет в результате небольшого исследования, проведенного мною в процессе работы на оптимизацией проекта <a href="http://www.savevid.com/">www.savevid.com</a> и призвана поделиться моим скромным опытом миграции на key-value базы дынных (БД) .<span id="more-1923"></span></p>
<p>Предистория состоит в том, что в процессе естественной эволюции кода и оптимизации запросов к MySQL RDBMS,  работа с &#8220;центральной&#8221; таблицей всей схемы БД (как несложно догадаться &#8211; это таблица videos), свелась к простому алгоритму:</p>
<ol>
<li>выбрать сложным запросом из разных таблиц все video_id, удовлетворяющие нужным критериям</li>
<li>по полученному списку video_id, выбрать информацию о видео (aka SELECT &#8230; FROM videos WHERE id IN (&#8230;))</li>
</ol>
<p>Причиной появления подобного use-case стал внушительный размер той самой videos.  А о катастрофичности любых JOIN-ов на большие (10Gb+) InnoDB-таблицы уже было написано немало. Хотя бы на том же <a href="http://www.mysqlperformanceblog.com/">MysqlPerfomanceBlog.com</a>.</p>
<p>Следовательно, если у нас есть выборки из таблицы ТОЛЬКО по primary key – то это первый кандидат на миграцию в key-value RDBMS.  Что мы, собственно, и пытались опробовать.</p>
<p>Среди массы кандидатов (а их оказалось действительно много), для первоначальных проб выбрали MemcacheDB.<br />
Причины весьма прозаичны, и были надиктованы &#8220;путем наименьшего сопротивления&#8221;:</p>
<ul>
<li>В проекте уже активно используется Memcached, клиент которого уже 100% совместим с MemcacheDB.<br />
Следовательно никаких сторонних библиотек &#8220;тянуть&#8221; в проект не нужно</li>
<li>Реализации клиентов для Memcached/MemcacheDB есть почти под все мыслимые и немыслимые платформы и языки программирования. Тут конечно огромное спасибо Бреду Фицпатрику и его Danga.com.</li>
<li>Memcached клиенты предоставляют базовый развномерный (хоть документация  и говорит о weight-based распределении) шардинг. В результате и MemcacheDB это перенял.</li>
<li>В основе backend-a MemcachedDB лежит BerkleyDB &#8211; одна из старейших и надежнейших key-value RDBMS, которая до сих под активно поддерживается Oracle Corp.</li>
<li>BerkleyDB поддерживает репликацию. И что самое приятное &#8211; как синхронную, так полу-синхронную и асинхронную.</li>
<li>Memcached-клиенты поддерживают сжатие данных, что является немаловажым фактором при использовании дорогостоящих SSD-винчестеров.</li>
</ul>
<p>В результате всего вышесказанного был проведен небольшой performance benchmark test.</p>
<p><strong>Железо:</strong></p>
<p>Использовался тестовый сервер 2 x Core2Quad / 16GB of RAM / 4 x 146Gb SAS HDD in RAID10<br />
MySQL: v5.4.2 , InnoDB, innodb_buffer_pool=6Gb, по максимуму оптимизирован для интерсивной записи.<br />
MemcaheDB: 1Gb of RAM, index type &#8211; hash, skip transaction support</p>
<p><strong>Производительность записи</strong><strong>:</strong></p>
<p>Создание 1 000 000  записей.<br />
MySQL: среднее время записи – 5.6 sec / 10k  (5.6 секунд на 10 000 записей)<br />
MemcacheDB: среднее время записи – 4.1 sec / 10k<br />
При включенном transaction support в MemcacheDB – около 7 sec/ 10k</p>
<p>Как видно MySQL не сильно отстает на записи, но при этом предоставляет полноценный ACID.</p>
<p><strong>Производительность чтения:</strong></p>
<p>Из ранее созданного миллиона записей случайным образом выбиралась одна запись по primary key.<br />
MySQL: среднее время чтения – 1.7 sec / 10K<br />
MemcacheDB: среднее время чтения – 0.7 sec / 10K</p>
<p>Т.е. в данном случае MemcacheDB более чем в 2 раза опережает MySQL.<br />
Думаю если заняться тюннингом первого, то можно этот разрыв увеличить  куда существеннее.</p>
<p><strong>Размер данных:</strong></p>
<p>Соотношение размеров файлов с данными (и индексами) получилось 1.5Gb  (MemcacheDB) против 2.3Gb (MySQL), т.е.  в 1.5 раза в пользу MemcacheDB.<br />
Сравнение проводилось без использования сжатия в MemcacheDB и без использования MySQL ROW_FORMAT=COMPRESSED.</p>
<p>Вот такой вот небольшой sidenote вышел. В результате, пока еще мне не совсем понятно нужно ли оно мне или нет.<br />
Если все-таки решеним испольовать MemcacheDB в production environment -  обязательно отпишусь о результатах.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2010/03/03/memcachedb-quick-benchmarks/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>MySQL: Slave could not parse relay log event entry</title>
		<link>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/</link>
		<comments>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 08:09:31 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1079</guid>
		<description><![CDATA[При использованиии MySQL Master-Slave (или же Master-Master) репликации бывает ситуация что по разным причинам версия слейва ниже чем версия мастера. Если при этом используется опция binlog_format=MIXED, в результате возможна ситуация когда мастер запишет в свой binlog запись вида: BINLOG 'XMawShMEAAAARwAAAEp5UwUAAGkAAAAAAAAACHNhblahblahblah' Которую слейв не сможет выполнить, ибо он ее попросту &#8220;еще не умеет&#8221;. Первой ласточкой того, [...]]]></description>
			<content:encoded><![CDATA[<p>При использованиии MySQL Master-Slave (или же Master-Master) репликации бывает ситуация что по разным причинам версия слейва ниже чем версия мастера.<br />
Если при этом используется опция <em>binlog_format=MIXED</em>, в результате возможна ситуация когда мастер запишет в свой binlog запись вида:<br />
<code>BINLOG 'XMawShMEAAAARwAAAEp5UwUAAGkAAAAAAAAACHNhblahblahblah'</code><br />
Которую слейв не сможет выполнить, ибо он ее попросту &#8220;еще не умеет&#8221;.</p>
<p>Первой ласточкой того, что это произошло станет сообщение &#8220;SLAVE DOWN&#8221; от мониторинговой системы и запись в MySQL-логе слейва сродни этой:</p>
<p><code>090917 14:55:51 [ERROR] Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: 71, event_type: 19<br />
090917 14:55:51 [ERROR] Error reading relay log event: slave SQL thread aborted because of I/O error<br />
090917 14:55:51 [ERROR] Slave: Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted (you can check this by running 'mysqlbinlog' o<br />
n the binary log), the slave's relay log is corrupted (you can check this by running 'mysqlbinlog' on the relay log), a network problem, or a bug in the master's or slave's MySQL<br />
code. If you want to check the master's binary log or slave's relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' on this slave. Error_code: 0<br />
090917 14:55:51 [ERROR] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'bin-us1.000022' position 17007149<br />
</code></p>
<p>Попытки пропустить неугодные записи старым добрым методом:<br />
<code>slave stop;<br />
set global sql_slave_skip_counter=1;<br />
slave start;</code><br />
в данном случае не помогают.</p>
<p>Как вариант решения предлагаю:</p>
<ol>
<li>Останавливаем slave: <em>&#8220;SLAVE STOP;&#8221;</em></li>
<li>&#8220;Выуживаем&#8221; из текста ошибки имя бинлога и позицию, на которой произошла ошибка.<br />
В данном случае это <em>bin-us1.000022</em> и позиция <em>17007149</em></li>
<li>На мастере выполняем запрос <em>&#8220;FLUSH LOGS;&#8221;</em></li>
<li>Делаем дамп бинлога начиная с найденной позиции:<br />
<code>mysqlbinlog --start-position=17007149 bin-us1.000022  &gt;  bin-us1-from17007149.sql</code></li>
<li>Загружаем полученный дамп на слейве с ключем &#8220;&#8211;force&#8221;<br />
<code>mysql --force -uuser -ppassword database &lt;  bin-us1-from17007149.sql</code></li>
<li>Выполняем на слейве &#8220;FLUSH LOGS;&#8221;</li>
<li>В data-директории находим relay-log с максимальным номером и размером около 100 байт (в моем случае это mysqld-relay-bin.000037)</li>
<li>В той же директории в файле relay-log.info<br />
первую строку заменяем на имя только что найденного relay-log-а, во вторую вписываем 0, в третью &#8211; следующий после &#8220;испорченного&#8221; бинлог, в четвертую &#8211; 0<br />
Получится что-то вроде:<br />
<code>/var/lib/mysql/mysqld-relay-bin.000037<br />
0<br />
bin-us1.000023<br />
0</code></li>
<li>Дальше на слейве выполняем:<br />
<code>CHANGE MASTER TO MASTER_LOG_FILE='bin-us1.000023', MASTER_LOG_POS=0;<br />
SLAVE START;</code></li>
<li>Ну и потом чтоб удостоверится что все &#8220;завелось&#8221;<br />
<code>SHOW SLAVE STATUS\G</code></li>
</ol>
<p>Должно работать.<br />
Подозреваю что пункты 4 и 5 можно обьеденить в один, более простой и элегантный.<br />
Но как это сделать &#8211; не гуглил.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/09/17/mysql-slave-could-not-parse-relay-log-event-entry/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL: Single row lock implementation</title>
		<link>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/</link>
		<comments>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/#comments</comments>
		<pubDate>Wed, 24 Jun 2009 14:01:35 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1066</guid>
		<description><![CDATA[При разработке приложений с использованием баз данных, часто возникает сценарий &#8220;insert if not exists&#8221;: if ( &#60;row exists in table&#62; ) then &#60;insert new record into table&#62; Из-за того, что данный блок псевдо-кода не выполяняется атомано, может возникнуть ситуация, когда между проверкой &#60;row exists in table&#62; и выполнением действия &#60;insert new record&#62; паралельный поток (клиент)  [...]]]></description>
			<content:encoded><![CDATA[<p>При разработке приложений с использованием баз данных, часто возникает сценарий &#8220;insert if not exists&#8221;: </p>
<pre><code class="sql">if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;</code></pre>
<p>Из-за того, что данный блок псевдо-кода не выполяняется атомано, может возникнуть ситуация, когда между проверкой <em>&lt;row exists in table&gt;</em> и выполнением действия <em>&lt;insert new record&gt;</em> паралельный поток (клиент)  выполнит добавление новой записи и в итоге в БД окажется 2 одинаковых записи (или произойдет ошибка &#8220;duplicate key&#8221;).</p>
<p>Такой, не самый приятный case, в книгах по паралельному программированию называют race condition и обходят путем создания синхронизирующих блокировок , использованием &#8220;выпрямителей&#8221; и тд.<br />
<span id="more-1066"></span><br />
С точки зрения MySQL надо бы переписать приведенный псевдокод в таком виде:</p>
<pre><code class="sql">mysql_query('LOCK TABLE `table` WRITE')

if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;

mysql_query('UNLOCK TABLES') </code></pre>
<p>В таком случае таблица будет заблокирована для записи и мы будем уверены что во время выполнения нашего кода, никто, кроме текущего потока, ничего в таблицу не &#8220;дозапишет&#8221; и race condition не возникнет.</p>
<p>Способ вроде простой, и если мне не изменяет память, даже рекомендовался в какой-то книге по разработке web-приложений.<br />
Самый же существенный его минус &#8211; он блокирует <strong>ВСЮ</strong> таблицу и все запросы на запись в эту таблицу будут ждать окончания выполнения блока кода и вызова &#8220;UNLOCK TABLES&#8221;.<br />
Это, в условиях интенсивной записи в таблицу, может создать весьма критичный bottleneck в производительности всей системы.</p>
<p>Очень неплохо бы в таком случае выполнять блокировку на уровне не всей таблицы, а только определенной строки. Немного покопавшись в мануале MySQL, отыскал несколько встроенных функций, которые помогают это реализовать:</p>
<dl>
<dt><a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock" rel="nofollow">GET_LOCK(str, timeout)</a></dt>
<dd>Создает глобальный мютекс с именем <em>str</em> и временем жизни <em>timeout</em>. При попытке другого потока &#8220;занять&#8221; уже созданный мютекс, он будет ожидать пока он не освободится вызовом RELEASE_LOCK() или не пройдет <em>timeout</em> секунд</dd>
<dt><a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock" rel="nofollow">RELEASE_LOCK(str)</a></dt>
<dd>Освобождает глобальный мютекс с именем <em>str</em></dd>
</dl>
<p>Кроме того есть еще 2 вспомогательные фунцкции для проверки создан ли уже мютекс, и если создан, то каким ProcessID: <a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_is-free-lock" rel="nofollow">IS_FREE_LOCK()</a> и <a href="http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_is-used-lock" rel="nofollow">IS_USED_LOCK()</a></p>
<p>С учетом вышесказанного описанный выше псевдокод, с использованием row-based блокировки будет выглядеть таким образом:</p>
<pre><code class="sql">mysql_query('DO GET_LOCK("my-prefix.my-row-id", 60)')

if ( &lt;row exists in table&gt; )
   then &lt;insert new record into table&gt;

mysql_query('DO RELEASE_LOCK("my-prefix.my-row-id")') </code></pre>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/06/24/mysql-single-row-lock-implementation/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Using iptables to prevent SSH brute force</title>
		<link>http://voituk.kiev.ua/2009/06/21/using-iptables-to-prevent-ssh-brute-force/</link>
		<comments>http://voituk.kiev.ua/2009/06/21/using-iptables-to-prevent-ssh-brute-force/#comments</comments>
		<pubDate>Sun, 21 Jun 2009 14:34:37 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1061</guid>
		<description><![CDATA[Себе на заметку, дабы потом опять не гуглить. Эти 2 простых iptables-правила спасают сервер от brute force перебора пароля от SSH: iptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set --name DEFAULT --rsourceiptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -m state --state [...]]]></description>
			<content:encoded><![CDATA[<p><em>Себе на заметку, дабы потом опять не гуглить.</em><br />
Эти 2 простых iptables-правила спасают сервер от brute force перебора пароля от SSH:<br />
<code>iptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set --name DEFAULT --rsource</code><code>iptables -I INPUT -i eth0 -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 180 --hitcount 5 --name DEFAULT --rsource -j DROP</code><br />
В результате те, кто делает более 5-ти попыток SSH-подключений за последние 3 минуты &#8211; блокируются на 3 минуты.<br />
Для того, чтоб эти правила запускались при старте системы делаем dump rules-файла:<br />
<code>iptables-save  > /etc/sysconfig/iptables</code></p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/06/21/using-iptables-to-prevent-ssh-brute-force/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Apache Tomcat init.d script</title>
		<link>http://voituk.kiev.ua/2009/04/28/apache-tomcat-initd-script/</link>
		<comments>http://voituk.kiev.ua/2009/04/28/apache-tomcat-initd-script/#comments</comments>
		<pubDate>Tue, 28 Apr 2009 13:33:18 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1022</guid>
		<description><![CDATA[Продолжая серию заметок &#8220;Из программиста в руководители админы&#8221; :), опять же себе на заметку пишем init-скрипт для корректного запуска/перезапуска Apache Tomcat под Linux. Создаем файл /etc/init.d/tomcat такого содержания: # Tomcat auto-start # # chkconfig: - 90 15 # description: Jakarta Tomcat Java Servlets and JSP server export JAVA_HOME=/usr/java/default export JRE_HOME=/usr/java/latest export CATALINA_HOME=/home/vadim/tomcat case $1 in [...]]]></description>
			<content:encoded><![CDATA[<p>Продолжая серию заметок &#8220;Из программиста в <span style="text-decoration: line-through;">руководители</span> админы&#8221; :), опять же себе на заметку пишем init-скрипт для корректного запуска/перезапуска Apache Tomcat под Linux.</p>
<p>Создаем файл <strong>/etc/init.d/tomcat</strong> такого содержания:</p>
<pre><code># Tomcat auto-start
#
# chkconfig: - 90 15
# description: Jakarta Tomcat Java Servlets and JSP server

export JAVA_HOME=/usr/java/default
export JRE_HOME=/usr/java/latest
export CATALINA_HOME=/home/vadim/tomcat

case $1 in
start)
  sh $CATALINA_HOME/bin/startup.sh
  ;;
stop)
  sh $CATALINA_HOME/bin/shutdown.sh
  ;;
restart)
  sh $CATALINA_HOME/bin/shutdown.sh
  sh $CATALINA_HOME/bin/startup.sh
  ;;
*)
  echo "Usage: $0  {start|stop|restart}"
  exit 1
  ;;
esac
exit 0</code></pre>
<p>После этого выполняем:<br />
<code>chmod +x /etc/init.d/tomcat<br />
 chkconfig tomcat on</code></p>
<p>Аналогичный <a href="http://voituk.kiev.ua/2009/02/09/nginx-initd-script/">init.d-скрипт для запуска nginx</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/04/28/apache-tomcat-initd-script/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Java + Groovy on Google App Engine</title>
		<link>http://voituk.kiev.ua/2009/04/08/java-groovy-on-google-app-engine/</link>
		<comments>http://voituk.kiev.ua/2009/04/08/java-groovy-on-google-app-engine/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 06:30:10 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=1000</guid>
		<description><![CDATA[Вот и свершилось то, чего так долго ждали и просили большевики прогрессивные ИТ-гики &#8211; Google добавляет поддержку Java (а вместе с ней и Groovy) в Google App Engine. Как сообщает в корпоративном блоге SpringSource Guillaume Laforge (project-manager проекта Groovy), последние несколько недель они работали совместно с командой Google App Engine над &#8220;правильным&#8221; запуском Groovy на [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" title="Google App Engine Groovy" src="http://voituk.kiev.ua/wp-content/uploads/google-app-engine-groovy.png" alt="" width="250" height="202" />Вот и свершилось то, чего так долго <a href="http://voituk.kiev.ua/2008/04/14/java-groovy-on-google-appspot/">ждали и просили</a> <span style="text-decoration: line-through;">большевики</span> прогрессивные ИТ-гики &#8211; Google <a href="http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html">добавляет поддержку Java</a> (а вместе с ней и Groovy) в Google App Engine.</p>
<p>Как сообщает в корпоративном блоге SpringSource Guillaume Laforge <em>(project-manager проекта Groovy)</em>, последние несколько недель они работали совместно с командой Google App Engine над &#8220;правильным&#8221; запуском Groovy на GAE.</p>
<p>Совмесная работа вылилась в набор дополнений в реализацию security-модели Groovy.</p>
<p>Небольшая <a href="http://blog.springsource.com/2009/04/07/write-your-google-app-engine-applications-in-groovy/">заметка о том, как запустить Groovy приложение в среде Google App Engine</a> от того же Guillaume Laforge.</p>
<p>P.S. Что-то мне подсказывает что теперь GAE перестанет быть игрушкой для питонистов, а станет упрощенной версией Amazon AWS, тем самым накорню убив остальные Java in Cloud &#8211; сервисы а-ля <a href="http://voituk.kiev.ua/2008/12/26/running-java-in-a-cloud/">Stax</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/04/08/java-groovy-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL: Archive Storage Engine</title>
		<link>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/</link>
		<comments>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/#comments</comments>
		<pubDate>Fri, 13 Mar 2009 18:56:24 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=974</guid>
		<description><![CDATA[Все очевидно, что любая современная информацонная система занимается хранением и анализом любого рода данных.  Аналитики утверждают что в среднем, обьем этих данных для среднестатистичекой компании растет на 42% в год. В результате проблема хранения этих данных постепенно приобретает бОльшую актуальность. Из своего опыта работы с достаточно большими массивами данных, хранимых в современных реляционных СУБД (по [...]]]></description>
			<content:encoded><![CDATA[<p>Все очевидно, что любая современная информацонная система занимается хранением и анализом любого рода данных.  Аналитики утверждают что в среднем, обьем этих данных для среднестатистичекой компании растет на 42% в год.<br />
В результате проблема хранения этих данных постепенно приобретает бОльшую актуальность.</p>
<p>Из своего опыта работы с достаточно большими массивами данных, хранимых в современных реляционных СУБД (по большей части в MySQL) могу сказать что не менее 50% данных в любой системе &#8211; это данные которые представляют собой только &#8220;историческую&#8221; ценность.<span id="more-974"></span></p>
<p>Чаще всего по ним просчитаны-пересчитына все возможные &#8220;срезы&#8221;, построены все отчеты и вероятность их использования в дальнейшем с каждым днем все больше приближается к нулю.<br />
Также часто бывает, что просто удалить подобные данные по разным причинам нельзя.</p>
<p>Будучи чуть более молодым и менее опытным, чем сейчас я делал так: разделял данные на 2 части (старые и новые), со старых делал дапм, архивировал и писал на CD, после чего смело удалял их из MySQL.<br />
Думаю не стоит обьяснять во что выливалась внезапная необходимость поднять старый массив данных для какого-то супер-нового отчета. :)</p>
<p>Вот тут очень кстати вспомнить за что я люблю MySQL &#8211; это его plugable storage engines, которые существуют под любую специфичную задачу.</p>
<p>Потому предлагаю кратко пробежаться по особенностям ARCHIVE storage engine, созданного для хранения &#8220;архивных&#8221; данных максимально эффективно:</p>
<ul>
<li>данные в archive-таблицах хранятся в сжатом библиотекой zlib виде</li>
<li>не поддерживает обновление и удаление даннных (UPDATE/REPLACE/DELETE) &#8211; только добавление (INSERT)</li>
<li>не поддерживает индексы</li>
<li>использует row-based локи</li>
<li>поддерживает strict sql_mode</li>
</ul>
<p>С появлением ARCHIVE  Storage Engine описанный выше use-case существенно преобразился: теперь старые данные не дампятся, а переносятся в &#8220;архивную&#8221; таблицу &#8211; полную копию существующей.<br />
В результате их хранение за долгий период не является очень накладным, а удобство использования и мгновенный доступ посредством SQL сохраняется.</p>
<p>В качестве эксперимента приведу результаты сегодняшнего &#8220;архивирования&#8221; описанным образом одной небольшой таблицы:<br />
<em>(по хорошему надо было взять таблицу побольше, но под рукой такой не оказалось)</em><br />
После разделения данных на &#8220;архивные&#8221; и &#8220;актуальные&#8221; и оптимизации таблицы &#8220;актулаьных&#8221; данных получил такое:</p>
<pre><code>+--------------------+--------+---------------+------------+
| table_name         | engine | total_size_mb | table_rows |
+--------------------+--------+---------------+------------+
| arch_downloads_log | MyISAM |         63.68 |      45801 |
| wap_downloads_log  | MyISAM |         18.67 |      13429 |
+--------------------+--------+---------------+------------+</code></pre>
<p>Итого 69тыс строк данных занимают почти 82Mb дискового пространства.<br />
После удаления индексов из arch_downloads_log и конвертирования в тип ARCHIVE получил:</p>
<pre><code>+--------------------+---------+---------------+------------+
| table_name         | engine  | total_size_mb | table_rows |
+--------------------+---------+---------------+------------+
| arch_downloads_log | ARCHIVE |          1.49 |      45801 |
| wap_downloads_log  | MyISAM  |         18.67 |      13429 |
+--------------------+---------+---------------+------------+</code></pre>
<p>Итого, таблица размером в 64Mb была сжата в 1.5Mb &#8211; тоесть в 42 раза.<br />
<em>(хм&#8230; как интересно совпало с 42%, о которых писалось вначале заметки)</em></p>
<p>Выводы? &#8211; Тут уж думайте сами, решайте сами надо ли и поможет ли оно вам.</p>
<p>Ссылки:</p>
<ul>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/archive-storage-engine.html">Документация об ARCHIVE storage engine от MySQL AB</a></li>
<li><a href="http://dev.mysql.com/tech-resources/articles/storage-engine.html">Обзор возможностей ARCHIVE storage engine</a> от Robin Schumacher</li>
</ul>
<p>P.S. Перечитал написанное  &#8211; какой-то банальщиной попахивает. Ну и ладно.</p>
<p>P.S.S. Ах, да! Те кто сейчас хочет подискутировать на тему &#8220;MySQL не предназначен для хранения больших обьемов данных&#8221; &#8211; предлагаю даже не начинать, ибо ответ будет единственно-правильный: &#8220;Просто вы не умеете его готовить&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/03/13/mysql-archive-storage-engine/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Project closed. Lessons learned.</title>
		<link>http://voituk.kiev.ua/2009/03/11/project-closed-lessons-learned/</link>
		<comments>http://voituk.kiev.ua/2009/03/11/project-closed-lessons-learned/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 09:26:03 +0000</pubDate>
		<dc:creator>juriy</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Just a life]]></category>
		<category><![CDATA[Работа]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=972</guid>
		<description><![CDATA[Вот и подошел к концу очередной абзац моего резюме. Проект, которым я руководил последние полтора года, закончен. Как минимум, закончена его разработка с нашей, аутсорсинговой стороны. Работая на этом проекте, я вынес для себя несколько важных уроков. Этот опыт я добывал сам: некоторые уроки дались легко, другие вылились в потерянное время и нервы. Чтобы как-то [...]]]></description>
			<content:encoded><![CDATA[<p>Вот и подошел к концу очередной абзац моего резюме. Проект, которым я руководил последние полтора года, закончен. Как минимум, закончена его разработка с нашей, аутсорсинговой стороны.</p>
<p>Работая на этом проекте, я вынес для себя несколько важных уроков. Этот опыт я добывал сам: некоторые уроки дались легко, другие вылились в потерянное время и нервы. Чтобы как-то прорезюмировать проект в целом: работа над ним была _нормальной_. Практически не было овертаймов, была хорошо налажена коммуникация с заказчиками и внутри компании все «рабочие моменты» решались на удивление быстро.<br />
<span id="more-972"></span><br />
<strong>Урок первый и самый важный.</strong><br />
Фидбек наше все. Лучше негативный отзыв, чем никакого отзыва. Если у заказчика не хватает времени посмотреть то, что вы сделали за пару недель &#8211; это терпимо. Месяц: это начинает дурно пахнуть, пара месяцев и вы на грани катастрофы. Просите, рассказывайте про важность отзывов и значимость мнения заказчика, организовывайте встречи специально для того, чтобы заказчик смотрел то, что вы сделали.<br />
Несмотря на всю парадоксальность ситуации, иногда на двухчасовой обзор итерации у бизнеса времени не хватает. Понять это можно: все люди, у всех бывают загруженные периоды, но вот допускать такого нельзя.<br />
Я считаю своей самой большой ошибкой за полтора года то, что я недостаточно настаивал на регулярных обзорах новой версии проекта, теша себя тем, что партнеры нам безгранично доверяют и верят в наш профессионализм. На самом деле, профессионализм и виденье проекта совершенно разные вещи: не повторяйте моей ошибки.<br />
В следующий раз, надо будет лечь поперек дороги, как толстый английский бульдог, и отказываться сдвинуться с места, пока не будет проведен нормальный обзор.</p>
<p><strong>Второй урок.</strong><br />
Ради спортивного интереса, этот будет из области позитивного опыта. Обычно такие заметки как эта, это заметки-страшилки в духе &#8220;как я наколол дров&#8221;. Поступлю иначе: разбавлю post mortem историями успеха.</p>
<p>Standup meeting в команде. Мало кто будет спорить с полезностью этого явления: каждый член команды рассказывает про то, что он успел сделать, какие у него планы и что мешает работать (да, да, это из Scrum). Я добавил в утренние встречи небольшую &#8220;зарядку для хвоста&#8221;: после встречи мы с командой рассматривали небольшую задачку на знание Java. Я выбирал задачки из Java Puzzlers иногда из своего опыта, иногда подсказывали друзья или коллеги. Задачки были простыми и покрывали один-два аспекта языка.<br />
Эффект был самым положительным: во-первых, встречи перестали быть уж слишком формальным &#8220;рапортованием о сделанной работе&#8221;. Во-вторых, задачки ненавязчиво стимулировали глубже познакомиться с предметом: я неоднократно замечал, как мои коллеги читали материалы, чтобы детальнее разобрать механизмы, которые затрагивала задачка. В-третьих, задачки настраивали на &#8220;рабочую волну&#8221;: после митинга было настроение поработать и закончить свою задачу, а не почитать еще немного &#8220;корреспондент&#8221;.</p>
<p><strong>Третий урок.</strong><br />
Тут даже не урок, а case-study. Кейс такой: человек регулярно заваливает интеграцию, забывает залить нужные файлы в репозиторий или заливает промежуточные варианты, вместо нужных. После того, как это обнаруживается, тратит некоторое время и все-таки чинит сборку. Проблема оказалась в том, что у сотрудника не был настроен плагин для работы с системой контроля версий.<br />
Мораль: если симптом повторяется, не стоит закрывать на него глаза, даже если его легко вылечить. Порой вылечить причину еще проще. Вторая мораль: не все разработчики одинаково легко озвучивают свои проблемы на утренних встречах, даже если проблема совсем мелкая (&#8220;ребят, у меня слетел плагин, помогите, пожалуйста, его настроить&#8221; &#8211; нежелание сказать эту простую фразу портило человеку жизнь несколько месяцев).<br />
Конечно, это раздолбайство разработчика. Но задача team-lead&#8217;а такие раздолбайства находить и пресекать.</p>
<p><strong>Четвертый урок.</strong><br />
И последний в рамках заметки. Иногда бывают периоды, когда задач нет. Совсем нет. Есть два варианта, как провести это время. Вариант номер один &#8211;  &#8220;ликвидация технического долга&#8221;: другими словами, переделать то, что раньше было сделано на скорую руку, покрыть тестами еще непокрытый блок кода, вобщем провести время с пользой для проекта. Вариант номер два: провести это время с пользой для себя. Нет, не солярий, ванна и маникюр, а самообразование.<br />
Как показала практика, лучший способ &#8220;самообразовываться&#8221; &#8211; ставить задачи по исследованию технологий и (!) разработке прототипных решений. Все согласятся что формулировка &#8220;ну, друзья, давайте что-нибудь почитаем, кому что интересно&#8221; нежизнеспособна. Такую задачу всерьез исполнять никто не станет. Даже если станет, то пользы для проекта не будет никакой.<br />
Вариант &#8220;читай вот это, или это&#8221; намного более жизнеспособен, но только при условии, что в результате работы будет выдан какой-нибудь артефакт: &#8220;доклад&#8221; для других членов команды (с тезисами в виде текста или презенташки) или работающий прототип решения. Лучше и то и другое. Во-первых, таким образом, есть конкретная цель и задача с конкретным критерием выполнения. Во-вторых, имея папочку с набором таких прототипов и презенташек довольно просто оформить материал в виде исследования на тему &#8220;какой API лучше использовать для решения нашей задачи&#8221;:  а это уже вполне реальная и ощутимая польза.</p>
<p>Вот, вобщем-то и все. Теперь осталось только выставить пивка коллегам да аккуратно сложить бумаги перед переездом на новый проект (а может, и на новое место работы). Удачи вам в ваших проектах.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/03/11/project-closed-lessons-learned/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>nginx init.d script</title>
		<link>http://voituk.kiev.ua/2009/02/09/nginx-initd-script/</link>
		<comments>http://voituk.kiev.ua/2009/02/09/nginx-initd-script/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 17:41:34 +0000</pubDate>
		<dc:creator>Vadim Voituk</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://voituk.kiev.ua/?p=962</guid>
		<description><![CDATA[Как-то так получилось что при установки nginx с исходников, в /etc/init.d/ не установился скрипт для автозапуска web-сервера. Потому исключительно себе на заметку (знаю точно &#8211; пригодится ещё) публикую то, что удалось найти в сети и допилить напильником до рабочего состояния. Скачать nginx init.d / rc.d скрипт.]]></description>
			<content:encoded><![CDATA[<p>Как-то так получилось что при установки nginx с исходников, в /etc/init.d/ не установился скрипт для автозапуска web-сервера.</p>
<p>Потому исключительно себе на заметку <em>(знаю точно &#8211; пригодится ещё)</em> публикую то, что удалось найти в сети и допилить напильником до рабочего состояния.</p>
<p><a href="http://voituk.kiev.ua/wp-content/uploads/nginx">Скачать nginx init.d / rc.d скрипт</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://voituk.kiev.ua/2009/02/09/nginx-initd-script/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

