From a271a1b50e9bec07e2ef3a05e38e7285113e4ce6 Mon Sep 17 00:00:00 2001 From: Amit Kapila Date: Mon, 4 Jan 2021 08:34:50 +0530 Subject: Allow decoding at prepare time in ReorderBuffer. This patch allows PREPARE-time decoding of two-phase transactions (if the output plugin supports this capability), in which case the transactions are replayed at PREPARE and then committed later when COMMIT PREPARED arrives. Now that we decode the changes before the commit, the concurrent aborts may cause failures when the output plugin consults catalogs (both system and user-defined). We detect such failures with a special sqlerrcode ERRCODE_TRANSACTION_ROLLBACK introduced by commit 7259736a6e and stop decoding the remaining changes. Then we rollback the changes when rollback prepared is encountered. Author: Ajin Cherian and Amit Kapila based on previous work by Nikhil Sontakke and Stas Kelvich Reviewed-by: Amit Kapila, Peter Smith, Sawada Masahiko, Arseny Sher, and Dilip Kumar Tested-by: Takamichi Osumi Discussion: https://postgr.es/m/02DA5F5E-CECE-4D9C-8B4B-418077E2C010@postgrespro.ru https://postgr.es/m/CAMGcDxeqEpWj3fTXwqhSwBdXd2RS9jzwWscO-XbeCfso6ts3+Q@mail.gmail.com --- doc/src/sgml/logicaldecoding.sgml | 104 +++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) (limited to 'doc/src') diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml index d63f90ff282..cf705ed9cda 100644 --- a/doc/src/sgml/logicaldecoding.sgml +++ b/doc/src/sgml/logicaldecoding.sgml @@ -165,7 +165,58 @@ COMMIT 693 ControlC $ pg_recvlogical -d postgres --slot=test --drop-slot - + + + The following example shows SQL interface that can be used to decode prepared + transactions. Before you use two-phase commit commands, you must set + max_prepared_transactions to at least 1. You must also set + the option 'two-phase-commit' to 1 while calling + pg_logical_slot_get_changes. Note that we will stream + the entire transaction after the commit if it is not already decoded. + + +postgres=# BEGIN; +postgres=*# INSERT INTO data(data) VALUES('5'); +postgres=*# PREPARE TRANSACTION 'test_prepared1'; + +postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1'); + lsn | xid | data +-----------+-----+--------------------------------------------------------- + 0/1689DC0 | 529 | BEGIN 529 + 0/1689DC0 | 529 | table public.data: INSERT: id[integer]:3 data[text]:'5' + 0/1689FC0 | 529 | PREPARE TRANSACTION 'test_prepared1', txid 529 +(3 rows) + +postgres=# COMMIT PREPARED 'test_prepared1'; +postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1'); + lsn | xid | data +-----------+-----+-------------------------------------------- + 0/1689DC0 | 529 | BEGIN 529 + 0/1689DC0 | 529 | table public.data: INSERT: id[integer]:3 data[text]:'5' + 0/1689FC0 | 529 | PREPARE TRANSACTION 'test_prepared1', txid 529 + 0/168A060 | 529 | COMMIT PREPARED 'test_prepared1', txid 529 +(4 row) + +postgres=#-- you can also rollback a prepared transaction +postgres=# BEGIN; +postgres=*# INSERT INTO data(data) VALUES('6'); +postgres=*# PREPARE TRANSACTION 'test_prepared2'; +postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1'); + lsn | xid | data +-----------+-----+--------------------------------------------------------- + 0/168A180 | 530 | BEGIN 530 + 0/168A1E8 | 530 | table public.data: INSERT: id[integer]:4 data[text]:'6' + 0/168A430 | 530 | PREPARE TRANSACTION 'test_prepared2', txid 530 +(3 rows) + +postgres=# ROLLBACK PREPARED 'test_prepared2'; +postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'two-phase-commit', '1'); + lsn | xid | data +-----------+-----+---------------------------------------------- + 0/168A4B8 | 530 | ROLLBACK PREPARED 'test_prepared2', txid 530 +(1 row) + + Logical Decoding Concepts @@ -1126,4 +1177,55 @@ stream_commit_cb(...); <-- commit of the streamed transaction + + + Two-phase commit support for Logical Decoding + + + With the basic output plugin callbacks (eg., begin_cb, + change_cb, commit_cb and + message_cb) two-phase commit commands like + PREPARE TRANSACTION, COMMIT PREPARED + and ROLLBACK PREPARED are not decoded. While the + PREPARE TRANSACTION is ignored, + COMMIT PREPARED is decoded as a COMMIT + and ROLLBACK PREPARED is decoded as a + ROLLBACK. + + + + To support the streaming of two-phase commands, an output plugin needs to + provide additional callbacks. There are multiple two-phase commit callbacks + that are required, (begin_prepare_cb, + prepare_cb, commit_prepared_cb, + rollback_prepared_cb and + stream_prepare_cb) and an optional callback + (filter_prepare_cb). + + + + If the output plugin callbacks for decoding two-phase commit commands are + provided, then on PREPARE TRANSACTION, the changes of + that transaction are decoded, passed to the output plugin, and the + prepare_cb callback is invoked. This differs from the + basic decoding setup where changes are only passed to the output plugin + when a transaction is committed. The start of a prepared transaction is + indicated by the begin_prepare_cb callback. + + + + When a prepared transaction is rollbacked using the + ROLLBACK PREPARED, then the + rollback_prepared_cb callback is invoked and when the + prepared transaction is committed using COMMIT PREPARED, + then the commit_prepared_cb callback is invoked. + + + + Optionally the output plugin can specify a name pattern in the + filter_prepare_cb and transactions with gid containing + that name pattern will not be decoded as a two-phase commit transaction. + + + -- cgit v1.2.3