Showing posts with label Work. Show all posts
Showing posts with label Work. Show all posts

Wednesday, April 29, 2015

How fast is your memchr

Recently, I got a simple task: calculate line counts in some text. The trick was to do it as quickly as possible. The naive approach was just to use some cycle on a text and count all characters whose code is '\n'. But of course this approach is not very clever.

The second approach was to take the standard memchr function from the C library. This is the most portable but not equally effective approach on different platforms.

The third approach was to treat input as a sequence of 32 or 64 bit integers and use 'XOR' operation with the repeated pattern. However, the trick was to use special magic bits to track if some of bytes in this word are zero after 'XOR'. The idea was taken from GNU C library, where it is used in memchr function.

The code that evaluates the performance of each method is placed here:
https://gist.github.com/vstakhov/99c04d6aa18e47c7950d

Currently, it uses linux specific timer to get time intervals, so if you want to test it on your OS then you need to change this timer's value appropriately.

Here are some results:

Linux/amd64 (gcc 4.8 -O3)

Naive: 5583 0.001655
Stupid xor: 5583 0.001553
Memchr: 5583 0.000229
Magic bits: 5583 0.000352

Here we can see that memchr in glibc is blazingly fast.

FreeBSD/amd64 (clang 3.4.1 -O3)

Naive: 5589 0.001343
Stupid xor: 5589 0.001431
Memchr: 5589 0.001323
Magic bits: 5589 0.000444

In FreeBSD, memchr is almost as slow as naive implementation.

Solaris/amd64 (gcc 4.4 -O2) - this is another hardware platform and test

Naive: 9013 0.872925
Stupid xor: 9013 0.812501
Memchr: 9013 0.889934
Magic bits: 9013 0.955014

-m64 -O2

Naive: 9013 1.138129
Stupid xor: 9013 0.982253
Memchr: 9013 0.741068
Magic bits: 9013 0.349103


As we can see, in Solaris, memchr is reasonably fast but is almost twice slower than bithack approach when used in 64 bits mode.

UPDATE: I've added some more cases to the evaluation, namely SSE2 and AVX2 versions using compiler intrinsics. Finally, I've evaluated the performance of algorithms on a larger file using my Mac laptop with Haswell CPU:

-m64 -mavx2 -O2

Naive: 4471200 0.179973
Stupid xor: 4471200 0.186705
Memchr: 4471200 0.083217
Memchr (musl): 4471200 0.164340
Magic bits: 4471200 0.122103
SSE: 4471200 0.073190
AVX2: 4471200 0.06790

So OSX libc is fast enough to beat the bithack version, so I presume it uses SSE for speed. Nonetheless, in 32 bits mode it sucks (but naive version comes even faster than libc one, because clang is smart enough to optimize it using SSE instructions):

-m32 -mavx2 -O2

Naive: 4471200 0.123135
Stupid xor: 4471200 0.109367
Memchr: 4471200 0.313270
Memchr (musl): 4471200 0.336193
Magic bits: 4471200 0.223416
SSE: 4471200 0.071051
AVX2: 4471200 0.070952

The performance of avx2 and sse2 versions is almost the same in this case. I've found that the version with vmovntdqa was slightly slower than a version with vpmaskmovd.

Monday, July 25, 2011

VirtualBox OSE и PXE

Чтобы в VirtualBox OSE работал pxeboot в guest системах, надо загрузить и установить в настройках расширение Oracle VM VirtualBox Extension Pack. Найти можно тут.

Wednesday, July 20, 2011

Автообучение rspamd

Стало совершенно очевидным, что автообучение, основанное на срабатывании правил, как это сделано в SA, - совершенно порочная практика, которая реально может привести к тому, что хорошие письма, но отправленные "не с тех" релеев или же имеющие некоторые спам сигнатуры (а таких честных писем достаточно много), будут статистикой еще больше давиться в сторону оценки как спам. Кроме того, это может засорять статистику неправильными срабатываниями. Для rspamd сейчас я продумываю концепцию, чтобы сделать автообучение максимально адаптивным.

Tuesday, May 25, 2010

Bitbucket.org

