LINUX.ORG.RU

Сообщения HolyBoy

 

Серверное приложение на Haskell: что происходит с памятью.

Форум — Development

Здравствуйте.

Есть сервер, который на скорости в 10 qps (в будущем может и поболее) обрабатывает короткие запросы. В самом начале он кушает 8032 КБ по показаниям RSS, через сутки или даже поболее — 15980 КБ.

Является ли подобное поведение свидетельством утечки или это норма для GC и программ на Haskell?

 

HolyBoy
()

Контроль за ресурсами в Haskell-программе.

Форум — Development

Есть такой вопрос: идёт поток запросов к TCP-серверу. Я этот поток обрабатываю, вытаскиваю данные, делаю разные запросы к другим серверам, результат возвращаю.

По сути, мой код является прокси-сервером, который дружит старое ПО с новым. Написан на кондуитах.

С ним есть проблема, решить которую я не смог: теряются ресурсы в виде файловых дескрипторов. Происходит это из-за подобного подхода:

main :: IO ()
main =
  runTCPServer (serverSettings 4002 hostnameListen) $ \proxy ->
    appSource proxy $= … $$ appSink proxy

…


condCyrusGet :: (MonadIO m, Monad m) => Conduit ByteString m ByteString
condCyrusGet = awaitForever $ \bs -> do
  res <- liftIO $ cyrusSock bs
  yield res

cyrusAppAddr = "/run/cyrusserv/cyrus.socket"

cyrusSock :: ByteString -> IO ByteString
cyrusSock ar = do
  soc <- NS.socket AF_UNIX Stream 0
  NS.connect soc (SockAddrUnix cyrusAppAddr)
  NSB.send soc ar
  msg <- NSB.recv soc 32768
  NS.sClose soc
  return msg

при подключении к сокету, который слушается совсем простым сервером:

hostnameListen = "/run/cyrusserv/cyrus.socket"
cyrusHostname = "127.0.0.1"

main :: IO ()
main = 
  runUnixServer (CNU.serverSettings hostnameListen) $ \proxy ->
    runTCPClient (CN.clientSettings 12345 cyrusHostname) $ \client_cyrus ->
      runConcurrently $
        Concurrently (appSource proxy $$ appSink client_cyrus) *>
          Concurrently (appSource client_cyrus $$ appSink proxy)

Его предназначением является связь каких-то локально запущенных серверов с одним удалённым.

Не закрываются вовремя подключения к Unix-сокету в функции cyrusSock и постепенно количество незакрытых соединений растёт. В итоге, всё заканчивается

socket: resource exhausted (Too many open files)

Полагаю, что проблема решается загоном этой функции cyrusSock и вообще, общения с этим Unix-сокетом через кондуит, примерно так, как сделано в последнем куске кода, но как это сделать — не представляю. Использовать bracket?

Что подскажет уважаемое сообщество?

 

HolyBoy
()

Как запомнить результат из IO ()?

Форум — Development

Есть, условно говоря, такой код:

main :: IO ()
main = do
  conf <- Con.load [Con.Required "service.cfg"]
  let subconf = Con.subconfig "lala" conf
  listen <- Con.lookupDefault  "127.0.0.1" subconf "listen" :: IO String
  port <- Con.lookupDefault 4002 subconf "port" :: IO Int
  runTCPServer …


ldapURI = "ldap://host/"
ldapUser = "cn=admin"
ldapPWD = "123"

ldapBDN = Just "ou=Org"
ldapAttrs = LDAPAttrList ["param1", "param2"]


ldapExecuteQuery :: ByteString -> String -> IO AnswerEmailCheck
ldapExecuteQuery m qwr = do
  conn <- ldapInitialize ldapURI
  ldapSimpleBind conn ldapUser ldapPWD
  result <- ldapSearch conn ldapBDN LdapScopeSubtree (Just qwr) ldapAttrs False
  return $ getRes m result

myConnectionRedis :: ConnectInfo
myConnectionRedis = defaultConnectInfo {connectHost = "127.0.0.1"}

condRedisGet :: MonadIO m => Conduit MailMqouta m ByteString
condRedisGet = awaitForever $ \(mail, mquota) -> do
  conn <- liftIO $ Database.Redis.connect myConnectionRedis
  result <- liftIO $ runRedis conn $ do
    res <- get . createRedisQuery $ mail
    case res of
