Перейти к основному содержимому
Нормативная версия документации - английская

Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.

Транзакции

Транзакции, которые крейт открывает для доставки: захват формы блокировки строк и, в транзакционном режиме, транзакция, через которую пишет обработчик, на уровне изоляции таблицы.

Транзакционный режим: транзакция, через которую пишет обработчик в каждой форме, и то, что фиксируется вне еёБлокировка строкАрендаБлокировка ключаTx захватаTx доставкиTx доставкиBEGIN, захватFOR UPDATESKIP LOCKEDSAVEPOINTсразу послезахватаОбработчикпишетчерез Txack: COMMITвместе сзавершением;иначе ROLLBACKTO SAVEPOINTЗахват ставитаренду,COMMITBEGIN науровнетаблицыОбработчикпишетчерез Txack: еслиаренда цела -COMMIT; еслипотеряна -откат всегоБлокировкаключа, взятиестрокиBEGIN насоединенииблокировкиОбработчикпишетчерез Txack: завершитьи COMMIT;иначе откат,затемзавершитьСнятиеблокировки
Схема текстом
  1. Tx захвата: BEGIN, захват FOR UPDATE SKIP LOCKED; SAVEPOINT сразу после захвата; Обработчик пишет через Tx; ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
  2. Tx доставки: BEGIN на уровне таблицы; Обработчик пишет через Tx; ack: если аренда цела - COMMIT; если потеряна - откат всего
  3. Tx доставки: BEGIN на соединении блокировки; Обработчик пишет через Tx; ack: завершить и COMMIT; иначе откат, затем завершить
  4. Блокировка строк: BEGIN, захват FOR UPDATE SKIP LOCKED; SAVEPOINT сразу после захвата; Обработчик пишет через Tx; ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
  5. Аренда: Захват ставит аренду, COMMIT; BEGIN на уровне таблицы; Обработчик пишет через Tx; ack: если аренда цела - COMMIT; если потеряна - откат всего
  6. Блокировка ключа: Блокировка ключа, взятие строки; BEGIN на соединении блокировки; Обработчик пишет через Tx; ack: завершить и COMMIT; иначе откат, затем завершить; Снятие блокировки
  7. BEGIN, захват FOR UPDATE SKIP LOCKED → SAVEPOINT сразу после захвата
  8. SAVEPOINT сразу после захвата → Обработчик пишет через Tx
  9. Обработчик пишет через Tx → ack: COMMIT вместе с завершением; иначе ROLLBACK TO SAVEPOINT
  10. Захват ставит аренду, COMMIT → BEGIN на уровне таблицы
  11. BEGIN на уровне таблицы → Обработчик пишет через Tx
  12. Обработчик пишет через Tx → ack: если аренда цела - COMMIT; если потеряна - откат всего
  13. Блокировка ключа, взятие строки → BEGIN на соединении блокировки
  14. BEGIN на соединении блокировки → Обработчик пишет через Tx
  15. Обработчик пишет через Tx → ack: завершить и COMMIT; иначе откат, затем завершить
  16. ack: завершить и COMMIT; иначе откат, затем завершить → Снятие блокировки

Подписка, смонтированная с .transactional(), предоставляет обработчику транзакцию доставки как Tx. Подтверждение фиксирует записи обработчика вместе с завершением строки, поэтому результаты обработчика и окончание его задачи - одна фиксация. Любой другой исход сначала откатывает записи обработчика, а затем завершает строку так же, как вне транзакционного режима. Строка, которую обработчик вставляет в таблицу очереди через Tx, тоже фиксируется вместе с подтверждением. Запись через пул и публикация через брокер фиксируются сами по себе.

Форма блокировки строк уже держит транзакцию, транзакцию захвата, поэтому её обработчик пишет внутри неё. Подписка ставит после захвата точку сохранения, SAVEPOINT ruststream_claim. Благодаря ей переобработка отбрасывает записи обработчика через ROLLBACK TO SAVEPOINT ruststream_claim и всё равно засчитывает попытку в той же фиксации. Формы аренды и рекомендательной блокировки фиксируют свои захваты сразу, поэтому открывают транзакцию для доставки после захвата. В форме аренды подтверждение вступает в силу, только пока строка всё ещё держит аренду доставки: потерянная аренда откатывает всю транзакцию вместе с записями обработчика, а завершение возвращает ошибку LeaseLost.

Каждая доставка в транзакционном режиме держит одно соединение для своей транзакции, в любой форме. Обработчик пакетов нельзя смонтировать с .transactional(): этот шаг обслуживает одиночные доставки.

Уровни изоляции и режимы​

База данныхБез объявленияУровни, которые может объявить таблица
PostgreSQLBEGIN, уровень сервера по умолчаниюread_committed, repeatable_read, serializable
MySQL, MariaDBREAD COMMITTEDвсе четыре
SQLiteBEGIN, 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.