В связи с многочисленными проблемами работы с sourceforge, связанных, например, с невозможностью управления trac'ом, я решил, что лучше будет перенести публичный репозиторий на http://bitbucket.org. Это платформа для публикации кода, где все элементы помещаются в mercurial репозитории (например, wiki). Это делает очень удобным резервное копирование информации. Так что http://rspamd.sourceforge.net сейчас редиректит на bitbucket. Также я практически дописал основную документацию к rspamd, и теперь она доступна в том числе в wiki butbucket'а. Для конвертации из texinfo, который я использую для написания документации, в wiki формат (creole - http://www.wikicreole.org/) я написал небольшой скрипт на перле, который конвертирует основные элементы документации (лежит тут: http://cebka.pp.ru/stuff/info2wiki.pl). Разумеется, он конвертирует неполный набор тегов docbook и зачастую работает не совсем верно, но, насколько я знаю, это единственный способ конвертации, т.к. варианты texinfo->docbook->wiki также не реализованы нормально. Кроме этого, я, наконец, затегал 0.3.0. В планах к 0.3.1 написание smtp прокси для обработки спама на ранних стадиях, а также допиливание документации к lua API rspamd.

Tuesday, April 27, 2010

Rspamd и xml

Замучавшись бороться с lex+yacc решил перевести конфигурацию rspamd в xml формат. Минусы старой системы довольно прозаичны: lex при переключении внутренних состояний парсера (lex states) не умеет при yyrestart'е переключаться в INITIAL state, что приводит к невозможности перечитывания конфига "на лету". Кроме этого, сами по себе lex+yacc предоставляют слишком много возможностей для генерации грамматик, что само по себе неплохо, но я ловлю себя на мысле, что bind like конфиг-файл зачастую не очень очевиден для пользователя, яркий пример, когда переменные rspamd на самом деле являются не переменными в полном понимании этого слова, а подстановками текста. Также такой конфиг крайне сложно парсить чем-то, отличным от оригинальной lex/yacc грамматики. Моя же идея была в расширении интерфейса управления кластера rspamd, давая возможность конфигурации машин в кластере более-менее атоматически. Выбор лежал между ini-like форматом и xml (yaml и json тоже рассматривались, но никаких существенных преимуществ, кроме уменьшения размера конфига, я не нашел), но у ini нет понятия уровней вложенности, а это мне было нужно для описания файлов статистики внутри classifier'а. Конечным решением системы, которая бы предоставляла компромисс между удобством ручного написания сложных правил и возможностью настройки параметров автоматически (через web интерфейс или же shell script), я выбрал lua + xml. То есть, логика правил описывается в lua, используя все возможности этого языка, включая, например, переменные, являющиеся функциями, а включаются эти правила, а также назначаются веса, описываются рабочие процессы в xml. Такое решение, на мой взгляд, позволяет отделить код правил от собственно процесса настройки системы. Поддержку старого формата я оставил, и теперь rspamd умеет конвертировать старый формат в xml (конвертировать правила в lua он, к сожалению, не умеет, но умеет представлять их в виде xml). Сразу же видимый профит - возможность "мягкого" рестарта с перечитыванием конфига. В будущем планируется введение динамических правил, которые можно было бы загружать в кластер через контроллер, не выполняя рестарта. Также анализ правил, заимствованных из spamassassin'а показал, что все это лучше делать через отдельные статистические файлы, которые после обучения поставлять вместе с rspamd. Ну и напоследок, если у кого-то вдруг появилось желание заменить SA или другую систему спам фильтрации на rspamd, но в rspamd не хватает какой-то функциональности, то я был бы рад выслушать подобные замечания, равно как и другие идеи по развитию проекта.

Thursday, February 4, 2010

Небольшой обзор возможностей rspamd

Так как до сих пор у меня не появилось идей, как рассказать легко и понятно о том, зачем и как использовать rspamd, я написал краткий обзор rspamd: фичи, установка, настройка и обучение. Надеюсь, он будет полезен тем, кто хочет использовать rspamd или тем, кто даже не знает о его существовании. Обзор тут:  http://cebka.pp.ru/why-rspamd.html.

Monday, August 31, 2009

Windows resolver

Знаете, что сделает resolver винды, если его попросить отрезолвить url c base10 закодированным ip http://9715522259? Правильно, резолвер винды сможет даже 9 миллиардов превратить в ip адрес, тупо взяв младшие 32 бита от результата преобразования. Поэтому для парсинга url'ей, рассылаемых спамерами, которые содержат base10 encoded ip, надо преобразовывать его в uintmax_t, и брать младшие 32 бита.

Monday, April 13, 2009

Rspamd

Бета версия rspamd доступна для тестирования. Для сборки требуется cmake и gmime2.2. Сейчас rspamd работает примерно на порядок быстрее, чем spamassassin, но для окончательного релиза необходимо еще много тестирования. Буду признателен за любую информацию об использовании rspamd, а также о багах, в нем найденных. Rspamd доступен тут: http://cebka.pp.ru/trac

Friday, February 20, 2009

CMake + libperl

Начну с того, что встроенный модуль CMake никуда не годится:

SET(PERL_POSSIBLE_INCLUDE_PATHS
/usr/lib/perl/5.8.3/CORE
/usr/lib/perl/5.8.2/CORE
/usr/lib/perl/5.8.1/CORE
/usr/lib/perl/5.8.0/CORE
/usr/lib/perl/5.8/CORE
)

Естественно, пользоваться им нельзя. Кроме того, у перла до 5.8.9 есть очень гадкая особенность - DynaLoader.a, который есть в ldflags'ах. Если собирается приложение с -fPIC, то порядок линковки объектов важен, и DynaLoader имеет свойство ломать сборку. Я решил эту проблему копированием DynaLoader.a за угол с добавлением префикса lib. Выглядит это так:

# Find perl libraries and cflags
EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e ccopts OUTPUT_VARIABLE PERL_CFLAGS)
EXECUTE_PROCESS(COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e ldopts OUTPUT_VARIABLE PERL_LDFLAGS)
STRING(REGEX REPLACE "[\r\n]" " " PERL_CFLAGS ${PERL_CFLAGS})
STRING(REGEX REPLACE " +$" "" PERL_CFLAGS ${PERL_CFLAGS})
STRING(REGEX REPLACE "[\r\n]" " " PERL_LDFLAGS ${PERL_LDFLAGS})
STRING(REGEX REPLACE " +$" "" PERL_LDFLAGS ${PERL_LDFLAGS})
# Handle DynaLoader
STRING(REGEX MATCH "/[^ ]*/DynaLoader.a" PERL_DYNALOADER ${PERL_LDFLAGS})
STRING(REGEX REPLACE "/[^ ]*/DynaLoader.a " "" PERL_LDFLAGS ${PERL_LDFLAGS})