…

Суть проблемы такова: я хочу вынести константы типа myConnectionRedis, ldapURI и прочего во внешний конфиг. То, как оно может быть исполнено, нарисовано в main.

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

Функции же эти вызываются очень часто, на каждый кусок данных.

Возможно, кстати, что мой подход к написанию кода ужасен, тогда прокомментируйте и скажите, что именно не так.

Заранее благодарю за советы.

 

HolyBoy
()

Часовые пояса и Data.Time

Форум — Development

Есть простейший код:

import Data.Time.Clock.POSIX
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import System.Locale

main = do
  print $ zonedTimeToUTC t1
  print $ zonedTimeToUTC t1'

t1 :: ZonedTime
t1 = readTime defaultTimeLocale
  "%B %e %Y %l:%M%P %Z"
  "March 8 2009 7:30pm EST"

t1' :: ZonedTime
t1' = readTime defaultTimeLocale
  "%B %e %Y %l:%M%P %Z"
  "March 8 2009 7:30pm MSK"

Выполняю и получаю:

*Main> main 
2009-03-09 00:30:00 UTC
2009-03-08 19:30:00 UTC

Что за?… Модуль не умеет зоны разбирать стандартные? Если посмотреть внутрь t1', то видно, что:

*Main> zonedTimeZone t1' 
MSK
*Main> zonedTimeToLocalTime  t1' 
2009-03-08 19:30:00

как и написано в документации http://hackage.haskell.org/package/time-1.4.2/docs/Data-Time-LocalTime.html#t...

Пробовал играться с http://hackage.haskell.org/package/timezone-series-0.1.3 — результат не изменился.

Интересно, где лыжи не едут и как получить корректный разбор часовых зон, отличных от тех, что в Америке живут?

 ,

HolyBoy
()

Ejabberd с LDAP-бекендом: показ аватарок.

Форум — Admin

Здравствуйте.

Настроено так:

{mod_vcard_ldap, [
    %% соответствие между полями Vcard и результатом поиска в LDAP
    {ldap_vcard_map, [
        {"NICKNAME", "%u", []},
        {"FAMILY", "%s", ["sn"]},
        {"GIVEN", "%s", ["givenName"]},
        {"MIDDLE", "%s", ["initials"]},
        {"FN", "%s %s %s", ["sn", "givenName", "initials"]},
        {"EMAIL", "%s", ["mail"]},
        {"ORGUNIT", "%s", ["ou"]},
        {"PHOTO", "%s", ["jpegPhoto"]},
        {"LOCALITY", "%s", ["l"]},
        {"TEL", "%s", ["telephoneNumber"]}
    ]},
    %% предлагаем искать только по этим полям
    {ldap_search_fields, [
        {"Пользователь", "%u"},
        {"Фамилия", "sn"},
        {"Имя", "givenName"},
        {"Отчество", "initials"},
        {"Почта", "mail"},
        {"Отдел", "ou"},
        {"Телефон", "telephoneNumber"}
    ]},
    %% показываем только эти результаты
    {ldap_search_reported, [
        {"Полное имя", "FN"},
        {"Пользователь", "NICKNAME"}
    ]}
  ]}

