On Thu, 15 Apr 2021 at 19:25, Amit Kapila <amit.kapil...@gmail.com> wrote: > On Thu, Apr 15, 2021 at 4:30 PM Japin Li <japi...@hotmail.com> wrote: >> >> >> On Thu, 15 Apr 2021 at 18:22, Amit Kapila <amit.kapil...@gmail.com> wrote: >> > On Wed, Apr 14, 2021 at 3:31 PM Amit Kapila <amit.kapil...@gmail.com> >> > wrote: >> >> >> >> On Tue, Apr 13, 2021 at 8:07 PM Petr Jelinek >> >> <petr.jeli...@enterprisedb.com> wrote: >> >> > >> >> > > On 12 Apr 2021, at 08:58, Amit Kapila <amit.kapil...@gmail.com> wrote: >> >> > > >> >> > > The problem happens only when we try to fetch IDENTITY_KEY attributes >> >> > > because pgoutput uses RelationGetIndexAttrBitmap() to get that >> >> > > information which locks the required indexes. Now, because TRUNCATE >> >> > > has already acquired an exclusive lock on the index, it seems to >> >> > > create a sort of deadlock where the actual Truncate operation waits >> >> > > for logical replication of operation to complete and logical >> >> > > replication waits for actual Truncate operation to finish. >> >> > > >> >> > > Do we really need to use RelationGetIndexAttrBitmap() to build >> >> > > IDENTITY_KEY attributes? During decoding, we don't even lock the main >> >> > > relation, we just scan the system table and build that information >> >> > > using a historic snapshot. Can't we do something similar here? >> >> > > >> >> > > Adding Petr J. and Peter E. to know their views as this seems to be an >> >> > > old problem (since the decoding of Truncate operation is introduced). >> >> > >> >> > We used RelationGetIndexAttrBitmap because it already existed, no other >> >> > reason. >> >> > >> >> >> >> Fair enough. But I think we should do something about it because using >> >> the same (RelationGetIndexAttrBitmap) just breaks the synchronous >> >> logical replication. I think this is broken since the logical >> >> replication of Truncate is supported. >> >> >> >> > I am not sure what exact locking we need but I would have guessed at >> >> > least AccessShareLock would be needed. >> >> > >> >> >> >> Are you telling that we need AccessShareLock on the index? If so, why >> >> is it different from how we access the relation during decoding >> >> (basically in ReorderBufferProcessTXN, we directly use >> >> RelationIdGetRelation() without any lock on the relation)? I think we >> >> do it that way because we need it to process WAL entries and we need >> >> the relation state based on the historic snapshot, so, even if the >> >> relation is later changed/dropped, we are fine with the old state we >> >> got. Isn't the same thing applies here in logicalrep_write_attrs? If >> >> that is true then some equivalent of RelationGetIndexAttrBitmap where >> >> we use RelationIdGetRelation instead of index_open should work? >> >> >> > >> > Today, again I have thought about this and don't see a problem with >> > the above idea. If the above understanding is correct, then I think >> > for our purpose in pgoutput, we just need to call RelationGetIndexList >> > and then build the idattr list for relation->rd_replidindex. >> >> Sorry, I don't know how can we build the idattr without open the index. >> If we should open the index, then we should use NoLock, since the TRUNCATE >> side hold AccessExclusiveLock. As Osumi points out in [1], The NoLock mode >> assumes that the appropriate lock on the index is already taken. >> > > Why can't we use RelationIdGetRelation() by passing the required > indexOid to it?
Hi Amit, as your suggested, I try to use RelationIdGetRelation() replace the index_open() to avoid specify the AccessSharedLock, then the TRUNCATE will not be blocked. Attached patch fixed it. Thoughts? -- Regrads, Japin Li. ChengDu WenWu Information Technology Co.,Ltd.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 29702d6eab..0ad59ef189 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -5060,7 +5060,7 @@ restart: bool isPK; /* primary key */ bool isIDKey; /* replica identity index */ - indexDesc = index_open(indexOid, AccessShareLock); + indexDesc = RelationIdGetRelation(indexOid); /* * Extract index expressions and index predicate. Note: Don't use @@ -5134,7 +5134,7 @@ restart: /* Collect all attributes in the index predicate, too */ pull_varattnos(indexPredicate, 1, &indexattrs); - index_close(indexDesc, AccessShareLock); + RelationClose(indexDesc); } /* diff --git a/src/test/subscription/t/010_truncate.pl b/src/test/subscription/t/010_truncate.pl index be2c0bdc35..cfcee2f1a7 100644 --- a/src/test/subscription/t/010_truncate.pl +++ b/src/test/subscription/t/010_truncate.pl @@ -3,7 +3,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 9; +use Test::More tests => 11; # setup @@ -158,3 +158,28 @@ is($result, qq(0||), 'truncate of multiple tables some not published'); $result = $node_subscriber->safe_psql('postgres', "SELECT count(*), min(a), max(a) FROM tab2"); is($result, qq(3|1|3), 'truncate of multiple tables some not published'); + +# setup synchronous logical replication + +$node_publisher->safe_psql('postgres', + "ALTER SYSTEM SET synchronous_standby_names TO 'sub1'"); +$node_publisher->safe_psql('postgres', "SELECT pg_reload_conf()"); + +# insert data to truncate + +$node_publisher->safe_psql('postgres', + "INSERT INTO tab1 VALUES (1), (2), (3)"); + +$node_publisher->wait_for_catchup('sub1'); + +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM tab1"); +is($result, qq(3|1|3), 'check synchronous logical replication'); + +$node_publisher->safe_psql('postgres', "TRUNCATE tab1"); + +$node_publisher->wait_for_catchup('sub1'); + +$result = $node_subscriber->safe_psql('postgres', + "SELECT count(*), min(a), max(a) FROM tab1"); +is($result, qq(0||), 'truncate replicated in synchronous logical replication');