IF(PERL_DYNALOADER)
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy ${PERL_DYNALOADER} ${project_BINARY_DIR}/compat/libdynaloader.a)
LINK_DIRECTORIES(${rspamd_BINARY_DIR}/compat/)
ENDIF(PERL_DYNALOADER)

Этот метод работает как при наличии DynaLoader.a, так и при его отсутствии. Достаточно в настройках таргета добавить следующее:

IF(PERL_DYNALOADER)
TARGET_LINK_LIBRARIES(target dynaloader)
ENDIF(PERL_DYNALOADER)

Wednesday, February 18, 2009

Эффективная почтовая система на базе nginx+postfix+rmilter

Довольно часто возникает необходимость сделать высокоэффективную почтовую систему с возможностью масштабированияв плане увеличения mx'ов. Для этого можно воспользоваться такой связкой: на входе стоит nginx с policy, которая выполняет асинхронный резолвинг адресов и фильтрацию по rbl+regexp. На многих потоках почты, это снижает процент почты, доходящей до MTA, примерно в 2-3 раза. При этом policy выполняет балансировку между различными backend'ами на основании весов. MTA использует rmiter для выполнения различных проверок: clamav, spamd, ratelimits, greylisting, regexps. При этом, все данные greylisting'а и лимитов сохраняются в memcached, что обеспечивает очень быструю работу и возможность скалирования системы.
Итак, вначале скачиваем nginx версии 0.6.11: http://sysoev.ru/nginx/nginx-0.6.11.tar.gz Более свежие версии, к сожалению, работать не будут. Далее скачиваем libevent и применяем мой патч для поддержки TXT записей в резолвере:
libevent_txt.patch
Почему этой возможности до сих пор нет в libevent, мне не очень понятно.
После установки патченной версии libevent можно устанавливать nginx и policy.
Вначале патчим исходники nginx патчем для поддержки policy (также фиксится работа pipelining'а и включается режим работы без аутентификации). Патч лежит тут:
http://cebka.pp.ru/hg/hgwebdir.cgi/nginx-smtp-policy/file/ab33c3b8f1f1/patch-src_mail

После этого собираем nginx с поддержкой модуля mail.
Далее можно собрать policy. Скачиваем policy отсюда: http://cebka.pp.ru/hg/hgwebdir.cgi/nginx-smtp-policy/archive/tip.tar.gz
Собирается просто командой make+make install. При этом ставятся файлы nginx-smtp-policy и nginx-policy-watchdog с соответствующими стартовыми скриптами в /usr/local/etc/rc.d
Настройка policy предельно проста: в каждой строчке содержится переменная и ее значение. Строчки с # в начале считаются комментариями. Пример используемого мной конфига:

# nameserver - definition of nameserver (maybe multiply)
# special value resolv.conf is used to parse /etc/resolv.conf file
nameserver 195.19.37.129

# logfile - full path to logfile
# Default: /var/log/policy.log

logfile /var/log/policy/policy.log

# pidfile - full path to pidfile
# Default: /var/run/policy.pid

pidfile /var/log/policy/policy.pid

# backend_host - address of host that are backends for nginx (postfix address)
# Example host1:weight1,host2:weight2
# Default: 127.0.0.1

backends 127.0.0.1:1

# backend_port - port of backend
# Default: 25

backend_port 25

# listen - address of socket to listen on, maybe tcp or unix:
# tcp: listen tcp:host:port
# unix: listen unix:/path/to/sock
# Default: tcp:localhost:7070

listen tcp:localhost:7070

# loglevel - numeric value of logging verbocity
# 0 - only errors are logged
# 1 - warnings and errors are logged
# 2 - warnings, notices and errors are logged
# 3 - everything including debug messages is logged
# Default: 0

loglevel 2

# helo_regexp_file - specify path to helo regexp file and error code and error message string
# separated by ':' (maybe multiply)
# Example: helo_regexp_file /usr/local/etc/nginx-policy/helo.re:554 5.7.1:Bad address
# Default:

helo_regexp_file /usr/local/etc/postfix/maps/policy_helo_regexp:554 5.7.1:Helo rejected

# hostname_regexp_file - specify path to host regexp file and error code and error message string
# separated by ':' (maybe multiply)
# Example: hostname_regexp_file /usr/local/etc/nginx-policy/host.re:554 5.7.1:Bad address
# Default:
hostname_regexp_file /usr/local/etc/postfix/maps/policy_reverse_regexp:554 5.7.1:We do not accept mail form this dynamic pool

check_rbl xbl.spamhaus.org
check_rbl insecure-bl.rambler.ru


Файлы, которые я использую для этих регэкспов, можно найти в http://cebka.pp.ru/stuff/
Postfix ставится стандартным образом. Отдельно могу указать следующие вещи:
если мы на одном хосте пускаем policy и postfix, то в main.cf указываем
inet_interfaces = localhost
также для корректной работы rmilter нужно указать следующие опции:
milter_protocol = 4
smtpd_milters = unix:/var/run/rmilter/rmilter.sock
milter_default_action = accept
milter_mail_macros =  i {auth_type} {auth_authen} {auth_author} {mail_addr} {client_addr} {client_name}


для работы XCLIENT указываем, с каких хостов он разрешен (это хост, где работает policy)
smtpd_authorized_xclient_hosts = localhost

Далее настроим nginx:
mail {
      server_name  host.ru;
      auth_http    localhost:7070/nginxauth.cgi;

      smtp_auth         none;
      #smtp_capabilities "SIZE 28311552" PIPELINING 8BITMIME;
      smtp_capabilities "SIZE 28311552" 8BITMIME;
      xclient on;
      smtp_helo_required on;
      smtp_banner "mxi.icn.bmstu.ru ESMTP nginx\n\nSystem Info: This is a mail server at host.ru\n\n\n\nEmail contact: <abuse@host.ru>\n\n";

      proxy_pass_error_message  on;

      server {
          #listen             3525;
          listen             ip1:25;
          listen             ip2:25;
          protocol           smtp;
          timeout           
; 300s;
      }
}


Rmilter для данной системы можно поставить из портов FreeBSD - mail/rmilter
Настройка rmilter детально описано в конфиге и странице rmilter (8). Memcached для работы мильтра можно использовать любой. Для общения с memcached лучше использовать tcp. Работа в режиме udp пока нестабильна из-за кучи ошибок в реализации udp в мемкешеде.
Работа этой связки проверялась в FreeBSD 6, FreeBSD 7 и FreeBSD 8-CURRENT. Если кому-то удастся завести эту связку на других системах, шлите патчи :) Также в http://cebka.pp.ru/stuff лежат примеры конфигов и используемые нами "белые" списки.

Tuesday, August 12, 2008

Репозиторий некоторых софтин, которые я написал в "Рамблере"

Во время работы в "Рамблере" я написал несколько вещей, которые могут быть полезны не только у нас, но и пригодиться другим. Поэтому я решил выложить их в виде mercurial репозитария. Среди них:
- http://cebka.pp.ru/hg/rmilter/ - фильтр почты для sendmail/postfix, позволяющий делать различные проверки, в частности, spamassassin/clamav/dcc/spf, также имеются встроенные средства для грейлистинга и проверки лимитов (через memcached), также поддерживаются проверки по регэкспам. Мильтр конфигурируется при помощи конфигурационного файла, снабженного комментариями и описанного в странице man (rmilter.conf.5)
- http://cebka.pp.ru/hg/nginx-smtp-policy/ - асинхронный резолвер имен (dns resolver) для работы с nginx с патчем, изначально написанным Максом Дуниным (http://mdounin.ru/). Также nginx-smtp-policy осуществляет load balancing по различным mx'ам (согласно весам) и отсеивание нежелательной почты путем проверки RBL и мониторинг mx'ов, на которые осуществляется транспорт почты (nginx-smtp-watchdog). Данная программа предназначена для почтовых систем, обслуживающих очень большой поток почты и существенно снижает нагрузку на конечные mx'ы (патч, который есть в репозитории, работает с nginx до версии 0.6.11 включительно).
- http://cebka.pp.ru/hg/libevent/ - патчи, необходимые для работы nginx-smtp-policy. Существенно ускоряют резолвинг за счет использования вместо линейного списка DNS запросов хеш таблицу, а также патч, позволяющий резолвить TXT записи (для проверки RBL).

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

Thursday, June 5, 2008

Использование dmalloc

Так как использовать dmalloc не так просто, то записать основные принципы на будущее не бесполезно. Итак, dmalloc линкуется статически, например, так:
.ifdef DMALLOC
CFLAGS+=    -DDMALLOC -DDMALLOC_FUNC_CHECK
LIBS+=    -ldmalloc
.endif

Далее, самый простой способ использовать библиотеку:
определяем функции malloc и free, чтобы иметь представление, из какой строчки кода они вызываются:
#ifdef DMALLOC
#include <dmalloc.h>
#define mymalloc(x) dmalloc_malloc(__FILE__, __LINE__, (x), DMALLOC_FUNC_MALLOC, 0, 1)
#define myrealloc(x, y) dmalloc_realloc(__FILE__, __LINE__, (x), (y), DMALLOC_FUNC_REALLOC, 0, 1)
#define myfree(x) dmalloc_free(__FILE__, __LINE__, (x), DMALLOC_FUNC_FREE)
#define mystrdup(x) dmalloc_strndup(__FILE__, __LINE__, (x), strlen((x)), 1)
#else
#define mymalloc(x) malloc((x))
#define myrealloc(x, y) realloc((x), (y))
#define myfree(x) free((x))
#define mystrdup(x) strdup((x))
#endif

Далее в коде пользуемся только этими функциями для работы с памятью.
Для инициализации библиотеки можно пользоваться переменными окружения. Но в ряде случаев это не работает (cgi, su и прочее). Поэтому я решил делать инициализацию так:

#ifdef DMALLOC
    char dmalloc_log[PATH_MAX];
    snprintf (dmalloc_log, sizeof (dmalloc_log), "debug=0x4f47d03,log=%s/dmalloc-%%p.log", DMALLOC_LOG_PATH);
    dmalloc_debug_setup (dmalloc_log);
#endif

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

Логирование неосвобожденной памяти тоже лучше выполнять самому, чтобы потом не бояться потерять информацию при падении программы где-то. На мой взгляд, лучше всего использовать марки:
в начале секции кода, где может утекать память ставим:
#ifdef DMALLOC
        unsigned int mark;
        mark = dmalloc_mark();
