Skip to content

Commit 524dbc1

Browse files
committed
Avoid superfluous work for commits during logical slot creation.
Before 955a684 logical decoding snapshot maintenance needed to cope with transactions it might not have seen in their entirety. For such transactions we'd to assume they modified the catalog (could have happened before we were watching), and thus a new snapshot had to be built, and distributed to concurrently running transactions. That's problematic because building a new snapshot isn't that cheap , especially as the the array of committed transactions needs to be sorted. When creating a slot on a server with a lot of transactions, this could make logical slot creation infeasibly expensive. After 955a684 there's no need to deal with transaction that aren't guaranteed to be fully observable. That allows to avoid building snapshots for transactions that haven't modified catalog, even before reaching consistency. While this isn't necessarily a bugfix, slot creation being impossible in some production workloads, is severe enough to warrant backpatching. Author: Andres Freund, based on a quite different patch from Petr Jelinek Analyzed-By: Petr Jelinek Reviewed-By: Petr Jelinek Discussion: https://postgr.es/m/f37e975c-908f-858e-707f-058d3b1eb214@2ndquadrant.com Backpatch: 9.4-, where logical decoding has been introduced
1 parent 955a684 commit 524dbc1

File tree

1 file changed

+68
-56
lines changed

1 file changed

+68
-56
lines changed

