Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

Улучшение работы с транзакциями #56

Open
codercms opened this issue Oct 23, 2020 · 0 comments
Open

Улучшение работы с транзакциями #56

codercms opened this issue Oct 23, 2020 · 0 comments

Comments

@codercms
Copy link

codercms commented Oct 23, 2020

Интро

В long-running приложениях (асинхронных/многопоточных/многопоточно-асинхронных) часто применяется подход пулинга соединений (ConnectionPool). К БД открывается N соединений и пока приложение дожидается результатов от БД, оно может начать выполнять параллельно другие запросы к БД, используя свободные соединия из пула, что очень сильно увеличивает производительность такого приложения.

При использовании пула соединений очень важно правильно управлять транзакциями БД, а именно: Соединения, на котором выполняется транзакция не должно быть возвращено обратно в пул до тех пор, пока транзакция не будет завершена/отменена. Это гарантирует консистентность данных, и что другой код не начнет выполнять свои запросы в чужой транзакции.
Для этого в таких языках как Go/NodeJS используется механизм создания "объекта" транзакции.
Пример псевдокода на PHP:

// Забрали соединение из пула и инициировали транзакцию
$transaction = $pool->createTransaction($isolationLevel);

// Запрос выполняется на другом соединии, которое не занято исполнением транзакции
$pool->query('SELECT 1');

// Выполняем запросы внутри транзакции
$transaction->query('INSERT INTO table (col1, col2) VALUES (?, ?)', $val1, $val2);
$transaction->query('UPDATE table SET col1 = ? WHERE col2 = ?', $newVal, $id);
$transaction->query('DELETE FROM table WHERE col2 = ?', $id);

$rows = $transaction->query('SELECT * FROM table');
foreach ($rows as $row) {
    // ...
}

// Закоммитили транзакцию, объект транзакции более недействителен, соединение вернулось обратно в пул
$transaction->commit();
// $transaction->rollback();
// $transaction->createSavepoint(); и т.д.

Проблематика

Сейчас данный компонент не умеет выполнять запросы на конкретной транзакции, т.к. не рассчитан на то, что не будет применяться пулинг соединений. Драйвер, который реализует пул соединений вынужден хранить внутреннее состояние и ограничивать возможности запуска нескольких транзакций в рамках одной ветки исполнения кода.
Внутреннее состояние - это какой корутине (либо же ветви кода), принадлежит транзакция.
И основываясь на идентификаторе корутины драйвер должен сам определять - нужно выполнить запрос в транзакции или же на свободном соединении из пула.

Предложение

Контракт DriverInterface имеет следующую сигнатуру: public function beginTransaction(string $isolationLevel = null): bool.

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

Почему бы не реализовать новый метод - createTransaction(string $isolationLevel = null): Transaction ?
(@wolfy-j возможно ты еще оставишь комментарии по этому поводу.)
Который бы использоваться для создания транзакций на тех драйверах, которые поддерживают пулинг соединеий.
А также научить данный компонент работать с такими объетами транзакций (выполнять на них запросы).

Связанная issue: cycle/orm#115

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants