Помните, мы когда-то писали телефонную книгу на Haskell и прикручивали к ней веб-интерфейс ? У этой телефонной книги есть существенный недостаток. Дело в том, что соединение с базой данных устанавливается только один раз, при запуске приложения. Если во время работы программы соединение порвется, для его восстановления телефонную книгу придется перезапустить. Кроме того, наличие всего лишь одного соединения делает параллельную обработку нескольких запросов в лучшем случае невозможным, а в худшем — чреватой ошибками. Сегодня мы этот ужасный недостаток устраним!

Для решения данной проблемы есть пакет resource-pool за авторством уже хорошо знакомого нам Bryan O’Sullivan . В пакете находится один-единственный модуль Data.Pool, который экспортирует две основных функции:

createPool
:: IO a — функция создания нового ресурса
-> ( a -> IO ( ) ) — функция уничтожения ресурса
-> Int — количество stripe-ов, см ниже
-> NominalDiffTime — сколько держать неиспользуемый ресурс открытым
-> Int — максимальное количество ресурсов на stripe
-> IO ( Pool a ) — возвращает пул

Здесь все должно быть предельно ясно, за исключением разве что понятия stripe. Дело в том, что когда вы пишите многопоточное приложение , все потоки могут упереться в пул соединений. Очевидное решение заключается в том, чтобы завести несколько пулов и сделать шардинг, например, по id потока. Так вот, страйп — это и есть один из пулов, между которыми производится шардинг.

Теперь посмотрим, как производится работа с ресурсами из пула:

withResource :: MonadBaseControl IO m => Pool a -> ( a -> m b ) -> m b

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

Для того, чтобы в телефонной книге появился пул соединений, пришлось внести совсем немного изменений. Во-первых, был изменен модуль Phonebook.Utils. Например, функция connectAndRun стала выглядеть так:

connectAndRun fun = do
( cs , s , t , c ) <- getConnectParams
pool <- createPool ( connectPostgreSQL cs ) disconnect s t c
fun pool

Как и раньше, все параметры подключения к базе данных, включая количество страйпов и другие свойства пула, можно поменять в конфиге . Во-вторых, поскольку между функциями больше не передается само соединение к базе данных, потребовалось переписать модули Phonebook.Interface.HTTP и Phonebook.Interface.CLI так, чтобы соединение извлекалось из пула на время выполнения каждого запроса. Например:

read pool = do
contacts <- liftIO $ withResource pool St . read
Sc . json $ map toObj contacts
where
toObj :: ( St . ContactId , St . Name , St . Phone ) -> Value
toObj ( cid , name , phone ) =
object [ «contactId» .= cid
, «name» .= name
, «phone» .= phone
]

Ну вот и все! Несложно убедиться, что теперь приложение спокойно переживает временную остановку PostgreSQL , и как ни в чем не бывало продолжает работу, когда сервер вновь становится доступен. Очевидно, пакет resource-pool можно использовать не только для баз данных .

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

admin

Share
Published by
admin

Recent Posts

Настройка Mercurial по HTTPS

Иногда mercurial, при скачивании и комитах ругается на https. Особенно на самоподписанные сертификаты. Чтобы заработал…

2 недели ago

Компиляция libvirt в Ubuntu

Компиляция libvirt Подробнее

2 недели ago

Ошибка libvirt permission denied: решение

Если возникает ошибка libvirt destroy lxc permission denied , при попытке остановить контейнер: _x000D_# virsh…

2 недели ago

Настройка редиректа в NGINX

Файлы с примерами редиректа для nginx Подробнее

2 недели ago

Включение gzip в NGINX

Как включить gzip сжатие в Nginx ? Подробнее

2 недели ago

Удаление postinst-скрипта в Linux

Иногда возникает ситуация, когда криво настроенные пакеты не устанавливаются в системе. У меня это произошло…

2 недели ago