src/backend/replication/logical/snapbuild.c

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -929,105 +929,122 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
929929
{
930930
int nxact;
931931

932-
bool forced_timetravel = false;
932+
bool needs_snapshot = false;
933+
bool needs_timetravel = false;
933934
bool sub_needs_timetravel = false;
934-
bool top_needs_timetravel = false;
935935

936936
TransactionId xmax = xid;
937937

938938
/*
939-
* If we couldn't observe every change of a transaction because it was
940-
* already running at the point we started to observe we have to assume it
941-
* made catalog changes.
942-
*
943-
* This has the positive benefit that we afterwards have enough
944-
* information to build an exportable snapshot that's usable by pg_dump et
945-
* al.
939+
* Transactions preceding BUILDING_SNAPSHOT will neither be decoded, nor
940+
* will they be part of a snapshot. So we don't need to record anything.
946941
*/
942+
if (builder->state == SNAPBUILD_START ||
943+
(builder->state == SNAPBUILD_BUILDING_SNAPSHOT &&
944+
TransactionIdPrecedes(xid, SnapBuildNextPhaseAt(builder))))
945+
{
946+
/* ensure that only commits after this are getting replayed */
947+
if (builder->start_decoding_at <= lsn)
948+
builder->start_decoding_at = lsn + 1;
949+
return;
950+
}
951+
947952
if (builder->state < SNAPBUILD_CONSISTENT)
948953
{
949954
/* ensure that only commits after this are getting replayed */
950955
if (builder->start_decoding_at <= lsn)
951956
builder->start_decoding_at = lsn + 1;
952957

953958
/*
954-
* We could avoid treating !SnapBuildTxnIsRunning transactions as
955-
* timetravel ones, but we want to be able to export a snapshot when
956-
* we reached consistency.
959+
* If building an exportable snapshot, force xid to be tracked, even
960+
* if the transaction didn't modify the catalog.
957961
*/
958-
forced_timetravel = true;
959-
elog(DEBUG1, "forced to assume catalog changes for xid %u because it was running too early", xid);
962+
if (builder->building_full_snapshot)
963+
{
964+
needs_timetravel = true;
965+
}
960966
}
961967

962968
for (nxact = 0; nxact < nsubxacts; nxact++)
963969
{
964970
TransactionId subxid = subxacts[nxact];
965971

966972
/*
967-
* If we're forcing timetravel we also need visibility information
968-
* about subtransaction, so keep track of subtransaction's state.
973+
* Add subtransaction to base snapshot if catalog modifying, we don't
974+
* distinguish to toplevel transactions there.
969975
*/
970-
if (forced_timetravel)
976+
if (ReorderBufferXidHasCatalogChanges(builder->reorder, subxid))
971977
{
978+
sub_needs_timetravel = true;
979+
needs_snapshot = true;
980+
981+
elog(DEBUG1, "found subtransaction %u:%u with catalog changes",
982+
xid, subxid);
983+
972984
SnapBuildAddCommittedTxn(builder, subxid);
985+
973986
if (NormalTransactionIdFollows(subxid, xmax))
974987
xmax = subxid;
975988
}
976-
977989
/*
978-
* Add subtransaction to base snapshot if it DDL, we don't distinguish
979-
* to toplevel transactions there.
990+
* If we're forcing timetravel we also need visibility information
991+
* about subtransaction, so keep track of subtransaction's state, even
992+
* if not catalog modifying. Don't need to distribute a snapshot in
993+
* that case.
980994
*/
981-
else if (ReorderBufferXidHasCatalogChanges(builder->reorder, subxid))
995+
else if (needs_timetravel)
982996
{
983-
sub_needs_timetravel = true;
984-
985-
elog(DEBUG1, "found subtransaction %u:%u with catalog changes.",
986-
xid, subxid);
987-
988997
SnapBuildAddCommittedTxn(builder, subxid);
989-
990998
if (NormalTransactionIdFollows(subxid, xmax))
991999
xmax = subxid;
9921000
}
9931001
}
9941002

995-
if (forced_timetravel)
1003+
/* if top-level modified catalog, it'll need a snapshot */
1004+
if (ReorderBufferXidHasCatalogChanges(builder->reorder, xid))
9961005
{
997-
elog(DEBUG2, "forced transaction %u to do timetravel.", xid);
998-
1006+
elog(DEBUG2, "found top level transaction %u, with catalog changes",
1007+
xid);
1008+
needs_snapshot = true;
1009+
needs_timetravel = true;
9991010
SnapBuildAddCommittedTxn(builder, xid);
10001011
}
1001-
/* add toplevel transaction to base snapshot */
1002-
else if (ReorderBufferXidHasCatalogChanges(builder->reorder, xid))
1012+
else if (sub_needs_timetravel)
10031013
{
1004-
elog(DEBUG2, "found top level transaction %u, with catalog changes!",
1005-
xid);
1006-
1007-
top_needs_timetravel = true;
1014+
/* track toplevel txn as well, subxact alone isn't meaningful */
10081015
SnapBuildAddCommittedTxn(builder, xid);
10091016
}
1010-
else if (sub_needs_timetravel)
1017+
else if (needs_timetravel)
10111018
{
1012-
/* mark toplevel txn as timetravel as well */
1019+
elog(DEBUG2, "forced transaction %u to do timetravel", xid);
1020+
10131021
SnapBuildAddCommittedTxn(builder, xid);
10141022
}
10151023

1016-
/* if there's any reason to build a historic snapshot, do so now */
1017-
if (forced_timetravel || top_needs_timetravel || sub_needs_timetravel)
1024+
if (!needs_timetravel)
10181025
{
1019-
/*
1020-
* Adjust xmax of the snapshot builder, we only do that for committed,
1021-
* catalog modifying, transactions, everything else isn't interesting
1022-
* for us since we'll never look at the respective rows.
1023-
*/
1024-
if (!TransactionIdIsValid(builder->xmax) ||
1025-
TransactionIdFollowsOrEquals(xmax, builder->xmax))
1026-
{
1027-
builder->xmax = xmax;
1028-
TransactionIdAdvance(builder->xmax);
1029-
}
1026+
/* record that we cannot export a general snapshot anymore */
1027+
builder->committed.includes_all_transactions = false;
1028+
}
1029+
1030+
Assert(!needs_snapshot || needs_timetravel);
10301031

1032+
/*
1033+
* Adjust xmax of the snapshot builder, we only do that for committed,
1034+
* catalog modifying, transactions, everything else isn't interesting
1035+
* for us since we'll never look at the respective rows.
1036+
*/
1037+
if (needs_timetravel &&
1038+
(!TransactionIdIsValid(builder->xmax) ||
1039+
TransactionIdFollowsOrEquals(xmax, builder->xmax)))
1040+
{
1041+
builder->xmax = xmax;
1042+
TransactionIdAdvance(builder->xmax);
1043+
}
1044+
1045+
/* if there's any reason to build a historic snapshot, do so now */
1046+
if (needs_snapshot)
1047+
{
10311048
/*
10321049
* If we haven't built a complete snapshot yet there's no need to hand
10331050
* it out, it wouldn't (and couldn't) be used anyway.
@@ -1059,11 +1076,6 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
10591076
/* add a new Snapshot to all currently running transactions */
10601077
SnapBuildDistributeNewCatalogSnapshot(builder, lsn);
10611078
}
1062-
else
1063-
{
1064-
/* record that we cannot export a general snapshot anymore */
1065-
builder->committed.includes_all_transactions = false;
1066-
}
10671079
}
10681080

10691081

0 commit comments

Comments
 (0)