LINUX.ORG.RU

perl async postgresql module

 , , , ,


1

1

Привет

Помогите, пожалуйста, придумать логику для модуля perl асинхронных запросов к БД. Почти всё получилось сделать как задумал, но внезапно выявилась архитектурная неопределённость.

Модуль будет работать только с postgres с помощью фичей DBD::Pg (http://search.cpan.org/~turnstep/DBD-Pg-2.19.3/Pg.pm#Asynchronous_Queries) и фреймворка AnyEvent.

Примерный сценарий использования:

use mk::adb;
my $adb=new mk::adb(recv=>["dbi:Pg:dbname=disarmer;host=/run/postgresql/;"]);
my $str1=$adb->q("select 1,pg_sleep(1);");#какой-то долгий запрос, $str1 - на самом деле не строка, а объект promise
my $str2=$adb->q("select 2,pg_sleep(2);");
... #выполняем какие то еще задачи
say "Result 1=$str1, result 2=$str2"; #Возвращаем результаты из БД, при преобразовании в promise в строку возвращаются нужные результаты запросов в виде строки

Суть такова: модуль при инициализации инстанса подключается к postgres с помощью обычного DBI несколькими подключениями (из аргументов). Затем при создании запрос попадает в очередь объекта (метод q), и если есть незанятый коннект к базе, запрос сразу отправляется в БД с флагом PG_ASYNC, на соответствующий сокет вешается AnyEvent watcher, управление возвращается программе, вместе с будущим результатом запроса (future/promise модель). То есть создание запроса происходит практически мгновенно, без блокирования.

После выполнения запроса (по срабатыванию watcher) вызывается callback, получающий строки результата и обрабатывающий их с помощью переданной в методе q функции. Если запрос еще не закончен, но больше делать нечего, то при вызове метода promise->finalize, выполнение блокируется, до получения результатов по нужному запросу($sth->pg_result).

Вот такой код получился на данный момент: http://disarmer.ru/ln/?li, вполне работает как задумано, если только число запросов не превышает числа коннектов к базе. Иначе возникает проблема: например ставим в очередь 10 долгих запросов и пытаемся получить результат последнего, а он еще не начал выполняться. По задумке тут нужно блокироваться пока БД не ответит на все предыдущие запросы, но как это красиво сделать не понимаю.

Да, про опасность слушания сокета к БД знаю (может прийти не только результат запроса а какие нибудь NOTIFY). В этом случае случится возможно долгая блокировка, но вроде без использования NOTIFY такого не должно происходить.

Самый похожий из имеющихся на cpan: AnyEvent::Pg::Pool, но он использует какой-то свой неповторимый XS драйвер к БД, и его использование вызывает много варнингов.

Первое, что надо уяснить. Эта асинхронность в твоем приложении нужна? Ты точно уверен, что в момент пока отложен запрос к бд происходят важные вычисления?

Второе. Тот вариант, который делает много запросов поверх одного соединения.... не возможен. Так сделан DBI. А все строится поверх него. DBI писали для форк-модели процессов. Чтобы его поведение изменить надо переписать основу основ.

Поэтому в контексте AnyEvent возможна только одна реализация: форк-процессы. Тогда очередь запросов будет похожа на ту, что ты хочешь. И всеравно все будет блокируемо внутри одного процесса.

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

gh0stwizard ★★★★★ ()
Ответ на: комментарий от gh0stwizard

Эта асинхронность в твоем приложении нужна?

Думаю да. Есть несколько мест, где это бы позволило обрабатывать другие задачи во время относительно долгого запроса к БД (не такие тяжёлые запросы, просто приходиться лезть за историческими данными на диск, соответственно CPU простаивает). Плюс, хочу попробовать заменить синхронную модель с несколькими процессами fastcgi на асинхронную с одним-двумя процессами (не то чтобы жмёт - больше в исследовательских целях) - получается это единственное место, которое не поддаётся.

Тот вариант, который делает много запросов поверх одного соединения.... не возможен. Так сделан DBI.

Да, не только DBI, протокол общения с базой тоже не позволяет (максимум неблокирующее api). Но это - не большая проблема, можно открыть несколько коннектов к базе в одном процессе.

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

Думаю для простоты и изоляции. Плюс для хранения результатов асинхронных запросов понадобилось бы больше памяти. И это сильно усложнило бы протокол.

Я уже и асинхронное middleware какое-нибудь искал. Странно, что pgpool не предоставляет какого-нибудь api, казалось бы он сам асинхронный.

disarmer ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.