-
-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Пулинг соединений для долгоживущих асинхронных приложений #115
Comments
Привет, ответим на след неделе. Извиняюсь за задержку. |
@wolfy-j привет! Есть новости? |
Привет, из-за событий в стране Тикет потерялся. Внесём на след неделю, тут все сложно. |
@wolfy-j удачи вам! |
Привет, грамотным местом будет использовать DatabaseInterface и реализовать свой способ работы с драйверами. https://github.com/spiral/database/blob/master/src/Database.php#L98 Придется заэкспозить transactionLevel из драйвера - https://github.com/spiral/database/blob/master/src/Driver/Driver.php#L79 (можешь сделать ПР для get метода). И не возращать драйвер в пул пока соединение не закрыто. Возможно, придется также сделать свой TransactionRunner - https://github.com/cycle/orm/blob/master/src/Transaction/Runner.php так как он работает с драйверами напрямую. |
@wolfy-j привет! У меня получилось реализовать несколько иначе, без кастомных раннеров и т.д. И у меня возникло несколько вопросов (предложений):
|
Привет, мы используем одну корутину в долгоживущих приложениях (смотри модель РР), но идея очень хорошая.
|
То вот этот тест упадет: https://github.com/spiral/database/blob/master/tests/Database/TransactionsTest.php#L204 , так как заместо идентификатора вернется NULL. |
@wolfy-j привет! Можешь сказать что-нибудь про п.2 #115 (comment) ? |
Привет, сорри заняты новым компонентом очень сильно. Если это убрать то само собой last insert id будет неопределенным в некоторых случаях. Надо копать, я пока не понимаю как это мешает пуллингу. |
@wolfy-j это не особо мешает пулингу, проблема в том, то что на Last insert ID не стоит завязываться. Т.е. вроде как ОРМка строит запрос с RETURNING, а по факту получается, что какой-то кусок кода тянет из lastInsertId IDШник. И если в своем драйвере как бы "выпилить" этот кусок с Last insert ID ломается ORMка, т.к. у сущности заместо ID становится NULL. |
В PG драйвере lastInsertID эмулируется через RETURNING, этот список нужен чтобы знать что возвращать. ОРМ же не знает она работает с PG или не с PG. |
@wolfy-j ага, теперь понял. Спасибо! |
Сдалать свою команду, returning не должен переписывать ваши значения. |
@wolfy-j все же касательно этого поста #115 (comment) , на самом деле это мешает пулингу из-за конкурентного доступа к данным. Пока одна корутина может читать $this->primaryKeys, другая может вызвать resetPrimarykeys. Я бы все же как-то пересмотрел этот дизайн, т.к. каждый раз это плодит кучу SQL запросов, что безусловно будет дико аффектить производительность приложений. |
Врядли в момент работы базы данные PK изменятся, так что лок это нормальное решение. Я правда не копал как они работают в PHP, но в другом языке я бы сделал так же. |
@wolfy-j чтоб не плодить топики, хочу спросить кое-что про Как ты считаешь, а не будет ли хорошей идеей, в ConstrainException собственно добавить такие параметры как SQLSTATE и т.д.? Тогда коду не нужно будет полагаться на более низлежащий слой реализаций конкретных драйверов. P.S. возможно также стоит рассмотреть вариант не сырым SQLSTATE, а что-то более высокоуровневое, например какой-нибудь enum, а-ля UNIQUE_VIOLATION, FOREIGN_KEY_VIOLATION и т.д. |
@wolfy-j чтоб было понятно, мне приходится делать вот так: $t = new Transaction($this->orm);
$t->persist($entity);
try {
$t->run();
} catch (ConstrainException $e) {
$prev = $e->getPrevious();
if ($prev instanceof QueryExecutionError) {
$sqlState = $prev->getDiagnostics()['sqlstate'] ?? '';
if ('23505' === $sqlState) {
throw new SynonymAlreadyExist($synonym);
}
if ('23503' === $sqlState) {
throw new CompanyNotFoundException($companyId);
}
}
throw $e;
} Думаю этот пример отлично иллюстрирует проблему |
Там уже заложен механизм типизации ошибок, возможно стоит его расширить. Я не против, чем более очевидные ошибки - тем лучше. |
@SerafimArts что думаешь? |
@wolfy-j у меня недостаточно опыта что бы давать конструктивные комментарии в рамках Цикла. А на счёт расширения ошибок - вполне разумно, т.к. в PHP try/catch завязан на типы, а не на коды и тем более |
Привет! Это прекрасная ORM, но немного не понятно, как ее использовать в контексте асинхронных приложений. Речь идет про Swoole/AMPHP/ReactPHP. Хотя речь идет скорее про Swoole, про корутиновую однопоточную модель исполнения.
Асинхронные приложения работают не совсем в констекте 1 запрос = 1 ответ, во время выполнения запроса может понадобиться выполнить какие-нибудь I/O операции, например пойти в БД за какими-нибудь данными. И заместо того, чтобы "зависнуть" на получении данных от БД - асинхронное приложение начинает обрабатывать следующий запрос.
PDO с таким (асинхронным) подходом конечно же несовместим (Swoole умеет делать PDO MySQL неблокирующим, но не PDO PGSQL).
Поэтому я написал свой драйвер - https://github.com/makise-co/postgres (вдохновившись драйвером от AMPHP, что является максимально близким по концепции к Swoole)
И написал адаптер этого драйвера для пакета
spiral/database
- https://github.com/makise-co/postgres-spiral-driverУ Postgres есть ограничение, что с 1 соединения нельзя выполнять более 1 запроса одновременно, поэтому встает вопрос о пулинге соединений.
Я не могу понять, как правильно реализовать пулинг в рамках ORM.
С одной стороны задача пулить соединения лежит на DBAL, а с другой стороны ORM может хранить у себя какие-то внутренние состояния, которые завязаны на конкретное соединение, которое в асинхронном приложение уже может быть занято обработкой других задач.
Плюс, пулить нужно грамотно, например при выполнении транзакций соединение не должно возвращаться в пул, а должно оставаться занятым до тех пор, пока транзакция не будет закоммичена или роллбекнута или возникнет ошибка соединения при работе с транзакцией.
Поэтому у меня возникает вопрос - Где и как правильно реализовать пулинг соединений, чтобы не столкнуться с багами и неправильным поведением?
Вне ORM и
database/spiral
я использую подход, который именую - LazyConnection.Как и в
database/spiral
(DriverInterface) у меня есть ConnectionInterface.Структура получается такая:
LazyConnection скрывает под собой работу с пулом соединений (пример можно увидеть тут - https://github.com/makise-co/framework/blob/master/src/Database/Connection/LazyConnection.php)
А сами репозитории, дабы исключить проблему конкурентности запросов каждый раз обращаются к DBAL с просьбой выдать им новый LazyConnection (https://github.com/makise-co/framework/blob/master/src/Database/DatabaseManager.php#L48).
The text was updated successfully, but these errors were encountered: