Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 072ee84

Browse files
Skip logical decoding of already-aborted transactions.
Previously, transaction aborts were detected concurrently only during system catalog scans while replaying a transaction in streaming mode. This commit adds an additional CLOG lookup to check the transaction status, allowing the logical decoding to skip changes also when it doesn't touch system catalogs, if the transaction is already aborted. This optimization enhances logical decoding performance, especially for large transactions that have already been rolled back, as it avoids unnecessary disk or network I/O. To avoid potential slowdowns caused by frequent CLOG lookups for small transactions (most of which commit), the CLOG lookup is performed only for large transactions before eviction. The performance benchmark results showed there is not noticeable performance regression due to CLOG lookups. Reviewed-by: Amit Kapila, Peter Smith, Vignesh C, Ajin Cherian Reviewed-by: Dilip Kumar, Andres Freund Discussion: https://postgr.es/m/CAD21AoDht9Pz_DFv_R2LqBTBbO4eGrpa9Vojmt5z5sEx3XwD7A@mail.gmail.com
1 parent 9e66a2b commit 072ee84

File tree

6 files changed

+246
-46
lines changed

6 files changed

+246
-46
lines changed

contrib/test_decoding/expected/stats.out

+38-4
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,46 @@ SELECT slot_name FROM pg_stat_replication_slots;
138138
(3 rows)
139139

140140
COMMIT;
141+
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats4_twophase', 'test_decoding', false, true) s4;
142+
?column?
143+
----------
144+
init
145+
(1 row)
146+
147+
-- The INSERT changes are large enough to be spilled but will not be, because
148+
-- the transaction is aborted. The logical decoding skips collecting further
149+
-- changes too. The transaction is prepared to make sure the decoding processes
150+
-- the aborted transaction.
151+
BEGIN;
152+
INSERT INTO stats_test SELECT 'serialize-toobig--1:'||g.i FROM generate_series(1, 5000) g(i);
153+
PREPARE TRANSACTION 'test1_abort';
154+
ROLLBACK PREPARED 'test1_abort';
155+
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats4_twophase', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
156+
count
157+
-------
158+
1
159+
(1 row)
160+
161+
-- Verify that the decoding doesn't spill already-aborted transaction's changes.
162+
SELECT pg_stat_force_next_flush();
163+
pg_stat_force_next_flush
164+
--------------------------
165+
166+
(1 row)
167+
168+
SELECT slot_name, spill_txns, spill_count FROM pg_stat_replication_slots WHERE slot_name = 'regression_slot_stats4_twophase';
169+
slot_name | spill_txns | spill_count
170+
---------------------------------+------------+-------------
171+
regression_slot_stats4_twophase | 0 | 0
172+
(1 row)
173+
141174
DROP TABLE stats_test;
142175
SELECT pg_drop_replication_slot('regression_slot_stats1'),
143176
pg_drop_replication_slot('regression_slot_stats2'),
144-
pg_drop_replication_slot('regression_slot_stats3');
145-
pg_drop_replication_slot | pg_drop_replication_slot | pg_drop_replication_slot
146-
--------------------------+--------------------------+--------------------------
147-
| |
177+
pg_drop_replication_slot('regression_slot_stats3'),
178+
pg_drop_replication_slot('regression_slot_stats4_twophase');
179+
pg_drop_replication_slot | pg_drop_replication_slot | pg_drop_replication_slot | pg_drop_replication_slot
180+
--------------------------+--------------------------+--------------------------+--------------------------
181+
| | |
148182
(1 row)
149183

contrib/test_decoding/expected/stream.out

+6
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,12 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'incl
114114
* detect that the subtransaction was aborted, and reset the transaction while having
115115
* the TOAST changes in memory, resulting in deallocating both decoded changes and
116116
* TOAST reconstruction data. Memory usage counters must be updated correctly.
117+
*
118+
* Set debug_logical_replication_streaming to 'immediate' to disable the transaction
119+
* status check happening before streaming the second insertion, so we can detect a
120+
* concurrent abort while streaming.
117121
*/
122+
SET debug_logical_replication_streaming = immediate;
118123
BEGIN;
119124
INSERT INTO stream_test SELECT repeat(string_agg(to_char(g.i, 'FM0000'), ''), 50) FROM generate_series(1, 500) g(i);
120125
ALTER TABLE stream_test ADD COLUMN i INT;
@@ -128,6 +133,7 @@ SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL,
128133
5
129134
(1 row)
130135

136+
RESET debug_logical_replication_streaming;
131137
DROP TABLE stream_test;
132138
SELECT pg_drop_replication_slot('regression_slot');
133139
pg_drop_replication_slot

contrib/test_decoding/sql/stats.sql

+19-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,25 @@ SELECT slot_name FROM pg_stat_replication_slots;
5050
SELECT slot_name FROM pg_stat_replication_slots;
5151
COMMIT;
5252

53+
54+
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_stats4_twophase', 'test_decoding', false, true) s4;
55+
56+
-- The INSERT changes are large enough to be spilled but will not be, because
57+
-- the transaction is aborted. The logical decoding skips collecting further
58+
-- changes too. The transaction is prepared to make sure the decoding processes
59+
-- the aborted transaction.
60+
BEGIN;
61+
INSERT INTO stats_test SELECT 'serialize-toobig--1:'||g.i FROM generate_series(1, 5000) g(i);
62+
PREPARE TRANSACTION 'test1_abort';
63+
ROLLBACK PREPARED 'test1_abort';
64+
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot_stats4_twophase', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
65+
66+
-- Verify that the decoding doesn't spill already-aborted transaction's changes.
67+
SELECT pg_stat_force_next_flush();
68+
SELECT slot_name, spill_txns, spill_count FROM pg_stat_replication_slots WHERE slot_name = 'regression_slot_stats4_twophase';
69+
5370
DROP TABLE stats_test;
5471
SELECT pg_drop_replication_slot('regression_slot_stats1'),
5572
pg_drop_replication_slot('regression_slot_stats2'),
56-
pg_drop_replication_slot('regression_slot_stats3');
73+
pg_drop_replication_slot('regression_slot_stats3'),
74+
pg_drop_replication_slot('regression_slot_stats4_twophase');

contrib/test_decoding/sql/stream.sql

+6
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL, 'incl
4949
* detect that the subtransaction was aborted, and reset the transaction while having
5050
* the TOAST changes in memory, resulting in deallocating both decoded changes and
5151
* TOAST reconstruction data. Memory usage counters must be updated correctly.
52+
*
53+
* Set debug_logical_replication_streaming to 'immediate' to disable the transaction
54+
* status check happening before streaming the second insertion, so we can detect a
55+
* concurrent abort while streaming.
5256
*/
57+
SET debug_logical_replication_streaming = immediate;
5358
BEGIN;
5459
INSERT INTO stream_test SELECT repeat(string_agg(to_char(g.i, 'FM0000'), ''), 50) FROM generate_series(1, 500) g(i);
5560
ALTER TABLE stream_test ADD COLUMN i INT;
@@ -58,6 +63,7 @@ INSERT INTO stream_test(data, i) SELECT repeat(string_agg(to_char(g.i, 'FM0000')
5863
ROLLBACK TO s1;
5964
COMMIT;
6065
SELECT count(*) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
66+
RESET debug_logical_replication_streaming;
6167

6268
DROP TABLE stream_test;
6369
SELECT pg_drop_replication_slot('regression_slot');

0 commit comments

Comments
 (0)