#endif
в конце секции:
#ifdef DMALLOC
          dmalloc_log_changed(mark,
                            1 /* log unfreed pointers */,
                            0 /* do not log freed pointers */,
                            1 /* log each pnt otherwise summary */);
#endif

В логе имеем что-то вида
1212677815: 289299: Dumping Not-Freed Pointers Changed Since Mark 289178:
1212677815: 289299:  memory table is empty
Или же указатели на память:

1212677788: 288383:  not freed: '0x80144af88|s1' (24 bytes) from 'unknown'
1212677788: 288383:  not freed: '0x80144afc8|s5' (40 bytes) from 'unknown'
1212677788: 288383:  not freed: '0x80144be88|s1' (24 bytes) from 'unknown'
1212677788: 288383:  not freed: '0x80144bec8|s1' (40 bytes) from 'unknown'

Можно уронить процесс в корку (kill -SEGV, например) и посмотреть эти адреса в gdb:
(gdb) x/40c 0x80144bec8

Friday, February 15, 2008

Race в коде закрытия unix сокета в FreeBSD 6.x

Выглядит race следующим образом (6.3-SMP):

 48976 nginx    1202926950.760500 CALL  recvfrom(0xad,0x1b2ab38,0x400,0,0,0)

 48976 nginx    1202926950.760511 GIO   fd 173 read 122 bytes

       "HTTP/1.0 200 OK\r

        Client-Host: [UNAVAILABLE]\r

        Auth-Status: OK\r

        Auth-Server: 10.8.2.19\r

        Auth-Port: 25\r

        Connection: close\r

        \r

       "

 48976 nginx    1202926950.760521 RET   recvfrom 122/0x7a

 48976 nginx    1202926950.760533 CALL  close(0xad)

 48976 nginx    1202926950.760548 RET   close -1 errno 57 Socket is not
 connected


Похоже, race возникает тут:

sys/kern/uipc_socket.c:


int soclose(so)

{

...

        if (so->so_state & SS_ISCONNECTED) {

                if ((so->so_state & SS_ISDISCONNECTING) == 0) {

                        error = sodisconnect(so);



}



int sodisconnect(so)

{

...

 if ((so->so_state & SS_ISCONNECTED) == 0)

                return (ENOTCONN);

}


so_state для unix сокета устанавливается следующим образом:


sys/kern/uipc_usrreq.c:


static void

unp_disconnect(struct unpcb *unp)

{

        struct unpcb *unp2 = unp->unp_conn;

        struct socket *so;


        UNP_LOCK_ASSERT();


        if (unp2 == NULL)

                return;

        unp->unp_conn = NULL;

        switch (unp->unp_socket->so_type) {


        case SOCK_DGRAM:

                LIST_REMOVE(unp, unp_reflink);

                so = unp->unp_socket;

                SOCK_LOCK(so);

                so->so_state &= ~SS_ISCONNECTED;

                SOCK_UNLOCK(so);

                break;


        case SOCK_STREAM:

                soisdisconnected(unp->unp_socket);

                unp2->unp_conn = NULL;

                soisdisconnected(unp2->unp_socket);

                break;

        }

}


Насколько я понял, для sock_stream закрываются оба конца соединения.
Закрываются так:


sys/kern/uipc_socket2.c:


void

soisdisconnected(so)

        register struct socket *so;

{


        /*

         * XXXRW: This code assumes that SOCK_LOCK(so) and

         * SOCKBUF_LOCK(&so->so_rcv) are the same.

         */

        SOCKBUF_LOCK(&so->so_rcv);

        so->so_state &=
~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);

        so->so_state |= SS_ISDISCONNECTED;

        so->so_rcv.sb_state |= SBS_CANTRCVMORE;

        sorwakeup_locked(so);

        SOCKBUF_LOCK(&so->so_snd);

        so->so_snd.sb_state |= SBS_CANTSENDMORE;

        sbdrop_locked(&so->so_snd, so->so_snd.sb_cc);

        sowwakeup_locked(so);

        wakeup(&so->so_timeo);

}


То есть, не исключена ситуация, когда в функции soclose у одного из
сокетов состояние SS_ISCONNECTED, а в функции sodisconnect этот флаг уже
успевает убраться. Тогда может вернуться ENOTCONN. Как вариант фикса можно попробовать убрать проверку

 if ((so->so_state & SS_ISCONNECTED) == 0)

                return (ENOTCONN);

из функции sodisconnect, например, возвращая в этом месте 0.

Tuesday, February 5, 2008

Миграция с jabberd2 на openfire

Используя плагин экспорта/импорта у openfire можно заводить
пользователей, используя достаточно простой xml код. Хотя этот плагин
работает достаточно нестабильно (xml файл, содержащий 4к пользователей
он не переварил, вываливаясь с NullPointerException), по крайней мере у
меня, но, возможно, кому-то это окажется полезным.
jabber_to_xml.pl



Thursday, December 13, 2007

Memcached UDP fix

Следующий патчик исправляет ошибку в обработке udp запросов memcached'ом, которая приводила к падению его в корку при посылке пакета с неверным заголовком, а также не освобождала буфер чтения при ошибочной команде или же ненайденном ключе.
patch-memcached-udp

Friday, December 7, 2007

Memcached UDP

Хотя memcached поддерживает формально работу по udp протоколу, но пользоваться им я не рекоммендую. Более того, если мемкешеду послать udp пакет с неправильным заголовком, то он (memcached) падает в корку. Далее, даже при посылке правильных пакетов у мемкешеда через некоторое время выплывает проблема неочистки командного буфера, и при посылке очередной команды мемкешед начинает "вспоминать" куски старых команд. Сейчас я пытаюсь исправить эти баги, и если все удастся, то будет патч совместно с библиотекой работы с мемкешедом, которая умеет udp (tcp тоже, но исходной задачей было написание thread-safe библиотеки для работы с мемкешедом). При использовании udp для извлечения большого количества маленьких ключей из мемкешеда должно весьма существенно ускориться.
Данные тестирования:
UDP:
Results of memcached stress test:
Total number of connections: 10000
Number of seconds for test: 2.23
Number of successfull connections: 10000
Connections per second: 4494.25
TCP:
Results of memcached stress test:
Total number of connections: 10000
Number of seconds for test: 4.38
Number of successfull connections: 10000
Connections per second: 2280.87

При этом, использовалось 100 одновременных коннекций к серверу, каждая из которых выполняла 3 операции над своим ключом: set, get и delete.

Friday, November 23, 2007

Использование libmilter во FreeBSD

При сборке libmilter по умолчанию во FreeBSD она использует select(3) по умолчанию. Однако, в нагруженных системах это приводит к ошибке EBADF при попытке сделать select, так как номер дескриптора превышает FD_SETSIZE. В таком случае можно применить патч, который собирает libmilter с использованием poll(3).
libmilter.patch

Thursday, November 8, 2007

Извлечение данных из sysctl (3) в FreeBSD

С системным вызовом sysctl есть одна проблема: нет очевидного способа узнать тип возвращаемого значения. Это неважно, когда тип мы точно знаем, но сильно затрудняет жизнь, когда не знаем. Получается тип следующим образом:
к mib-у приписываются с начала два дополнительных целых - 0,4, то есть, целевой миб будет выглядеть так:
0,4,[mib]
где mib - искомый mib
Далее делается sysctl для этого mib-а, размер лучше задать равным BUFSIZ или же получить размер, указав в качестве буфера NULL.
Чтобы узнать, что хранится в mib'е необходимо разобрать полученный буфер следующим образом:
(unsigned int) (char) [(char) [(char)...]]
Для извлечения значения первое поле можно не использовать, но в нем также хранится тип, который извлекается бинарным "И" с константой CTLTYPE (number & CTLTYPE). Полученное значение сравнивается с CTL константами в /usr/include/sys/sysctl.h. Два символа после этого значения также можно использовать для опеределения типа - 1-й символ определяет тип, второй символ (которого может не быть) определяет наличие знака для численных типов. Соответствие типов и символов типа:
int - 'I'
long - 'L'
quad_t - 'Q'
pointer - 'P'
typedef - 'T'
struct - 'S'
Для 'S' и 'T' типов тип описывается после запятой, то есть, строчка будет 'S,some_struct' или 'T,some_type_t'. Для численных типов второй символ может быть 'U', тогда это число - беззнаковое число.

Thursday, November 1, 2007

Настройка cgi репозитария для mercurial

Преамбула:
для выполнения коммиттов нескольих пользователей в несколько репозитариев лучше всего использовать cgi расширение mercurial. Для этого необходимо настроить соответствующим образом apache, и hgwebdir.

Настройка web-серверва

создаем какую-то директорию, например /usr/local/hgstore, назначаем ее владельца www
записываем в httpd.conf:
    Alias /hg/ "/usr/local/hgstore/"
    <Directory "/usr/local/hgstore/">
        Order allow,deny
        Allow from all
        AllowOverride All
        Options ExecCGI FollowSymLinks
        AddHandler cgi-script .cgi
        RewriteEngine On
        #write base depending on where the base url lives
        RewriteBase /hg
        RewriteRule ^$ hgwebdir.cgi  [L] 
        # Send requests for files that exist to those files.
        RewriteCond %{REQUEST_FILENAME} !-f
        # Send requests for directories that exist to those directories.
        RewriteCond %{REQUEST_FILENAME} !-d
        # Send requests to hgwebdir.cgi, appending the rest of url.
        RewriteRule (.*) hgwebdir.cgi/$1  [QSA,L]
    </Directory>

Создаем в этой директории такой .htaccess:

AuthUserFile /usr/local/www/htpasswd_hgstore
AuthName "Mercurial Repository"
AuthType Basic
<LimitExcept GET>
  Require valid-user
</LimitExcept>

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

Настройка mercurial

Далее копируем hgwebdir в заданную директорию:
# install -b /usr/local/share/mercurial/www/hgwebdir.cgi /usr/local/hgstore
# chmod +x /usr/local/hgstore/hgwebdir.cgi

Для каждого репозитария прописываем соответствующую строчку в hgweb.config:
[paths]
VIRTUAL_PATH = FILESYSTEM_PATH
 
VIRTUAL_PATH - путь, который будет показан в url: https://host/hg/path
FILESYSTEM_PATH - путь, относительно расположения hgwebdir.cgi

Далее создаем hgrc по умолчанию:

[extensions]
hgext.notify =
hgext.gpg =

[email]
from = mercury@example.com

[smtp]
host = localhost

[web]
allow_push = *
push_ssl = false
contact = Vsevolod Stakhov
description = Get our elephants
baseurl = https://hostname/hg/rmilter/
allow_archive = bz2 gz zip
style = gitweb

[hooks]
# send one email per group of changes
changegroup.notify = python:hgext.notify.hook
#incoming.notify = python:hgext.notify.hook

[notify]
# multiple sources can be specified as a whitespace separated list
sources = serve push pull bundle
# really send email
test = false
# subscriber data lives in the notify repo
config = /usr/local/hgstore/notify.conf
#repos live in /usr/local/hgstore/repos on server, so strip 4 "/" chars
strip = 4
template = X-Hg-Repo: {webroot}\nSubject: {webroot}: {desc|firstline|strip}\nFrom: {author}\n\nchangeset {node|short} in {root}\ndetails: {baseurl}{webroot}?cmd=changeset;node={node|short}\n\t{desc|tabindent|strip}\n
maxdiff = -1

Этот файл нужно копировать в каталог repo/REPONAME/,hg/hgrc
для каждого репозитория:
# mkdir repo
# hg init repo/REPONAME
# cp hgrc repo/REPONAME/.hg/hgrc
Поправив соответствующим образом.

Далее нужно не забыть прописать этот путь в hgweb.config.
После этого можно делать push и смотреть репозиторий через web.

Для добавления нового репозитария надо:

* сделать начальный каталог при помощи hg init repos/REPONAME
* прописать путь к нему в файле hgweb.config
* скопировать hgrc по умолчанию в каталог repos/REPONAME/.hg/hgrc
* отредактировать файл repos/REPONAME/.hg/hgrc

После всего нужно сменить фладельца всех файлов на www:
# chown -R www /usr/local/hgstore

Friday, October 26, 2007

Встроенные функции gcc для атомарных операций

В gcc 4.1.0 появились специальные встроенные функции, предназначенные для выполнения атомарных операций. Более подробно о них написано тут.
Например, следующий код берет адрес позиции указателя в определенном массиве и инкрементирует указатель (после операции он указывает на следующий элемент в массиве):
 cur_pos = __sync_fetch_and_add (&pos, sizeof(u_char));

В gcc3 этих функций нет.