Аватары соответствуют стандартам по размерам и прочим характеристикам (http://www.xmpp.org/extensions/xep-0153.html#bizrules-image), хранятся в соответствующем поле jpegPhoto.

Ростер отдаётся автоматически, при подключении. Соответственно, все поля в нём заполняются автоматически и корректно (ник, статус и тд), за исключением аватары. Вот пример того, что получаю от сервера, когда подключаюсь:

<<<< user1@domain/Vacuum-IM 10:40:31 +3 <<<<
  <presence from="user2@domain/123" to="user1@domain/Vacuum-IM">
    <priority>30</priority>
    <status>tralala</status>
    <x xmlns="jabber:x:avatar">
      <hash>35b6967ad4cf11b2a2b65c16017c0a0128063c22</hash>
    </x>
    <c xmlns="http://jabber.org/protocol/caps" node="http://vacuum-im.googlecode.com" ver="nvOfScxvX/KRll5e2pqmMEBIls0=" hash="sha-1"/>
    <x xmlns="vcard-temp:x:update">
      <photo/>
    </x>
    <delay xmlns="urn:xmpp:delay" from="user2@domain/123" stamp="2014-06-05T06:22:56Z"/>
    <x xmlns="jabber:x:delay" stamp="20140605T06:22:56"/>
  </presence>

Аватара не отображается. При этом, внутри vCard, если я его открою, показывается корректно. Если же я ткну «обновить Vcard», то аватар стягивается и показывается в ростере до следующего перезапуска клиента или истечения какого-то таймаута:

>>>> user1@domain/Vacuum-IM 10:51:30 +11343 >>>>
  <iq type="get" to="user2@domain" id="sid_40">
    <vCard xmlns="vcard-temp"/>
  </iq>

<<<< user1@domain/Vacuum-IM 10:51:30 +16 <<<<
  <iq from="user2@domain" type="result" to="user1@domain/Vacuum-IM" id="sid_40">
    <vCard xmlns="vcard-temp">
      <NICKNAME>user2</NICKNAME>
      <FN>Иванов Иван Иванович</FN>
      <EMAIL>
        <INTERNET/>
        <PREF/>
        <USERID>mail@mail.mail</USERID>
      </EMAIL>
      <PHOTO>
        <TYPE>image/jpeg</TYPE>
        <BINVAL>/9j/4AAQSkZJRg…==</BINVAL>
      </PHOTO>
      <TEL>
        <VOICE/>
        <WORK/>
        <NUMBER>123</NUMBER>
      </TEL>
      <N>
        <FAMILY>Иванов</FAMILY>
        <GIVEN>Иван</GIVEN>
        <MIDDLE>Иванович</MIDDLE>
      </N>
      <ORG>
        <ORGUNIT>Подразделение</ORGUNIT>
      </ORG>
      <ADR>
        <LOCALITY>city</LOCALITY>
      </ADR>
    </vCard>
  </iq>

Сталкивался ли кто-то с такой проблемой и как заборол?

 ,

HolyBoy
()

conduits: выбор вариантов

Форум — Development

Здравствуйте.

Есть такой код:

{-# LANGUAGE OverloadedStrings #-}

import Data.Conduit
import Conduit
import Control.Concurrent.Async
import Data.Conduit.Network
import Data.Conduit.Binary as DCB
import Data.Conduit.List as DCL
import Data.ByteString.Char8 as B

hostnameListen = "127.0.0.1"

main :: IO ()
main = 
  runTCPServer (serverSettings 4003 hostnameListen) $ \server ->
    appSource server $= DCB.lines $= condToInt $= condToBS $$ appSink server

condToInt :: Conduit ByteString IO Int
condToInt = awaitForever $ \bs -> do
  let res = B.readInt bs
  case res of
    Nothing -> yield 0
    Just (x, _) -> yield x

condToBS :: Conduit Int IO ByteString
condToBS = awaitForever $ \i -> do
  let result = B.pack . show $ i
  yield result

condMultiplN :: Int -> Conduit Int IO Int
condMultiplN m = awaitForever $ \i ->
  yield $ i * m

Ломаю голову над следующим: как сделать так, чтобы в

appSource server $= DCB.lines $= condToInt $= condToBS $$ appSink server
после condToInt, в зависимости от чётности числа (к примеру), вышедшего из этого кондуита, зависело то, какой кондуит дальше будет его обрабатывать.

Поясню подробнее.

main :: IO ()
main = 
  runTCPServer (serverSettings 4003 hostnameListen) $ \server -> do
    (resum1, res) <- appSource server $= DCB.lines $$+ condToInt 
    case (odd res) of
      True -> resum1 $$+- condMultiplN 2 =$ condToBS $$ appSink server
      otherwise -> resum1 $$+- condMultiplN 3 =$ condToBS $$ appSink server

Я знаю, что показанный код нерабочий, ибо

($$+) :: Monad m => Source m a -> Sink a m b -> m (ResumableSource m a, b)
но мне кажется, что мою мысль передать получилось: взять что-то, проверить и пустить по одному из направлений, взять следующее что-то, опять проверить и пустить по одному из направлений, повторить.

Вопрос: как этого добиться?

 

HolyBoy
()

Руководство по написанию клиент-серверных приложений с использованием network-conduit

Форум — Development

Я попросил разработчика оных кондуитов набросать несколько примеров, чтобы объяснить, как с ними работать. Он выполнил просьбу: http://www.yesodweb.com/blog/2014/03/network-conduit-async

Смотрите и комментируйте, желательно, у него в блоге, чтобы поправил/добавил какие-то моменты.

Считаю, очень круто получилось: садись и пиши что-то своё.

 ,

HolyBoy
()

conduit-network и утечки памяти: как правильно готовить?

Форум — Development

Написан сервер, который принимает на вход строки и возвращает их в верхнем регистре:

import Data.Conduit
import Data.Conduit.Network 

import Data.ByteString.Char8 as BS
import Control.Monad.IO.Class

import qualified Data.Conduit.List as CL
import qualified Data.Conduit.Binary as CB
import Data.Conduit.Text
import Data.Text (toUpper)

main :: IO ()
main = runTCPServer (serverSettings 4000 HostIPv4) echoApp

echoApp :: Application IO
echoApp appData = appSource appData $= upperConduit $$ appSink appData

upperConduit :: Conduit ByteString IO ByteString
upperConduit = decode utf8 =$= CL.map toUpper =$= encode utf8

Компилируем:

ghc -O2 -rtsopts server.hs

и прогоняем через него файл размером в 174 Мб:

cat test.txt | nc localhost 4000

После окончания прогона сервер съедает до 400 Мб памяти и не освобождает её.

14,931,590,048 bytes allocated in the heap
      56,583,656 bytes copied during GC
     145,399,256 bytes maximum residency (11 sample(s))
     181,088,520 bytes maximum slop
             431 MB total memory in use (65 MB lost due to fragmentation)

Если запустить в параллель 2-3 таких прогона, то съедается в несколько раз больше памяти.

Почему так происходит и как правильно реализовать работу с кондуитами в данном случае?

 

HolyBoy
()

Хвостовая рекурсия.

Форум — Development

Разбираюсь с хвостовой рекурсией.

Реализовал алгоритм поиска пирамидального числа:

-- recursion version
--
fermaPyr 0 = 0
fermaPyr n = fermaTria n + fermaTria (n - 1)

fermaTria 0 = 0
fermaTria n = n + fermaTria (n - 1)

-- tail recursion version
-- 
fermaPyrAcc n = let fAcc 0 a = a
                    fAcc n a = fAcc (n - 1) (fermaTriaAcc n + a)
                in fAcc n 0

fermaTriaAcc n = let fAcc 0 a = a
                     fAcc n a = fAcc (n - 1) (n + a)
                 in fAcc n 0

-- tail recursion ver.2
--
fermaPyrAcc2 0 = 0
fermaPyrAcc2 n = fermaTriaAcc n + fermaTriaAcc (n - 1)



main = do
    let a = fermaPyr 1000000
        b = fermaPyrAcc 10000
        c = fermaPyrAcc2 1000000
    putStrLn $ show a
    --putStrLn $ show b
    --putStrLn $ show c

Запуск варианта а:

     177,963,104 bytes allocated in the heap
     187,666,896 bytes copied during GC
      36,388,928 bytes maximum residency (8 sample(s))
          36,384 bytes maximum slop
              82 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       237 colls,     0 par    0.13s    0.13s     0.0005s    0.0014s
  Gen  1         8 colls,     0 par    0.09s    0.10s     0.0119s    0.0285s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.10s  (  0.10s elapsed)
  GC      time    0.22s  (  0.22s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.01s  (  0.01s elapsed)
  Total   time    0.33s  (  0.33s elapsed)

  %GC     time      67.7%  (67.7% elapsed)

  Alloc rate    1,831,837,945 bytes per MUT second

  Productivity  32.3% of total user, 31.9% of total elapsed

Всё нормально, расход памяти, как и ожидалось, немалый, время исполнения тоже.

Запускаю вариант b:

   3,201,017,336 bytes allocated in the heap
         697,536 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      6104 colls,     0 par    0.03s    0.03s     0.0000s    0.0000s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    1.96s  (  1.98s elapsed)
  GC      time    0.03s  (  0.03s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    1.99s  (  2.01s elapsed)

  %GC     time       1.7%  (1.7% elapsed)

  Alloc rate    1,635,694,726 bytes per MUT second

  Productivity  98.3% of total user, 97.4% of total elapsed

Здесь, памяти расходуется гораздо меньше, но время исполнения… при аргументе на 2 порядка меньше, чем в других вариантах, работает в несколько раз медленнее. Увеличив аргумент на порядок, я уже не дожидался окончания расчётов.

Вариант с:

     128,057,440 bytes allocated in the heap
          29,752 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       243 colls,     0 par    0.00s    0.00s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.08s  (  0.09s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.08s  (  0.09s elapsed)

  %GC     time       2.0%  (2.1% elapsed)

  Alloc rate    1,571,481,972 bytes per MUT second

  Productivity  97.8% of total user, 94.8% of total elapsed

Самый быстрый и меньше всего расходует память.

Для меня не очевидна разница между вариантами b и с. Почему оно так? Я ожидал, что вариант b будет самым лучшим т.к. в функции fermaPyrAcc2 нет хвостовой рекурсии, она только в её внутреннем цикле.

 

HolyBoy
()

Шрифты в Opera под KDE выглядят ужасно

Форум — Desktop

После тестирования гнома3 понял, что это не моё. Установил KDE.

Вот скриншот сообщения, показанного в ФФ: http://s30.postimg.org/g9uckwbap/image.png

А вот, Opera: http://s29.postimg.org/ua47iecyv/image.png

Шрифты от ParaType, но их смена не помогает. Рендер шрифтов от infinality. Под 2-м гномом работало нормально. Как можно исправить это?

 ,

HolyBoy
()

Parsec на ByteString

Форум — Development

Здравствуйте.

Написан один модуль примерно в таком стиле:

import Text.Parsec
import Text.Parsec.ByteString


import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS (pack, unpack, concat, singleton, append, cons)


import Control.Applicative ( (<$>), (<*), (*>), (<*>) )
import Data.Char (chr)


--------------
--- Syntax ---
--------------



-- Where any quoted-pair appears, it is to be interpreted as the
-- character alone.  That is to say, the "\" character that appears as
-- part of a quoted-pair is semantically "invisible".
quotedPair :: Parser ByteString
quotedPair = BS.cons '\\' <$> (char '\\' *> (vchar <|> wsp <|> obsQp))

---------------------------
--- Folding White Space ---
--- and comments        ---
---------------------------

-- Folding White Space
fws :: Parser ByteString
fws = try (optional (wspMany >> crlf) >> wspMany1) <|> obsFws

-- Printable US-ASCII 
-- characters not including
-- "(", ")", or "\"
ctext :: Parser ByteString
ctext = BS.singleton <$> ranges [[33..39],[42..91],[93..126]] <|> obsCtext

ccontent :: Parser ByteString
ccontent = ctext <|> quotedPair <|> comment

comment :: Parser ByteString
comment = BS.concat <$> (between (char '(') (char ')') $
    many (optional fws *> ccontent) <* optional fws) <?> "comment"

-- comments and folding white space
cfws :: Parser ByteString
cfws = BS.concat <$>
    (try (many1 (optional fws *> comment)) <* optional fws) <|>
        fws

-----------------------------
--- Simple Lexical Tokens ---
-----------------------------

-- white space
-- from RFC 5234
wsp :: Parser ByteString
wsp = BS.singleton <$> (char ' ' <|> char '\t') <?> "space or tab"

-- Internet standard newline
-- from RFC 5234
crlf :: Parser ByteString
crlf = (do
    cr
    lf
    return $ BS.pack "\r\n") <?> "CRLF"

…and so on

Повсевместно приходится использовать преобразования типа singleton, pack и т.п. Есть мнение, что это добавляет оверхед.

Есть ли иной способ приготовить Parsec для получения максимальной производительности при обработке ByteString'ов?

 ,

HolyBoy
()

Tree structure в LDAP и MTA: как подружить?

Форум — Admin

Для хранения виртуальных доменов, ящиков, алиасов используется древовидная структура в LDAP:

o=p
ou=Domains,o=p
dc=local,ou=Domains,o=p
dc=test,dc=local,ou=Domains,o=p

для домена test.local и mail=example@test.local,dc=test,dc=local,ou=Domains,o=p для почтового ящика example@test.local. Таким образом, если у нас 100500 разных доменов, то такая структура уменьшает объёмы повторяющейся информации.

Чтобы запросить нужную информацию, надо сформировать соответствующий запрос. Для exim он выглядит примерно так, скажем, если ищем local_domains:

LDAP_AUTH = user="uid=exim,ou=S,o=p" pass="123"
LDAP_URL = ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock

DOMAIN_IN_DC = ${sg{$domain}{[.]}{,dc=}}
LDAP_BASE = dc=DOMAIN_IN_DC,ou=Domains,o=p

LDAP_QUERY_DOMAINS = dc?base?(&(objectClass=mailDomain)(accountStatus=active))


domainlist local_domains = @ : ${lookup ldap {LDAP_AUTH LDAP_URL/LDAP_BASE?LDAP_QUERY_DOMAINS}{$domain}fail}

Основное тут следующее: в макросе DOMAIN_IN_DC мы test.local приводим к виду test,dc=local, затем, LDAP_BASE возвращает dc=test,dc=local,ou=Domains,o=p и мы ищем нужные данные в ветке, зависящей от домена.

Решил посмотреть, что на эту тему есть у postfix и не нашёл ничего интересного. Т.е., да, postfix умеет искать в LDAP, но делает это примитивно, в расчёте на то, что все домены представляют собой плоский список, а поскольку я не создаю для записи dc=test,dc=local,ou=Domains,o=p атрибута типа virtDomain со значением test.local, т.к. не вижу смысла дублировать уже имеющуюся информацию, то получается, что в древовидной схеме постфикс не найдёт доменов.

Вопросы: это неизлечимый дефект в дизайне постфикса? Если излечимый, то как его можно поправить? Написать отдельный аутентификатор, который это умеет? Написать отдельный демон, который будет сам общаться с LDAP и отдавать postfix результаты поиска?

 , ,

HolyBoy
()

Велосипед на haskell

Форум — Development

Существует некая маленькая функция на С:

unsigned int str_hash(const char *p)
{
        const unsigned char *s = (const unsigned char *)p;
	unsigned int g, h = 0;

	while (*s != '\0') {
		h = (h << 4) + *s;
		if ((g = h & 0xf0000000UL)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}
		s++;
	}
	return h;
}

с целью обучения переписал её на Haskell:

import Data.Bits as DB
import Data.Word
import Data.List as DL (foldl')


simpleHash :: [Word8] -> Int
simpleHash warr = DL.foldl' shiftFunc 0 warr
    where
       shiftFunc h s =  ((middleTransf h) `shiftL` 4) + (fromIntegral s :: Int)


middleTransf :: Int -> Int
middleTransf arg = case (bitAnd arg) of
    0 -> arg
    _ -> (xorShift arg) `xor` (bitAnd arg)
    where
        bitAnd a = a .&. 0xf0000000
        xorShift b = b `xor` ((bitAnd b) `shiftR` 24)

Вопросы:

1. Правильно ли?

2. Возможно ли сделать эту реализацию короче/нагляднее/эффективнее?

3. Возможно ли использование Prelude.foldl вместо Data.List.foldl' ?

 ,

HolyBoy
()

Миграция от procmail.rc к sieve: проблемы

Форум — Admin

Здравствуйте.

Имеется procmailrc примерно с таким содержимым:

:0
* ^From one-two
{
        :0 B
        * ^Subject: WOW
        /dev/null
}

Он ищет в письме наличие заголовка From с соответствующим содержимым и проверяет, есть ли также в теле письма строка, начинающаяся с Subject.

Если написать на Sieve что-то типа (для начала):

if body :regex "^Subject: WOW" {
  discard;
  stop;
}

то ничего не находится, почему-то регулярка не срабатывает. Варианты с

if body :regex "Subject: WOW"

или

if body :contains "Subject: WOW"

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

Когда сохранил письмо и проверил локально нужной регуляркой («^Subject…») его содержимое, то текст выцепился.

Получается, что проблема в том, что почему-то BODY самим Sieve интерпретируется не построчно.

Кто как решал данную проблему?

 ,

HolyBoy
()

Генерация запроса к LDAP серверу средствами Exim

Форум — Admin

Здравствуйте.

Предположим, что есть base DN o=x.

Представим, что имеется дерево такое, что домен $N_LVL,…$2_LVL.$1_LVL представлен в виде записи dc=$N_LVL,…dc=$2_LVL,dc=$1_LVL,o=x, т.е., например, example.com можно найти как

dn: o=x

dn: dc=com,o=x

dn: dc=example,dc=com,o=x

и тд

Предполагаю, что в Exim для создания запроса к такому дереву надо будет ${domain} привести к виду $N_LVL,…dc=$2_LVL,dc=$1_LVL с помощью замены точек в доменном имени на ",dc=", (tr, sg) а затем, воткнуть эту подстроку в «dc=${подстрока},o=x», во всяком случае, не вижу, почему это не может не сработать.

Вопрос: как сильно это замедлит Exim при большом потоке писем? Есть ли более оптимальные пути для решения такой задачи?

 ,

HolyBoy
()

Запуск системы с разделами на LVM

Форум — Admin

Приветствую всех.

Решил снова пощупать systemd. Поставил всё и настроил так, как описано в http://wiki.gentoo.org/wiki/Systemd

Система у меня вся на LVM, в т.ч. и корень, так что, загружаюсь с initramfs. Вот кусок grub.conf:

title systemd
root (hd0,0)
kernel /boot/kernel root=/dev/ram0 real_root=/dev/vg/root dolvm real_init=/usr/lib/systemd/systemd
initrd /boot/initramfs

Загрузка идёт нормально до момента, когда согласно fstab

/dev/sda1	/boot	ext2		noauto,noatime,nodiratime	1 2
/dev/vg/root	/	xfs		noatime,nodiratime		0 1
/dev/sda2	none	swap		sw				0 0
/dev/vg/home	/home	xfs	noatime,nodiratime	0 2
/dev/vg/kernel	/usr/src	xfs	noatime,nodiratime	0 2
/dev/vg/portage	/usr/portage	reiserfs	noatime,nodiratime	0 2
/dev/vg/distfiles	/usr/portage/distfiles	xfs	noatime,nodiratime	0 2
/dev/vg/packages	/usr/portage/packages	xfs	noatime,nodiratime	0 2
/dev/vg/log	/var/log	xfs	noatime,nodiratime	0 2
/dev/vg/vartmp	/var/tmp	reiserfs	noatime,nodiratime	0 2
/dev/vg/layman	/var/lib/layman	reiserfs	noatime,nodiratime	0 2
tmpfs		/tmp		tmpfs		size=500M,noatime	0 0
/dev/vg/somefiles   /home/basov/files       xfs     noatime,nodiratime      0 2
/dev/vg/libvirt_images  /var/lib/libvirt/images     xfs     noatime,nodiratime  0 2

надо подключать остальные разделы, кроме корня. И вот тут, после подключения своп-файла процесс загрузки останавливается. Когда systemd даёт возможность посмотреть, что происходит, в логе написано следующее:

-- Unit swap.target has finished starting up.
-- 
-- The start-up result is done.
May 09 11:05:50 workmega systemd[1]: Job dev-vg-libvirt_images.device/start timed out.
May 09 11:05:50 workmega systemd[1]: Timed out waiting for device dev-vg-libvirt_images.device.
-- Subject: Unit dev-vg-libvirt_images.device has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-- Documentation: http://www.freedesktop.org/wiki/Software/systemd/catalog/be02cf6855d2428ba40df7e9d022f03d
-- 
-- Unit dev-vg-libvirt_images.device has failed.
-- 
-- The result is timeout.
May 09 11:05:50 workmega systemd[1]: Dependency failed for /var/lib/libvirt/images.
-- Subject: Unit var-lib-libvirt-images.mount has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-- Documentation: http://www.freedesktop.org/wiki/Software/systemd/catalog/be02cf6855d2428ba40df7e9d022f03d
-- 
-- Unit var-lib-libvirt-images.mount has failed.
-- 
-- The result is dependency.
May 09 11:05:50 workmega systemd[1]: Dependency failed for Local File Systems.
-- Subject: Unit local-fs.target has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
-- Documentation: http://www.freedesktop.org/wiki/Software/systemd/catalog/be02cf6855d2428ba40df7e9d022f03d
-- 
-- Unit local-fs.target has failed.

Если попытаться закомментировать строку c /dev/vg/libvirt_images в fstab, то ругаться будет на другой раздел и т.д.

Раньше, как я понял, проблема решалась с помощью специального сервиса из темы по ссылке http://forums.gentoo.org/viewtopic-t-946520-start-0.html , но с нынешним объединённым sys-apps/systemd-203-r1 и udev данное решение не работает.

И как с этим справляться?

 ,

HolyBoy
()

openldap и ACL по objectClass

Форум — Admin

Здравствуйте.

Есть вот такая структура:

dn: o=z
objectclass: organization
objectclass: top
o: z

dn: domainName=example.org,o=z
objectclass: mailDomain
objectclass: top
domainname: example.org

dn: uid=user,domainName=example.org,o=z
objectclass: account
objectclass: mailUser
objectclass: top
mail: user@example.org
uid: user

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

Первый вариант (некрасивый):

Добавляем подветку

dn: ou=Admins,domainName=example.org,o=z
objectclass: organizationalUnit
objectclass: top
ou: Admins

dn: uid=postmaster,ou=Admins,domainName=example.org,o=z
objectclass: account
objectclass: mailAdmin
objectclass: top
mail: postmaster@example.org
uid: postmaster

Пишем ACL:

{0}to attrs=userPassword
	by self write
	by anonymous auth
	by * none
{1}to dn.regex="^(.+,)?(domainName=[^,]+,o=z)$"
	by dn.onelevel,expand="ou=Admins,$2" write
{2}to *
	by self write

В результате, только те, кто находится в поддереве ou=Admin каждого domainName, имеют права на изменения только в в пределах собственной песочницы domainName=[^,]+,o=z .

Второй вариант (как хочется):

Добавляем только аккаунты такого вида:

dn: uid=admin,domainName=example.org,o=z
objectclass: account
objectclass: mailAdmin
objectclass: top
mail: admin@example.org
uid: admin

(см. objectClass) и переписываем этот ACL

{1}to dn.regex="^(.+,)?(domainName=[^,]+,o=z)$"
	by dn.onelevel,expand="ou=Admins,$2" write

к такому виду, который понимает что в ветку dn.regex может писать только такая запись из subtree/onelevel/etc текущего domainName, которая имеет objectClass=mailAdmin.

Итак, вопрос: как это объяснить openldap'у?

 ,

HolyBoy
()

Высокоуровневый API для OpenGL

Форум — Development

Здравствуйте.

Посоветуйте, пожалуйста, что-нибудь высокоуровневое, для рисования объектов типа точек, окружностей и любых других геометрических фигур. Эта штука должна быть достаточно быстрой, чтобы не тормозить при работе с выводом данных из C-программ. GLUT всё-же недостаточно высокого уровня.

Также, хотелось бы, чтобы генерируемый код поддерживал opengl 4.3+

 

HolyBoy
()

Сортировка строк внутри секций конфигурационного файла.

Форум — Development

Здравствуйте.

Есть файл такого формата:

/section1
set parameter1
add parameter 2
set parameter 3
/section2
add parameter1
/section3
set parameter1
…

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

/section1
add parameter 2
set parameter1
set parameter 3
/section2
add parameter1
/section3
set parameter1
…

Порядок секций меняться не должен.

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

Есть ли более быстрые и простые способы получить то, что надо? ЯП: bash/awk

 ,

HolyBoy
()

Courier-IMAP и большие инсталляции.

Форум — Admin

Здравствуйте.

Предполагается работать с большим количеством пользователей, требуется каким-то образом распределить нагрузку. У сабжевого IMAP-сервера есть вот такое:

With its built-in IMAP and POP3 aggregation proxy, the Courier IMAP server has practically infinite horizontal scalability. In a proxy configuration, a pool of Courier servers service initial IMAP and POP3 connections from clients. They wait to receive the client's log in request, look up the server that actually holds this mail account's mailbox, and establish a proxy connection to the server, all in a single, seamless process. Mail accounts can be moved between different servers, to achieve optimum resource usage.

© http://www.courier-mta.org/imap/

Ну и подробности процесса описаны тут: http://www.courier-mta.org/imap/README.proxy.html

Вопросы к тем, кто много работал с данным сервером:

  • Это единственный способ распределения нагрузки?
  • Нет ли каких-то встроенных дополнительных механизмов для обеспечения HA хранилища?
  • Означает ли фраза «Mail accounts can be moved between different servers…», что перемещением аккаунтов между серверами и выбором наименее загруженного/занятого занимается сам Courier автоматически, согласно правилам?

На данный момент у меня сложилось впечатление, что Courier умеет только промежуточное проксирование и проксирование в том случае, если человек подключился не напрямую. Данные о том, на каком сервере лежит аккаунт, берутся из централизованной базы. Перемещать содержимое ящиков между серверами или делать их копию на лету он не умеет. Таким образом, по возможностям в этой области он ничуть не лучше dovecot. Это так?

 , ,

HolyBoy
()

RSS подписка на новые темы