Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.
Транзакции
Транзакции, которые крейт открывает для доставки: захват формы блокировки строк и, в транзакционном режиме, транзакция, через которую пишет обработчик, на уровне изоляции таблицы.
Схема текстом
- Tx захвата: BEGIN, захват FOR UPDATE SKIP LOCKED; SAVEPOINT сразу после захвата; Обработчик пишет через Tx; ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
- Tx доставки: BEGIN на уровне таблицы; Обработчик пишет через Tx; ack: если аренда цела - COMMIT; если потеряна - откат всего
- Tx доставки: BEGIN на соединении блокировки; Обработчик пишет через Tx; ack: завершить и COMMIT; иначе откат, затем завершить
- Блокировка строк: BEGIN, захват FOR UPDATE SKIP LOCKED; SAVEPOINT сразу после захвата; Обработчик пишет через Tx; ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
- Аренда: Захват ставит аренду, COMMIT; BEGIN на уровне таблицы; Обработчик пишет через Tx; ack: если аренда цела - COMMIT; если потеряна - откат всего
- Блокировка ключа: Блокировка ключа, взятие строки; BEGIN на соединении блокировки; Обработчик пишет через Tx; ack: завершить и COMMIT; иначе откат, затем завершить; Снятие блокировки
- BEGIN, захват FOR UPDATE SKIP LOCKED → SAVEPOINT сразу после захвата
- SAVEPOINT сразу после захвата → Обработчик пишет через Tx
- Обработчик пишет через Tx → ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
- Захват ставит аренду, COMMIT → BEGIN на уровне таблицы
- BEGIN на уровне таблицы → Обработчик пишет через Tx
- Обработчик пишет через Tx → ack: если аренда цела - COMMIT; если потеряна - откат всего
- Блокировка ключа, взятие строки → BEGIN на соединении блокировки
- BEGIN на соединении блокировки → Обработчик пишет через Tx
- Обработчик пишет через Tx → ack: завершить и COMMIT; иначе откат, затем завершить
- ack: завершить и COMMIT; иначе откат, затем завершить → Снятие блокировки
Подписка, смонтированная с .transactional(), предоставляет обработчику транзакцию доставки как
Tx. Подтверждение фиксирует записи обработчика вместе с завершением
строки, поэтому результаты обработчика и окончание его задачи - одна фиксация. Любой другой исход
сначала откатывает записи обработчика, а затем завершает строку так же, как вне транзакционного
режима. Строка, которую обработчик вставляет в таблицу очереди через Tx, тоже фиксируется вместе
с подтверждением. Запись через пул и публикация через брокер фиксируются сами по себе.
Форма блокировки строк уже держит транзакцию, транзакцию захвата, поэтому её обработчик пишет
внутри неё. Подписка ставит после захвата точку сохранения, SAVEPOINT ruststream_claim. Благодаря
ей переобработка отбрасывает записи обработчика через ROLLBACK TO SAVEPOINT ruststream_claim и
всё равно засчитывает попытку в той же фиксации. Формы аренды и рекомендательной блокировки
фиксируют свои захваты сразу, поэтому открывают транзакцию для доставки после захвата. В форме
аренды подтверждение вступает в силу, только пока строка всё ещё держит аренду доставки:
потерянная аренда откатывает всю транзакцию вместе с записями обработчика, а завершение
возвращает ошибку LeaseLost.
Каждая доставка в транзакционном режиме держит одно соединение для своей транзакции, в любой
форме. Обработчик пакетов нельзя смонтировать с .transactional(): этот шаг обслуживает
одиночные доставки.
Уровни изоляции и режимы
| База данных | Без объявления | Уровни, которые может объявить таблица |
|---|---|---|
| PostgreSQL | BEGIN, уровень сервера по умолчанию | read_committed, repeatable_read, serializable |
| MySQL, MariaDB | READ COMMITTED | все четыре |
| SQLite | BEGIN, deferred | режимы: deferred, immediate, exclusive |
Таблица объявляет уровень через isolation = .. или режим SQLite через mode = .., и крейт
открывает на нём захват формы блокировки строк и каждую транзакцию транзакционного режима.
PostgreSQL выполняет READ UNCOMMITTED как READ COMMITTED, поэтому его диалект этот уровень не
открывает, а таблица, которая его объявляет, не компилируется с брокером PostgreSQL. SQLite
выполняет каждую транзакцию как сериализуемую: режим решает, когда транзакция берёт блокировку
записи базы данных, при первой записи или в самом начале. На SQLite транзакция доставки держит эту
блокировку, пока доставка не завершится, и всё это время не пускает других писателей, включая
захваты.
MySQL и MariaDB открывают транзакции на READ COMMITTED, потому что их уровень по умолчанию,
REPEATABLE READ, блокирует промежутки между строками, которые просматривает блокирующее чтение:
захват, удерживаемый на время обработчика, блокировал бы каждую вставку в таблицу до своего
окончания. Таблица блокировки строк на PostgreSQL на уровне repeatable_read или serializable
получает в захватах ошибки сериализации, когда захваты и завершения её строк выполняются
одновременно, поэтому практичный уровень для этой формы там - read_committed. Таблица аренды в
транзакционном режиме на любом из этих двух уровней останавливается при старте на PostgreSQL:
такая транзакция видит строку такой, какой её нашёл первый оператор, и не увидела бы аренду,
которую с тех пор продлила задача продления.
Крейт управляет этими транзакциями сам, а не через тип транзакции sqlx: транзакция, отброшенная открытой, закрывает своё соединение, и сервер её откатывает. Уровни изоляции принадлежат самим базам данных: PostgreSQL, MySQL, SQLite.