On Tue, Jan 14, 2020 at 07:35:22PM +0900, Kyotaro Horiguchi wrote: > At Thu, 26 Dec 2019 12:46:39 +0900 (JST), Kyotaro Horiguchi > <horikyota....@gmail.com> wrote in > > At Wed, 25 Dec 2019 16:15:21 -0800, Noah Misch <n...@leadboat.com> wrote in > > > === Defect 1: Forgets to skip WAL after SAVEPOINT; DROP TABLE; ROLLBACK TO > > > > > > A test in transactions.sql now fails in > > > AssertPendingSyncs_RelationCache(), > > > when running "make check" under wal_level=minimal. I test this way: > > > > > > printf '%s\n%s\n' 'wal_level = minimal' 'max_wal_senders = 0' > > > >$PWD/minimal.conf > > > make check TEMP_CONFIG=$PWD/minimal.conf > > > > > > Self-contained demonstration: > > > begin; > > > create table t (c int); > > > savepoint q; drop table t; rollback to q; -- forgets table is skipping > > > wal > > > commit; -- assertion failure > > This is complex than expected. The DROP TABLE unconditionally removed > relcache entry. To fix that, I tried to use rd_isinvalid but it failed > because there's a state that a relcache invalid but the corresponding > catalog entry is alive. > > In the attached patch 0002, I added a boolean in relcache that > indicates that the relation is already removed in catalog but not > committed.
This design could work, but some if its properties aren't ideal. For example, RelationIdGetRelation() can return a !rd_isvalid relation when the relation has been dropped. What others designs did you consider, if any? On Thu, Jan 16, 2020 at 02:20:57PM +0900, Kyotaro Horiguchi wrote: > --- a/src/backend/utils/cache/relcache.c > +++ b/src/backend/utils/cache/relcache.c > @@ -3114,8 +3153,10 @@ AtEOXact_cleanup(Relation relation, bool isCommit) > */ > if (relation->rd_createSubid != InvalidSubTransactionId) > { > - if (isCommit) > - relation->rd_createSubid = InvalidSubTransactionId; > + relation->rd_createSubid = InvalidSubTransactionId; > + > + if (isCommit && !relation->rd_isdropped) > + {} /* Nothing to do */ What is the purpose of this particular change? This executes at the end of a top-level transaction. We've already done any necessary syncing, and we're clearing any flags that caused WAL skipping. I think it's no longer productive to treat dropped relations differently. > @@ -3232,6 +3272,19 @@ AtEOSubXact_cleanup(Relation relation, bool isCommit, > } > } > > + /* > + * If this relation registered pending sync then dropped, subxact > rollback > + * cancels the uncommitted drop, and commit propagates it to the parent. > + */ > + if (relation->rd_isdropped) > + { > + Assert (!relation->rd_isvalid && > + (relation->rd_createSubid != > InvalidSubTransactionId || > + relation->rd_firstRelfilenodeSubid != > InvalidSubTransactionId)); > + if (!isCommit) > + relation->rd_isdropped = false; This does the wrong thing when there exists some subtransaction rollback that does not rollback the DROP: \pset null 'NULL' begin; create extension pg_visibility; create table droppedtest (c int); select 'droppedtest'::regclass::oid as oid \gset savepoint q; drop table droppedtest; release q; -- rd_dropped==true select * from pg_visibility_map(:oid); -- processes !rd_isvalid rel (not ideal) savepoint q; select 1; rollback to q; -- rd_dropped==false (wrong) savepoint q; select 1; rollback to q; select pg_relation_size(:oid), pg_relation_filepath(:oid), has_table_privilege(:oid, 'SELECT'); -- all nulls, okay select * from pg_visibility_map(:oid); -- assertion failure rollback;