<?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>JL&#039;s blog &#187; LWT</title>
	<atom:link href="http://john.5070.info/tag/lwt/feed/" rel="self" type="application/rss+xml" />
	<link>http://john.5070.info</link>
	<description>:-)</description>
	<lastBuildDate>Sun, 06 Dec 2009 17:11:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Функционалы против классов</title>
		<link>http://john.5070.info/2009/10/%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%be%d0%bd%d0%b0%d0%bb%d1%8b-%d0%bf%d1%80%d0%be%d1%82%d0%b8%d0%b2-%d0%ba%d0%bb%d0%b0%d1%81%d1%81%d0%be%d0%b2/</link>
		<comments>http://john.5070.info/2009/10/%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%be%d0%bd%d0%b0%d0%bb%d1%8b-%d0%bf%d1%80%d0%be%d1%82%d0%b8%d0%b2-%d0%ba%d0%bb%d0%b0%d1%81%d1%81%d0%be%d0%b2/#comments</comments>
		<pubDate>Sun, 18 Oct 2009 16:57:30 +0000</pubDate>
		<dc:creator>John Lepikhin</dc:creator>
				<category><![CDATA[Ocaml]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[LWT]]></category>
		<category><![CDATA[функтор]]></category>

		<guid isPermaLink="false">http://john.5070.info/?p=203</guid>
		<description><![CDATA[Товарищ (RedChrom) задал вопрос, что я использую больше при разработке на Окамле. Не особо задумываясь ответил, что фифти-фифти. Потом сделал простой grep на свои исходники, и выяснил, что на 80% всё-таки модули и функторы. Причём, объекты и классы по большей части в очень старых исходниках. Сейчас 100% функторы.
Сейчас развлекаюсь написанием универсального интерфейса к разным хранилищам [...]]]></description>
			<content:encoded><![CDATA[<p>Товарищ (RedChrom) задал вопрос, что я использую больше при разработке на Окамле. Не особо задумываясь ответил, что фифти-фифти. Потом сделал простой grep на свои исходники, и выяснил, что на 80% всё-таки модули и функторы. Причём, объекты и классы по большей части в очень старых исходниках. Сейчас 100% функторы.</p>
<p><span id="more-203"></span>Сейчас развлекаюсь написанием универсального интерфейса к разным хранилищам (в первую очередь, это FS, memcached, memcachedb) с записями вида key+value. В качестве основы, создал универсальный интерфейс:</p>

<div class="wp_syntax"><div class="code"><pre class="ocaml" style="font-family:monospace;"><span style="color: #06c; font-weight: bold;">module</span> <span style="color: #06c; font-weight: bold;">type</span> STORAGE <span style="color: #a52a2a;">=</span>
        <span style="color: #06c; font-weight: bold;">sig</span>
                <span style="color: #06c; font-weight: bold;">type</span> t
                <span style="color: #06c; font-weight: bold;">type</span> storage_descr_t
&nbsp;
                <span style="color: #06c; font-weight: bold;">val</span> init<span style="color: #a52a2a;">:</span> storage_descr_t <span style="color: #a52a2a;">-&gt;</span> t init_result
&nbsp;
                <span style="color: #06c; font-weight: bold;">val</span> get<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> <span style="color: #a52a2a;">-&gt;</span> get_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span>
                <span style="color: #06c; font-weight: bold;">val</span> get_list<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> list <span style="color: #a52a2a;">-&gt;</span> get_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> list
&nbsp;
                <span style="color: #06c; font-weight: bold;">val</span> delete<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> <span style="color: #a52a2a;">-&gt;</span> delete_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span>
&nbsp;
                <span style="color: #06c; font-weight: bold;">val</span> set<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> <span style="color: #a52a2a;">-&gt;</span> ?value_size<span style="color: #a52a2a;">:</span>value_size <span style="color: #a52a2a;">-&gt;</span> value <span style="color: #a52a2a;">-&gt;</span> set_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span>
                <span style="color: #06c; font-weight: bold;">val</span> add<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> <span style="color: #a52a2a;">-&gt;</span> ?value_size<span style="color: #a52a2a;">:</span>value_size <span style="color: #a52a2a;">-&gt;</span> value <span style="color: #a52a2a;">-&gt;</span> add_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span>
                <span style="color: #06c; font-weight: bold;">val</span> replace<span style="color: #a52a2a;">:</span> t <span style="color: #a52a2a;">-&gt;</span> Key<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span> <span style="color: #a52a2a;">-&gt;</span> ?value_size<span style="color: #a52a2a;">:</span>value_size <span style="color: #a52a2a;">-&gt;</span> value <span style="color: #a52a2a;">-&gt;</span> replace_result Lwt<span style="color: #a52a2a;">.</span><span style="color: #060;">t</span>
        <span style="color: #06c; font-weight: bold;">end</span></pre></div></div>

<p>Написал модуль FileSystem, реализующий такой интерфейс. Поскольку файлы могут быть (условно) бесконечного размера, это стало самой простой реализацией интерфейса. Но не всё в жизни так легко. Например, memcached имеет максимальный размер пары key+value где-то в районе 64KB. А хочется в нём хранить побольше. Да и файловые системы бывают дурные. Напрмер, не позволяют создавать файлы больше 512MB. Поэтому на основе FileSystem (ключевое слово import) был написан маленький модуль FileSystemSized, который помимо value пишет в файлик размер value. Штука, на первый взгляд, бесполезная. Но идём дальше. Создаём функтор Splitted, который принимает модуль с интерфейсом STORAGE и делает модули с тем же уже знакомым нам интерфейсом STORAGE. Этот функтор хитрый: он знает, какого максимум размера value может хранить переданный ему модуль. Если в одну запись не влазит — он бьёт value на кусочки, для каждого кусочка генерит свой уникальный ключ, и таким образом пишет. В первом кусочке он сохраняет реальный размер всего value. Вот зачем нам нужен был модуль FileSystemSized. На основе этого функтора получается чудесный модуль FileSystemSplitted. По образу и подобию можно сделать MemcachedSplitted. Или даже FileSystemSplitted_and_again_Splitted, только я не придумал зачем :)</p>
<p>Мыслим вперёд. Ключ у нас — строчка строгой длины, с равной вероятностью имеющая в первом/втором/третьем/&#8230; байте любое из значений 0..255. На основе первого байта ключа, мы можем раскидать пары key+value на 256 физических хранилищ (ну, или меньше, как захочется). Назовём такой функтор Distributed, и он тоже будет создавать модули с интерфейсом STORAGE. Получается изумительная картинка:</p>

<div class="wp_syntax"><div class="code"><pre class="ocaml" style="font-family:monospace;"><span style="color: #5d478b; font-style: italic;">(* Создаём уже знакомый нам модуль для хранения больших значений
   в маленьких файлах.
*)</span>
<span style="color: #06c; font-weight: bold;">module</span> FileSystemSplitted <span style="color: #a52a2a;">=</span> Splitted<span style="color: #6c6;">&#40;</span>FileSystemSized<span style="color: #6c6;">&#41;</span>
&nbsp;
<span style="color: #5d478b; font-style: italic;">(* Каждое значение хранится в одном файлике, но файлики раскидываются
   по разным директориям.
*)</span>
<span style="color: #06c; font-weight: bold;">module</span> FileSystemDistributed <span style="color: #a52a2a;">=</span> Distributed<span style="color: #6c6;">&#40;</span>FileSystemSized<span style="color: #6c6;">&#41;</span>
&nbsp;
<span style="color: #5d478b; font-style: italic;">(* Каждое значение пилится на части, части раскидываются по разным директориям.
   С remote mount, получаем хранилище бесконечного размера для почти
   бесконечного количества значений.
*)</span>
<span style="color: #06c; font-weight: bold;">module</span> FileSystemDistributedSplitted <span style="color: #a52a2a;">=</span> Splitted<span style="color: #6c6;">&#40;</span>FileSystemDistributed<span style="color: #6c6;">&#41;</span>
&nbsp;
<span style="color: #5d478b; font-style: italic;">(* Каждое значение хранится в одной директории, но там оно пилится на части.
   Директорий для хранения может быть несколько. Хранилище получается тоже
   бесконечным, но наполняется не так равномерно.
 *)</span>
<span style="color: #06c; font-weight: bold;">module</span> FileSystemSplittedDistributed <span style="color: #a52a2a;">=</span> Distributed<span style="color: #6c6;">&#40;</span>FileSystemSplitted<span style="color: #6c6;">&#41;</span></pre></div></div>

<p>Как видите, с помощью фактически одного интерфейса и двух функторов, получаем невероятное, лютое количество вариантов хранилища. Даже круче, чем в GlusterFS или GNU Hurd. А ведь мысль можно продолжить ещё дальше, дописав функторы Raid0 (писать значение в несколько хранилищ), Clustered (создаётся на основе списка хранилищ, умеющих только читать и списка хранилищ, умеющих изменять), Flock (за счёт некоторого performace degradation, делать exclusive lock на записи)&#8230; чем я и занимаюсь :) Замечу, что всё это в функционально чисто, без side-effects (они есть только на уровне хранилища) и безумно типобезопасно.</p>
]]></content:encoded>
			<wfw:commentRss>http://john.5070.info/2009/10/%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%be%d0%bd%d0%b0%d0%bb%d1%8b-%d0%bf%d1%80%d0%be%d1%82%d0%b8%d0%b2-%d0%ba%d0%bb%d0%b0%d1%81%d1%81%d0%be%d0%b2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>oProxy release</title>
		<link>http://john.5070.info/2009/05/oproxy-release/</link>
		<comments>http://john.5070.info/2009/05/oproxy-release/#comments</comments>
		<pubDate>Wed, 20 May 2009 13:31:28 +0000</pubDate>
		<dc:creator>John Lepikhin</dc:creator>
				<category><![CDATA[oProxy]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[cluster]]></category>
		<category><![CDATA[LWT]]></category>
		<category><![CDATA[Ocaml]]></category>
		<category><![CDATA[хостинг]]></category>

		<guid isPermaLink="false">http://john.5070.info/?p=106</guid>
		<description><![CDATA[Не прошло и полгода, как я решил разродиться на релиз прокси. Назовём его &#8220;1.1&#8243;.
Самой большой моей ошибкой в проектировании была идея использовать POSIX треды. Всё замечательно, но только до тех пор, пока активных соединений не становится больше сотни. Дело в том, что такие треды, по сути, являются почти что отдельными процессами: у них свой стек, [...]]]></description>
			<content:encoded><![CDATA[<p>Не прошло и полгода, как я решил разродиться на релиз прокси. Назовём его &#8220;1.1&#8243;.</p>
<p><span id="more-106"></span>Самой большой моей ошибкой в проектировании была идея использовать POSIX треды. Всё замечательно, но только до тех пор, пока активных соединений не становится больше сотни. Дело в том, что такие треды, по сути, являются почти что отдельными процессами: у них свой стек, их обрабатывает шедулер ядра и т.д. Единственное отличие — у них в пределах одного процесса единое адресное пространство. Так вот, всё это вызывало такие проблемы, как переполнение стека, нелинейность масштабирования и пр. В качестве альтернатиы рассматривалось два варианта:</p>
<ol>
<li>Библиотека Equeue. Знаменита тем, что предоставляет низкий уровень к управлению очередями обработки дескрипторов. Это и минус — работать с ней не слишком комфортно: &#8220;есть только очередь и события, всё остальное на ваше усмотрение&#8221;.</li>
<li>Библиотека Lwt. По сути, то же самое, но выполнено совсем, совсем с другого бока. В интерфейсе предоставляется модель монад и псевдопотоков (отсюда название: LightWeight Threads). Быдлокодеру, навроде меня, достаточно лишь описать зависимости между состояниями: что делать, когда я получу данные из вон того сокета, и куда их записать потом.</li>
</ol>
<p>Выбор пал на Lwt. А вот тут следует сказать несколько восторженных слов о типизации Хиндли — Милнера, используемой, в том числе, в Ocaml. Как я уже сказал, потоки POSIX при разработке выглядят практически как отдельные процессы. Создал поток, и забыл о нём, а он варится сам по себе. В Lwt же надо отдельно позаботиться о последствиях каждой операции IO. Прежде всего, я заменил все вызовы Unix.socket (создание сокета) — благо в коде почти все они были в моей обёртке и реально вызов встречался только в 2-3 местах — на Lwt_unix.socket. И понеслось. Типовычислялка компилятора сразу указала мне, где я, дурень, пытаюсь подсунуть в нормальную сокетную функцию параметр с каким-то странным типом Lwt_unix.file_descr. Следующая неделя прошла за чрезвычайно интеллектуальной работой. Компилятор говорил мне, что нужно сделать чтобы впасть в нирвану, а я всячески пытался её достичь. Таким образом, буквально через несколько дней негритянской работы, передо мной предстал код, который мало того, что не содержал ни одного вызова к стандартным функциям IO, так ещё и компилился.</p>
<p>Каково же было моё удивление, когда с первым запуском оно ещё и заработало! После недели тупой работы, перехода с философии &#8220;одно соединение == один самостоятельный псевдопроцесс&#8221; на &#8220;одно соединение == один конечный автомат&#8221;, после исправления тучи кода <em>исключительно</em> по указаниям компилятора! Кривенько, но заработало, результат я увидел. Последующая неделя ушла на исправление совсем новых багов, багов ещё со старой версии и дописывания новых приятных мелочей.</p>
<p>Итого, имеем. Кластер с определённым количеством клиентов (пока меньше, чем на одной машине в среднем, но уже существенно). Прокся заведует как балансировкой нагрузки между HTTP-узлами, так и между MySQL. Т.е. гоняются вполне реальные данные настоящих сайтов. На одно <strong>соединение</strong> (не запрос) HTTP в среднем уходит чуть меньше миллисекунды CPU (это и проксирование динамики, и отдача статики с помощью системного sendfile). На соединение MySQL уходит несколько больше — около 5 миллисекунд, хотя алгоритм там наоборот в целом проще. Связываю это с</p>
<ul>
<li>sendfile() там не применишь, все проксируемые данные копируются в userspace</li>
<li> Имею богатый опыт разглядывания скриптов клиентов. А это совсем другая, удивительная, не поддающаяся научному изучению культура. У них принято сказать SELECT * на большую табличку, затем сложным алгоритмом с циклами тройной вложенности вычленить из многомегабайтного результата 2 строки, затем их отправить обратно в мускуль с целью join&#8217;а в непристойной позе с другой табличкой.</li>
</ul>
<p>Памяти жрёт в рабочем режиме при текущих нагрузках стабильно 12-15MB (один мастер, 4 рабочих).</p>
<p>Планы (или мечты?) на будущее:</p>
<ul>
<li>Пропатчить Lwt на предмет использования epoll() вместо select(). Чтобы в будущем не было мучительно больно обрабатывать 10000 одновременных соединений. Если, конечно, до этого дойдёт.</li>
<li>Написать некую глюкалу, которая сильно поможет в поиске причин высоких нагрузок как на сайтах, так и в БД. Пока мне это видится чем-то вроде: oproxyreport &#8220;select response_time-start_time, uri from http where host=&#8217;ispsystem.com&#8217; and uri begins with &#8216;/ru/&#8217; save to &#8216;/tmp/load.log&#8217;&#8221;. Если будет реализовано — сразу можно будет написать интерфейсик в ISPmanager, чтобы клиенты могли сами это делать.</li>
</ul>
<p>Я всё сказал.</p>
<p>p.s. Нет, не всё :) С переходом на Lwt, обнаружилась ещё одна приятная особенность: портабельность. Например, во FreeBSD 7.1. поломали те самые POSIX треды, из-за чего мне пришлось отказаться от альфа-тестирования прокси на ней в своё время. Lwt же ни от чего не зависит. Select() есть везде, а не блокирующие сокеты криво реализованы разве что в винде.</p>
]]></content:encoded>
			<wfw:commentRss>http://john.5070.info/2009/05/oproxy-release/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
