Thread
Commits
-
Make detach-partition-concurrently-4 less timing sensitive
- eb43bdbf5104 14.0 landed
-
Improve documentation on DETACH PARTITION lock levels
- db6e1aeb952e 14.0 landed
-
Track detached partitions more accurately in partdescs
- 3fe773b14975 14.0 landed
-
Allow a partdesc-omitting-partitions to be cached
- d6b8d29419df 14.0 landed
-
Fix relcache inconsistency hazard in partition detach
- 8aba9322511f 14.0 landed
-
Don't add a redundant constraint when detaching a partition
- 7b357cc6ae55 14.0 landed
-
ALTER TABLE ... DETACH PARTITION ... CONCURRENTLY
- 71f4c8c6f74b 14.0 landed
-
Let ALTER TABLE Phase 2 routines manage the relation pointer
- cd03c6e94b09 14.0 landed
-
Check default partitions constraints while descending
- f481d2823297 14.0 landed
- ef1e1250e716 12.5 landed
- d0230a43fcae 13.0 landed
-
ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-08-03T23:48:54Z
I've been working on the ability to detach a partition from a partitioned table, without causing blockages to concurrent activity. I think this operation is critical for some use cases. There was a lot of great discussion which ended up in Robert completing a much sought implementation of non-blocking ATTACH. DETACH was discussed too because it was a goal initially, but eventually dropped from that patch altogether. Nonetheless, that thread provided a lot of useful input to this implementation. Important ones: [1] https://postgr.es/m/CA+TgmoYg4x7AH=_QSptvuBKf+3hUdiCa4frPkt+RvXZyjX1n=w@mail.gmail.com [2] https://postgr.es/m/CA+TgmoaAjkTibkEr=xJg3ndbRsHHSiYi2SJgX69MVosj=LJmug@mail.gmail.com [3] https://postgr.es/m/CA+TgmoY13KQZF-=HNTrt9UYWYx3_oYOQpu9ioNT49jGgiDpUEA@mail.gmail.com Attached is a patch that implements ALTER TABLE ... DETACH PARTITION .. CONCURRENTLY. In the previous thread we were able to implement the concurrent model without the extra keyword. For this one I think that won't work; my implementation works in two transactions so there's a restriction that you can't run it in a transaction block. Also, there's a wait phase that makes it slower than the non-concurrent one. Those two drawbacks make me think that it's better to keep both modes available, just like we offer both CREATE INDEX and CREATE INDEX CONCURRENTLY. Why two transactions? The reason is that in order for this to work, we make a catalog change (mark it detached), and commit so that all concurrent transactions can see the change. A second transaction waits for anybody who holds any lock on the partitioned table and grabs Access Exclusive on the partition (which now no one cares about, if they're looking at the partitioned table), where the DDL action on the partition can be completed. ALTER TABLE is normally unable to run in two transactions. I hacked it (0001) so that the relation can be closed and reopened in the Exec phase (by having the rel as part of AlteredTableInfo: when ATRewriteCatalogs returns, it uses that pointer to close the rel). It turns out that this is sufficient to make that work. This means that ALTER TABLE DETACH CONCURRENTLY cannot work as part of a multi-command ALTER TABLE, but that's alreay enforced by the grammar anyway. DETACH CONCURRENTLY doesn't work if a default partition exists. It's just too problematic a case; you would still need to have AEL on the default partition. I haven't yet experimented with queries running in a standby in tandem with a detach. -- Álvaro Herrera
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-08-03T23:51:44Z
On 2020-Aug-03, Alvaro Herrera wrote: > There was a lot of great discussion which ended up in Robert completing > a much sought implementation of non-blocking ATTACH. DETACH was > discussed too because it was a goal initially, but eventually dropped > from that patch altogether. Nonetheless, that thread provided a lot of > useful input to this implementation. Important ones: > > [1] https://postgr.es/m/CA+TgmoYg4x7AH=_QSptvuBKf+3hUdiCa4frPkt+RvXZyjX1n=w@mail.gmail.com > [2] https://postgr.es/m/CA+TgmoaAjkTibkEr=xJg3ndbRsHHSiYi2SJgX69MVosj=LJmug@mail.gmail.com > [3] https://postgr.es/m/CA+TgmoY13KQZF-=HNTrt9UYWYx3_oYOQpu9ioNT49jGgiDpUEA@mail.gmail.com There was some discussion about having a version number in the partition descriptor somewhere as a means to implement this. I couldn't figure out how that would work, or what the version number would be attached to. Surely the idea wasn't to increment the version number to every partition other than the one being detached? -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-08-04T16:56:25Z
On 2020-Aug-03, Alvaro Herrera wrote: > Why two transactions? The reason is that in order for this to work, we > make a catalog change (mark it detached), and commit so that all > concurrent transactions can see the change. A second transaction waits > for anybody who holds any lock on the partitioned table and grabs Access > Exclusive on the partition (which now no one cares about, if they're > looking at the partitioned table), where the DDL action on the partition > can be completed. I forgot to mention. If for whatever reason the second transaction fails, (say the user aborts it or there is a crash), then the partition is still marked as detached, so no queries would see it; but all the ancillary catalog data remains. Just like when CREATE INDEX CONCURRENTLY fails, you keep an invalid index that must be dropped; in this case, the changes to do are much more extensive, so manual action is out of the question. So there's another DDL command to be invoked, ALTER TABLE parent DETACH PARTITION part FINALIZE; which will complete the detach action. If we had UNDO then perhaps it would be possible to register an action so that the detach is completed automatically. But for now this seems sufficient. Another aspect worth mentioning is constraints. In the patch, I create a CHECK constraint to stand for the partition constraint that's going to logically disappear. This was mentioned as a potential problem in one of Robert's emails (I didn't actually verify that this is a problem). However, a funny thing is that if a constraint already exists, you get a dupe, so after a few rounds of attach/detach you can see them pile up. I'll have to fix this at some point. But also, I need to think about whether foreign keys have similar problems, since they are also used by the optimizer. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Robert Haas <robertmhaas@gmail.com> — 2020-08-04T17:53:17Z
On Mon, Aug 3, 2020 at 7:49 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > Why two transactions? The reason is that in order for this to work, we > make a catalog change (mark it detached), and commit so that all > concurrent transactions can see the change. A second transaction waits > for anybody who holds any lock on the partitioned table and grabs Access > Exclusive on the partition (which now no one cares about, if they're > looking at the partitioned table), where the DDL action on the partition > can be completed. Is there a more detailed theory of operation of this patch somewhere? What exactly do you mean by marking it detached? Committing the change makes it possible for all concurrent transactions to see the change, but does not guarantee that they will. If you can't guarantee that, then I'm not sure how you can guarantee that they will behave sanely. Even if you can, it's not clear what the sane behavior is: what happens when a tuple gets routed to an ex-partition? What happens when an ex-partition needs to be scanned? What prevents problems if a partition is detached, possibly modified, and then reattached, possibly with different partition bounds? I think the two-transaction approach is interesting and I can imagine that it possibly solves some problems, but it's not clear to me exactly which problems it solves or how it does so. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-08-26T23:40:07Z
On 2020-Aug-04, Robert Haas wrote: > On Mon, Aug 3, 2020 at 7:49 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > > Why two transactions? The reason is that in order for this to work, we > > make a catalog change (mark it detached), and commit so that all > > concurrent transactions can see the change. A second transaction waits > > for anybody who holds any lock on the partitioned table and grabs Access > > Exclusive on the partition (which now no one cares about, if they're > > looking at the partitioned table), where the DDL action on the partition > > can be completed. > > Is there a more detailed theory of operation of this patch somewhere? > What exactly do you mean by marking it detached? Committing the change > makes it possible for all concurrent transactions to see the change, > but does not guarantee that they will. If you can't guarantee that, > then I'm not sure how you can guarantee that they will behave sanely. Sorry for the long delay. I haven't written up the theory of operation. I suppose it is complicated enough that it should be documented somewhere. To mark it detached means to set pg_inherits.inhdetached=true. That column name is a bit of a misnomer, since that column really means "is in the process of being detached"; the pg_inherits row goes away entirely once the detach process completes. This mark guarantees that everyone will see that row because the detaching session waits long enough after committing the first transaction and before deleting the pg_inherits row, until everyone else has moved on. The other point is that the partition directory code can be asked to include detached partitions, or not to. The executor does the former, and the planner does the latter. This allows a transient period during which the partition descriptor returned to planner and executor is different; this makes the situation equivalent to what would have happened if the partition was attached during the operation: in executor we would detect that there is an additional partition that was not seen by the planner, and we already know how to deal with that situation by your handling of the ATTACH code. > Even if you can, it's not clear what the sane behavior is: what > happens when a tuple gets routed to an ex-partition? What happens when > an ex-partition needs to be scanned? During the transient period, any transaction that obtained a partition descriptor before the inhdetached mark is committed should be able to get the tuple routing done successfully, but transactions using later snapshots should get their insertions rejected. Reads should behave in the same way. > What prevents problems if a partition is detached, possibly modified, > and then reattached, possibly with different partition bounds? This should not be a problem, because the partition needs to be fully detached before it can be attached again. And if the partition bounds are different, that won't be a problem, because the previous partition bounds won't be present in the pg_class row. Of course, the new partition bounds will be checked to the existing contents. There is one fly in the ointment though, which is that if you cancel the wait and then immediately do the ALTER TABLE DETACH FINALIZE without waiting for as long as the original execution would have waited, you might end up killing the partition ahead of time. One solution to this would be to cause the FINALIZE action to wait again at start. This would cause it to take even longer, but it would be safer. (If you don't want it to take longer, you can just not cancel it in the first place.) This is not a problem if the server crashes in between (which is the scenario I had in mind when doing the FINALIZE thing), because of course no transaction can continue to run across a crash. I'm going to see if I can get the new delay_execution module to help test this, to verify whether my claims are true. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Robert Haas <robertmhaas@gmail.com> — 2020-08-27T15:46:09Z
On Wed, Aug 26, 2020 at 7:40 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > To mark it detached means to set pg_inherits.inhdetached=true. That > column name is a bit of a misnomer, since that column really means "is > in the process of being detached"; the pg_inherits row goes away > entirely once the detach process completes. This mark guarantees that > everyone will see that row because the detaching session waits long > enough after committing the first transaction and before deleting the > pg_inherits row, until everyone else has moved on. OK. Do you just wait until the XID of the transaction that set inhdetached is all-visible, or how do you do it? > The other point is that the partition directory code can be asked to > include detached partitions, or not to. The executor does the former, > and the planner does the latter. This allows a transient period during > which the partition descriptor returned to planner and executor is > different; this makes the situation equivalent to what would have > happened if the partition was attached during the operation: in executor > we would detect that there is an additional partition that was not seen > by the planner, and we already know how to deal with that situation by > your handling of the ATTACH code. Ah ha! That is quite clever and I don't think that I would have thought of it. So all the plans that were created before you set inhdetached=true have to be guaranteed to be invaliated or gone altogether before you can actually delete the pg_inherits row. It seems like it ought to be possible to ensure that, though I am not surely of the details exactly. Is it sufficient to wait for all transactions that have locked the table to go away? I'm not sure exactly how this stuff interacts with the plan cache. > There is one fly in the ointment though, which is that if you cancel the > wait and then immediately do the ALTER TABLE DETACH FINALIZE without > waiting for as long as the original execution would have waited, you > might end up killing the partition ahead of time. One solution to this > would be to cause the FINALIZE action to wait again at start. This > would cause it to take even longer, but it would be safer. (If you > don't want it to take longer, you can just not cancel it in the first > place.) This is not a problem if the server crashes in between (which > is the scenario I had in mind when doing the FINALIZE thing), because of > course no transaction can continue to run across a crash. Yeah, it sounds like this will require some solution, but I agree that just waiting "longer" seems acceptable. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Hao Wu <hawu@vmware.com> — 2020-08-31T07:00:19Z
Hi hacker, I tested the patch provided by Alvaro. It seems not well in REPEATABLE READ. gpadmin=*# \d+ tpart Partitioned table "public.tpart" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+---------+-----------+----------+---------+---------+--------------+------------- i | integer | | | | plain | | j | integer | | | | plain | | Partition key: RANGE (i) Partitions: tpart_1 FOR VALUES FROM (0) TO (100), tpart_2 FOR VALUES FROM (100) TO (200) begin isolation level repeatable read; BEGIN gpadmin=*# select * from tpart; i | j -----+----- 10 | 10 50 | 50 110 | 110 120 | 120 150 | 150 (5 rows) -- Here, run `ALTER TABLE tpart DROP PARTITION tpart_2 CONCURRENTLY` -- but only complete the first transaction. -- the tuples from tpart_2 are gone. gpadmin=*# select * from tpart; i | j ----+---- 10 | 10 50 | 50 (2 rows) gpadmin=*# \d+ tpart_2 Table "public.tpart_2" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+---------+-----------+----------+---------+---------+--------------+------------- i | integer | | | | plain | | j | integer | | | | plain | | Partition of: tpart FOR VALUES FROM (100) TO (200) Partition constraint: ((i IS NOT NULL) AND (i >= 100) AND (i < 200)) Access method: heap -- the part tpart_2 is not showed as DETACHED gpadmin=*# \d+ tpart Partitioned table "public.tpart" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description --------+---------+-----------+----------+---------+---------+--------------+------------- i | integer | | | | plain | | j | integer | | | | plain | | Partition key: RANGE (i) Partitions: tpart_1 FOR VALUES FROM (0) TO (100), tpart_2 FOR VALUES FROM (100) TO (200) -- next, the insert failed. It's OK. gpadmin=*# insert into tpart values(130,130); ERROR: no partition of relation "tpart" found for row DETAIL: Partition key of the failing row contains (i) = (130). Is this an expected behavior? Regards, Hao Wu ________________________________ From: Robert Haas <robertmhaas@gmail.com> Sent: Thursday, August 27, 2020 11:46 PM To: Alvaro Herrera <alvherre@2ndquadrant.com> Cc: Pg Hackers <pgsql-hackers@lists.postgresql.org> Subject: Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY On Wed, Aug 26, 2020 at 7:40 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > To mark it detached means to set pg_inherits.inhdetached=true. That > column name is a bit of a misnomer, since that column really means "is > in the process of being detached"; the pg_inherits row goes away > entirely once the detach process completes. This mark guarantees that > everyone will see that row because the detaching session waits long > enough after committing the first transaction and before deleting the > pg_inherits row, until everyone else has moved on. OK. Do you just wait until the XID of the transaction that set inhdetached is all-visible, or how do you do it? > The other point is that the partition directory code can be asked to > include detached partitions, or not to. The executor does the former, > and the planner does the latter. This allows a transient period during > which the partition descriptor returned to planner and executor is > different; this makes the situation equivalent to what would have > happened if the partition was attached during the operation: in executor > we would detect that there is an additional partition that was not seen > by the planner, and we already know how to deal with that situation by > your handling of the ATTACH code. Ah ha! That is quite clever and I don't think that I would have thought of it. So all the plans that were created before you set inhdetached=true have to be guaranteed to be invaliated or gone altogether before you can actually delete the pg_inherits row. It seems like it ought to be possible to ensure that, though I am not surely of the details exactly. Is it sufficient to wait for all transactions that have locked the table to go away? I'm not sure exactly how this stuff interacts with the plan cache. > There is one fly in the ointment though, which is that if you cancel the > wait and then immediately do the ALTER TABLE DETACH FINALIZE without > waiting for as long as the original execution would have waited, you > might end up killing the partition ahead of time. One solution to this > would be to cause the FINALIZE action to wait again at start. This > would cause it to take even longer, but it would be safer. (If you > don't want it to take longer, you can just not cancel it in the first > place.) This is not a problem if the server crashes in between (which > is the scenario I had in mind when doing the FINALIZE thing), because of > course no transaction can continue to run across a crash. Yeah, it sounds like this will require some solution, but I agree that just waiting "longer" seems acceptable. -- Robert Haas EnterpriseDB: https://urldefense.proofpoint.com/v2/url?u=http-3A__www.enterprisedb.com&d=DwIBaQ&c=lnl9vOaLMzsy2niBC8-h_K-7QJuNJEsFrzdndhuJ3Sw&r=tqYUKh-fXcYPWSaF4E-D6A&m=SEDl-6dEISo7BA0qWuv1-idQUVtO0M6qz7hcfwlrF3I&s=pZ7Dx6xrJOYkKKMlXR4wpJNZv-W10wQkMfXdEjtIXJY&e= The Enterprise PostgreSQL Company -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-09-01T18:15:27Z
On 2020-Aug-27, Robert Haas wrote: > On Wed, Aug 26, 2020 at 7:40 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > > To mark it detached means to set pg_inherits.inhdetached=true. That > > column name is a bit of a misnomer, since that column really means "is > > in the process of being detached"; the pg_inherits row goes away > > entirely once the detach process completes. This mark guarantees that > > everyone will see that row because the detaching session waits long > > enough after committing the first transaction and before deleting the > > pg_inherits row, until everyone else has moved on. > > OK. Do you just wait until the XID of the transaction that set > inhdetached is all-visible, or how do you do it? I'm just doing WaitForLockers( ... AccessExclusiveLock ...) on the partitioned table at the start of the second transaction. That will wait until all lockers that have obtained a partition descriptor with the old definition are gone. Note we don't actually lock the partitioned table with that lock level. In the second transaction we additionally obtain AccessExclusiveLock on the partition itself, but that's after nobody sees it as a partition anymore. That lock level is needed for some of the internal DDL changes, and should not cause problems. I thought about using WaitForOlderSnapshots() instead of waiting for lockers, but it didn't seem to solve any actual problem. Note that on closing the first transaction, the locks on both tables are released. This avoids the deadlock hazards because of the lock upgrades that would otherwise occur. This means that the tables could be dropped or changed in the meantime. The case where either relation is dropped is handled by using try_relation_open() in the second transaction; if either table is gone, then we can just mark the operation as completed. This part is a bit fuzzy. One thing that should probably be done is have a few operations (such as other ALTER TABLE) raise an error when run on a table with inhdetached=true, because that might get things out of step and potentially cause other problems. I've not done that yet. > So all the plans that were created before you set > inhdetached=true have to be guaranteed to be invaliated or gone > altogether before you can actually delete the pg_inherits row. It > seems like it ought to be possible to ensure that, though I am not > surely of the details exactly. Is it sufficient to wait for all > transactions that have locked the table to go away? I'm not sure > exactly how this stuff interacts with the plan cache. Hmm, any cached plan should be released with relcache inval events, per PlanCacheRelCallback(). There are some comments in plancache.h about "unsaved" cached plans that I don't really understand :-( -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Hao Wu <hawu@vmware.com> — 2020-09-02T04:25:16Z
Not related to DETACH PARTITION, but I found a bug in ATTACH PARTITION. Here are the steps to reproduce the issue: create table tpart(i int, j int) partition by range(i); create table tpart_1(like tpart); create table tpart_2(like tpart); create table tpart_default(like tpart);alter table tpart attach partition tpart_1 for values from(0) to (100); alter table tpart attach partition tpart_default default;insert into tpart_2 values(110,110),(120,120),(150,150);1: begin; 1: alter table tpart attach partition tpart_2 for values from(100) to (200); 2: begin; -- insert will be blocked by ALTER TABLE ATTACH PARTITION 2: insert into tpart values (110,110),(120,120),(150,150); 1: end; 2: select * from tpart_default; 2: end; After that the partition tpart_default contains (110,110),(120,120),(150,150) inserted in session 2, which obviously violates the partition constraint. Regards, Hao Wu
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2020-09-03T09:53:38Z
Hi Hao, On Wed, Sep 2, 2020 at 5:25 PM Hao Wu <hawu@vmware.com> wrote: > > Not related to DETACH PARTITION, but I found a bug in ATTACH PARTITION. > Here are the steps to reproduce the issue: > > create table tpart(i int, j int) partition by range(i); > create table tpart_1(like tpart); > create table tpart_2(like tpart); > create table tpart_default(like tpart);alter table tpart attach partition tpart_1 for values from(0) to (100); > alter table tpart attach partition tpart_default default;insert into tpart_2 values(110,110),(120,120),(150,150);1: begin; > 1: alter table tpart attach partition tpart_2 for values from(100) to (200); > 2: begin; > -- insert will be blocked by ALTER TABLE ATTACH PARTITION > 2: insert into tpart values (110,110),(120,120),(150,150); > 1: end; > 2: select * from tpart_default; > 2: end; > > > After that the partition tpart_default contains (110,110),(120,120),(150,150) > inserted in session 2, which obviously violates the partition constraint. Thanks for the report. That looks like a bug. I have started another thread to discuss this bug and a patch to fix it to keep the discussion here focused on the new feature. Subject: default partition and concurrent attach partition https://www.postgresql.org/message-id/CA%2BHiwqFqBmcSSap4sFnCBUEL_VfOMmEKaQ3gwUhyfa4c7J_-nA%40mail.gmail.com -- Amit Langote EnterpriseDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-09-10T20:54:24Z
On 2020-Aug-31, Hao Wu wrote: > I tested the patch provided by Alvaro. It seems not well in REPEATABLE READ. > -- the tuples from tpart_2 are gone. > gpadmin=*# select * from tpart; > i | j > ----+---- > 10 | 10 > 50 | 50 > (2 rows) Interesting example, thanks. It seems this can be fixed without breaking anything else by changing the planner so that it includes detached partitions when we are in a snapshot-isolation transaction. Indeed, the results from the detach-partition-concurrently-1.spec isolation test are more satisfying with this change. The attached v2, on current master, includes that change, as well as fixes a couple of silly bugs in the previous one. (Because of experimenting with git workflow I did not keep the 0001 part split in this one, but that part is unchanged from v1.) -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Robert Haas <robertmhaas@gmail.com> — 2020-09-11T20:28:49Z
On Thu, Sep 10, 2020 at 4:54 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > Interesting example, thanks. It seems this can be fixed without > breaking anything else by changing the planner so that it includes > detached partitions when we are in a snapshot-isolation transaction. > Indeed, the results from the detach-partition-concurrently-1.spec > isolation test are more satisfying with this change. Hmm, so I think the idea here is that since we're out-waiting plans with the old partition descriptor by waiting for lock release, it's OK for anyone who has a lock to keep using the old partition descriptor as long as they continuously hold the lock. Is that right? I can't think of a hole in that logic, but it's probably worth noting in the comments, in case someone is tempted to change the way that we out-wait plans with the old partition descriptor to some other mechanism. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2020-09-23T05:39:55Z
Hi Alvaro, Studying the patch to understand how it works. On Tue, Aug 4, 2020 at 8:49 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > Why two transactions? The reason is that in order for this to work, we > make a catalog change (mark it detached), and commit so that all > concurrent transactions can see the change. A second transaction waits > for anybody who holds any lock on the partitioned table and grabs Access > Exclusive on the partition (which now no one cares about, if they're > looking at the partitioned table), where the DDL action on the partition > can be completed. + /* + * Concurrent mode has to work harder; first we add a new constraint to the + * partition that matches the partition constraint. The reason for this is + * that the planner may have made optimizations that depend on the + * constraint. XXX Isn't it sufficient to invalidate the partition's + * relcache entry? ... + /* Add constraint on partition, equivalent to the partition constraint */ + n = makeNode(Constraint); + n->contype = CONSTR_CHECK; + n->conname = NULL; + n->location = -1; + n->is_no_inherit = false; + n->raw_expr = NULL; + n->cooked_expr = nodeToString(make_ands_explicit(RelationGetPartitionQual(partRel))); + n->initially_valid = true; + n->skip_validation = true; + /* It's a re-add, since it nominally already exists */ + ATAddCheckConstraint(wqueue, tab, partRel, n, + true, false, true, ShareUpdateExclusiveLock); I suspect that we don't really need this defensive constraint. I mean even after committing the 1st transaction, the partition being detached still has relispartition set to true and RelationGetPartitionQual() still returns the partition constraint, so it seems the constraint being added above is duplicative. Moreover, the constraint is not removed as part of any cleaning up after the DETACH process, so repeated attach/detach of the same partition results in the constraints piling up: create table foo (a int, b int) partition by range (a); create table foo1 partition of foo for values from (1) to (2); create table foo2 partition of foo for values from (2) to (3); alter table foo detach partition foo2 concurrently; alter table foo attach partition foo2 for values from (2) to (3); alter table foo detach partition foo2 concurrently; alter table foo attach partition foo2 for values from (2) to (3); \d foo2 Table "public.foo2" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- a | integer | | | b | integer | | | Partition of: foo FOR VALUES FROM (2) TO (3) Check constraints: "foo2_a_check" CHECK (a IS NOT NULL AND a >= 2 AND a < 3) "foo2_a_check1" CHECK (a IS NOT NULL AND a >= 2 AND a < 3) Noticed a bug/typo in the patched RelationBuildPartitionDesc(): - inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock); + inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock, + include_detached); You're passing NoLock for include_detached which means you never actually end up including detached partitions from here. I think it is due to this bug that partition-concurrent-attach test fails in my run. Also, the error seen in the following hunk of detach-partition-concurrently-1 test is not intentional: +starting permutation: s1brr s1prep s1s s2d s1s s1exec2 s1c +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1prep: PREPARE f(int) AS INSERT INTO d_listp VALUES ($1); +step s1s: SELECT * FROM d_listp; +a + +1 +2 +step s2d: ALTER TABLE d_listp DETACH PARTITION d_listp2 CONCURRENTLY; <waiting ...> +step s1s: SELECT * FROM d_listp; +a + +1 +step s1exec2: EXECUTE f(2); DEALLOCATE f; +step s2d: <... completed> +error in steps s1exec2 s2d: ERROR: no partition of relation "d_listp" found for row +step s1c: COMMIT; As you're intending to make the executor always *include* detached partitions, the insert should be able route (2) to d_listp2, the detached partition. It's the bug mentioned above that causes the executor's partition descriptor build to falsely fail to include the detached partition. -- Amit Langote EnterpriseDB: http://www.enterprisedb.com -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@2ndquadrant.com> — 2020-09-23T15:23:21Z
On 2020-Sep-23, Amit Langote wrote: Hi Amit, thanks for reviewing this patch! > On Tue, Aug 4, 2020 at 8:49 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > I suspect that we don't really need this defensive constraint. I mean > even after committing the 1st transaction, the partition being > detached still has relispartition set to true and > RelationGetPartitionQual() still returns the partition constraint, so > it seems the constraint being added above is duplicative. Ah, thanks for thinking through that. I had vague thoughts about this being unnecessary in the current mechanics, but hadn't fully materialized the thought. (The patch was completely different a few unposted iterations ago). > Moreover, the constraint is not removed as part of any cleaning up > after the DETACH process, so repeated attach/detach of the same > partition results in the constraints piling up: Yeah, I knew about this issue (mentioned in my self-reply on Aug 4) and didn't worry too much about it because I was thinking I'd rather get rid of the constraint addition in the first place. > Noticed a bug/typo in the patched RelationBuildPartitionDesc(): > > - inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock); > + inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock, > + include_detached); > > You're passing NoLock for include_detached which means you never > actually end up including detached partitions from here. I fixed this in the version I posted on Sept 10. I think you were reading the version posted at the start of this thread. Thanks, -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2020-09-24T03:51:52Z
Hi Alvaro, Sorry I totally failed to see the v2 you had posted and a couple of other emails where you mentioned the issues I brought up. On Thu, Sep 24, 2020 at 12:23 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > On 2020-Sep-23, Amit Langote wrote: > I suspect that we don't really need this defensive constraint. I mean > > even after committing the 1st transaction, the partition being > > detached still has relispartition set to true and > > RelationGetPartitionQual() still returns the partition constraint, so > > it seems the constraint being added above is duplicative. > > Ah, thanks for thinking through that. I had vague thoughts about this > being unnecessary in the current mechanics, but hadn't fully > materialized the thought. (The patch was completely different a few > unposted iterations ago). > > > Moreover, the constraint is not removed as part of any cleaning up > > after the DETACH process, so repeated attach/detach of the same > > partition results in the constraints piling up: > > Yeah, I knew about this issue (mentioned in my self-reply on Aug 4) and > didn't worry too much about it because I was thinking I'd rather get rid > of the constraint addition in the first place. Okay, gotcha. > > Noticed a bug/typo in the patched RelationBuildPartitionDesc(): > > > > - inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock); > > + inhoids = find_inheritance_children(RelationGetRelid(rel), NoLock, > > + include_detached); > > > > You're passing NoLock for include_detached which means you never > > actually end up including detached partitions from here. > > I fixed this in the version I posted on Sept 10. I think you were > reading the version posted at the start of this thread. I am trying the v2 now and I can confirm that those problems are now fixed. However, I am a bit curious about including detached partitions in some cases while not in other, which can result in a (to me) surprising behavior as follows: Session 1: create table foo (a int, b int) partition by range (a); create table foo1 partition of foo for values from (1) to (2); create table foo2 partition of foo for values from (2) to (3); ...attach GDB and set breakpoint so as to block right after finishing the 1st transaction of DETACH PARTITION CONCURRENTLY... alter table foo detach partition foo2 concurrently; <hits breakpoint, wait...> Session 2: begin; insert into foo values (2); -- ok select * from foo; select * from foo; -- ?! a | b ---+--- (0 rows) Maybe, it's fine to just always exclude detached partitions, although perhaps I am missing some corner cases that you have thought of? Also, I noticed that looking up a parent's partitions via RelationBuildPartitionDesc or directly will inspect inhdetached to include or exclude partitions, but checking if a child table is a partition of a given parent table via get_partition_parent doesn't. Now if you fix get_partition_parent() to also take into account inhdetached, for example, to return InvalidOid if true, then the callers would need to not consider the child table a valid partition. So, RelationGetPartitionQual() on a detached partition should actually return NIL, making my earlier claim about not needing the defensive CHECK constraint invalid. But maybe that's fine if all places agree on a detached partition not being a valid partition anymore? -- Amit Langote EnterpriseDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Michael Paquier <michael@paquier.xyz> — 2020-10-01T03:50:39Z
On Thu, Sep 24, 2020 at 12:51:52PM +0900, Amit Langote wrote: > Also, I noticed that looking up a parent's partitions via > RelationBuildPartitionDesc or directly will inspect inhdetached to > include or exclude partitions, but checking if a child table is a > partition of a given parent table via get_partition_parent doesn't. > Now if you fix get_partition_parent() to also take into account > inhdetached, for example, to return InvalidOid if true, then the > callers would need to not consider the child table a valid partition. > So, RelationGetPartitionQual() on a detached partition should actually > return NIL, making my earlier claim about not needing the defensive > CHECK constraint invalid. But maybe that's fine if all places agree > on a detached partition not being a valid partition anymore? It would be good to get that answered, and while on it please note that the patch needs a rebase. -- Michael
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Andy Fan <zhihui.fan1213@gmail.com> — 2020-10-15T01:04:24Z
Hi Alvaro: On Tue, Aug 4, 2020 at 7:49 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > I've been working on the ability to detach a partition from a > partitioned table, without causing blockages to concurrent activity. > I think this operation is critical for some use cases. > I think if it is possible to implement the detech with a NoWait option . ALTER TABLE ... DETACH PARTITION .. [NoWait]. if it can't get the lock, raise "Resource is Busy" immediately, without blocking others. this should be a default behavior. If people do want to keep trying, it can set a ddl_lock_timeout to 'some-interval', in this case, it will still block others(so it can't be as good as what you are doing, but very simple), however the user would know what would happen exactly and can coordinate with their application accordingly. I'm sorry about this since it is a bit of off-topics or it has been discussed already. -- Best Regards Andy Fan
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2020-10-15T01:08:40Z
On 2020-Oct-15, Andy Fan wrote: > I think if it is possible to implement the detech with a NoWait option . > > ALTER TABLE ... DETACH PARTITION .. [NoWait]. > > if it can't get the lock, raise "Resource is Busy" immediately, > without blocking others. this should be a default behavior. If > people do want to keep trying, it can set a ddl_lock_timeout to > 'some-interval', in this case, it will still block others(so it can't > be as good as what you are doing, but very simple), however the user > would know what would happen exactly and can coordinate with their > application accordingly. I'm sorry about this since it is a bit of > off-topics or it has been discussed already. Hi. I don't think this has been discussed, but it doesn't really solve the use case I want to -- in many cases where the tables are continuously busy, this would lead to starvation. I think the proposal to make the algorithm work with reduced lock level is much more useful. I think you can already do NOWAIT behavior, with LOCK TABLE .. NOWAIT followed by DETACH PARTITION, perhaps with a nonzero statement timeout.
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
David Rowley <dgrowleyml@gmail.com> — 2020-10-15T01:09:11Z
On Thu, 15 Oct 2020 at 14:04, Andy Fan <zhihui.fan1213@gmail.com> wrote: > > I think if it is possible to implement the detech with a NoWait option . > > ALTER TABLE ... DETACH PARTITION .. [NoWait]. > > if it can't get the lock, raise "Resource is Busy" immediately, without blocking others. > this should be a default behavior. If people do want to keep trying, it can set > a ddl_lock_timeout to 'some-interval', in this case, it will still block others(so it > can't be as good as what you are doing, but very simple), however the user > would know what would happen exactly and can coordinate with their > application accordingly. I'm sorry about this since it is a bit of off-topics > or it has been discussed already. How would that differ from setting a low lock_timeout and running the DDL? I think what Alvaro wants to avoid is taking the AEL in the first place. When you have multiple long overlapping queries to the partitioned table, then there be no point in time where there are zero locks on the table. It does not sound like your idea would help with that. David
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Andy Fan <zhihui.fan1213@gmail.com> — 2020-10-15T03:38:23Z
Hi David/Alvaro: On Thu, Oct 15, 2020 at 9:09 AM David Rowley <dgrowleyml@gmail.com> wrote: > On Thu, 15 Oct 2020 at 14:04, Andy Fan <zhihui.fan1213@gmail.com> wrote: > > > > I think if it is possible to implement the detech with a NoWait option . > > > > ALTER TABLE ... DETACH PARTITION .. [NoWait]. > > > > if it can't get the lock, raise "Resource is Busy" immediately, without > blocking others. > > this should be a default behavior. If people do want to keep trying, > it can set > > a ddl_lock_timeout to 'some-interval', in this case, it will still > block others(so it > > can't be as good as what you are doing, but very simple), however the > user > > would know what would happen exactly and can coordinate with their > > application accordingly. I'm sorry about this since it is a bit of > off-topics > > or it has been discussed already. > > How would that differ from setting a low lock_timeout and running the DDL? > They are exactly the same (I didn't realize this parameter when I sent the email). > I think what Alvaro wants to avoid is taking the AEL in the first > place. I'm agreed with this, that's why I said "so it can't be as good as what you are doing" > When you have multiple long overlapping queries to the > partitioned table, then there be no point in time where there are zero > locks on the table. It does not sound like your idea would help with that. Based on my current knowledge, "detach" will hold an exclusive lock and it will have higher priority than other waiters. so it has to wait for the lock holder before it (named as sess 1). and at the same time, block all the other waiters which are requiring a lock even the lock mode is compatible with session 1. So "deteach" can probably get its lock in a short time (unless some long transaction before it). I'm not sure if I have some misunderstanding here. Overall I'd be +1 for this patch. -- Best Regards Andy Fan
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2020-10-16T22:13:21Z
On 2020-Sep-24, Amit Langote wrote: Hello Amit, > Sorry I totally failed to see the v2 you had posted and a couple of > other emails where you mentioned the issues I brought up. No worries, I appreciate you reviewing this. > However, I am a bit curious about including detached partitions in > some cases while not in other, which can result in a (to me) > surprising behavior as follows: [ snip ] > begin; > insert into foo values (2); -- ok > select * from foo; > select * from foo; -- ?! > a | b > ---+--- > (0 rows) > > Maybe, it's fine to just always exclude detached partitions, although > perhaps I am missing some corner cases that you have thought of? Well, this particular case can be fixed by changing ExecInitPartitionDispatchInfo specifically, from including detached partitions to excluding them, as in the attached version. Given your example I think the case is fairly good that they should be excluded there. I can't think of a case that this change break. However I'm not sure that excluding them everywhere is sensible. There are currently two cases where they are included (search for calls to CreatePartitionDirectory if you're curious). One is snapshot-isolation transactions (repeatable read and serializable) in set_relation_partition_info, per the example from Hao Wu. If we simply exclude detached transaction there, repeatable read no longer works properly; rows will just go missing for no apparent reason. I don't think this is acceptable. The other case is ExecCreatePartitionPruneState(). The whole point of including detached partitions here is to make them available for queries that were planned before the detach and executed after the detach. My fear is that the pruning plan will contain references (from planner) to partitions that the executor doesn't know about. If there are extra partitions at the executor side, it shouldn't harm anything (and it shouldn't change query results); but I'm not sure that things will work OK if partitions seen by the planner disappear from under the executor. I'm posting this version just as a fresh rebase -- it is not yet intended for commit. I haven't touched the constraint stuff. I still think that that can be removed before commit, which is a simple change; but even if not, the problem with the duplicated constraints should be easy to fix. Again, my thanks for reviewing.
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2020-11-03T23:56:06Z
Here's an updated version of this patch. Apart from rebasing to current master, I made the following changes: * On the first transaction (the one that marks the partition as detached), the partition is locked with ShareLock rather than ShareUpdateExclusiveLock. This means that DML is not allowed anymore. This seems a reasonable restriction to me; surely by the time you're detaching the partition you're not inserting data into it anymore. * In ExecInitPartitionDispatchInfo, the previous version always excluded detached partitions. Now it does include them in isolation level repeatable read and higher. Considering the point above, this sounds a bit contradictory: you shouldn't be inserting new tuples in partitions being detached. But if you do, it makes more sense: in RR two queries that insert tuples in the same partition would not fail mid-transaction. (In a read-committed transaction, the second query does fail, but to me that does not sound surprising.) * ALTER TABLE .. DETACH PARTITION FINALIZE now waits for concurrent old snapshots, as previously discussed. This should ensure that the user doesn't just cancel the wait during the second transaction by Ctrl-C and run FINALIZE immediately afterwards, which I claimed would bring inconsistency. * Avoid creating the CHECK constraint if an identical one already exists. (Note I do not remove the constraint on ATTACH. That seems pointless.) Still to do: test this using the new hook added by 6f0b632f083b.
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Anastasia Lubennikova <a.lubennikova@postgrespro.ru> — 2020-11-30T15:22:35Z
On 04.11.2020 02:56, Alvaro Herrera wrote: > Here's an updated version of this patch. > > Apart from rebasing to current master, I made the following changes: > > * On the first transaction (the one that marks the partition as > detached), the partition is locked with ShareLock rather than > ShareUpdateExclusiveLock. This means that DML is not allowed anymore. > This seems a reasonable restriction to me; surely by the time you're > detaching the partition you're not inserting data into it anymore. > > * In ExecInitPartitionDispatchInfo, the previous version always > excluded detached partitions. Now it does include them in isolation > level repeatable read and higher. Considering the point above, this > sounds a bit contradictory: you shouldn't be inserting new tuples in > partitions being detached. But if you do, it makes more sense: in RR > two queries that insert tuples in the same partition would not fail > mid-transaction. (In a read-committed transaction, the second query > does fail, but to me that does not sound surprising.) > > * ALTER TABLE .. DETACH PARTITION FINALIZE now waits for concurrent old > snapshots, as previously discussed. This should ensure that the user > doesn't just cancel the wait during the second transaction by Ctrl-C and > run FINALIZE immediately afterwards, which I claimed would bring > inconsistency. > > * Avoid creating the CHECK constraint if an identical one already > exists. > > (Note I do not remove the constraint on ATTACH. That seems pointless.) > > Still to do: test this using the new hook added by 6f0b632f083b. Status update for a commitfest entry. The commitfest is nearing the end and this thread is "Waiting on Author". As far as I see the last message contains a patch. Is there anything left to work on or it needs review now? Alvaro, are you planning to continue working on it? -- Anastasia Lubennikova Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2020-11-30T15:29:51Z
On 2020-Nov-30, Anastasia Lubennikova wrote: > The commitfest is nearing the end and this thread is "Waiting on Author". > As far as I see the last message contains a patch. Is there anything left to > work on or it needs review now? Alvaro, are you planning to continue working > on it? Thanks Anastasia. I marked it as needs review.
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2020-12-01T01:30:51Z
On Tue, Nov 03, 2020 at 08:56:06PM -0300, Alvaro Herrera wrote: > Here's an updated version of this patch. > > Apart from rebasing to current master, I made the following changes: > > * On the first transaction (the one that marks the partition as > detached), the partition is locked with ShareLock rather than > ShareUpdateExclusiveLock. This means that DML is not allowed anymore. > This seems a reasonable restriction to me; surely by the time you're > detaching the partition you're not inserting data into it anymore. I don't think it's an issue with your patch, but FYI that sounds like something I had to do recently: detach *all* partitions of various tabls to promote their partition key column from timestamp to timestamptz. And we insert directly into child tables, not routed via parent. I don't your patch is still useful, but not to us. So the documentation should be clear about that. > * ALTER TABLE .. DETACH PARTITION FINALIZE now waits for concurrent old > snapshots, as previously discussed. This should ensure that the user > doesn't just cancel the wait during the second transaction by Ctrl-C and > run FINALIZE immediately afterwards, which I claimed would bring > inconsistency. > > * Avoid creating the CHECK constraint if an identical one already > exists. > > (Note I do not remove the constraint on ATTACH. That seems pointless.) > > Still to do: test this using the new hook added by 6f0b632f083b. tab complete? > + * Ensure that foreign keys still hold after this detach. This keeps lock > + * on the referencing tables, which prevent concurrent transactions from keeps locks or which prevents > +++ b/doc/src/sgml/ref/alter_table.sgml > @@ -947,6 +950,24 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM > attached to the target table's indexes are detached. Any triggers that > were created as clones of those in the target table are removed. > </para> > + <para> > + If <literal>CONCURRENTLY</literal> is specified, this process runs in two > + transactions in order to avoid blocking other sessions that might be accessing > + the partitioned table. During the first transaction, > + <literal>SHARE UPDATE EXCLUSIVE</literal> is taken in both parent table and missing "lock" taken *on* ? > + partition, and the partition is marked detached; at that point, the transaction probably "its partition," > + If <literal>FINALIZE</literal> is specified, complete actions of a > + previous <literal>DETACH CONCURRENTLY</literal> invocation that > + was cancelled or crashed. say "actions are completed" or: If FINALIZE is specified, a previous DETACH that was cancelled or interrupted is completed. > + if (!inhdetached && detached) > + ereport(ERROR, > + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), > + errmsg("cannot complete detaching partition \"%s\"", > + childname), > + errdetail("There's no partial concurrent detach in progress."))); maybe say "partially-complete" or remove "partial" > + * the partition being detached? Putting them on the partition being > + * detached would be wrong, since they'd become "lost" after the but after *that* ? > + * Concurrent mode has to work harder; first we add a new constraint to the > + * partition that matches the partition constraint, if there isn't a matching > + * one already. The reason for this is that the planner may have made > + * optimizations that depend on the constraint. XXX Isn't it sufficient to > + * invalidate the partition's relcache entry? Ha. I suggested this years ago. https://www.postgresql.org/message-id/20180601221428.GU5164@telsasoft.com |. The docs say: if detaching/re-attach a partition, should first ADD CHECK to | avoid a slow ATTACH operation. Perhaps DETACHing a partition could | implicitly CREATE a constraint which is usable when reATTACHing? > + * Then we close our existing transaction, and in a new one wait for > + * all process to catch up on the catalog updates we've done so far; at processes > + * We don't need to concern ourselves with waiting for a lock the > + * partition itself, since we will acquire AccessExclusiveLock below. lock *on* ? > + * If asked to, wait until existing snapshots are gone. This is important > + * in the second transaction of DETACH PARTITION CONCURRENTLY is canceled: s/in/if/ > +++ b/src/bin/psql/describe.c > - printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name, > - partdef); > + printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name, > + partdef, > + strcmp(detached, "t") == 0 ? " DETACHED" : ""); The attname "detached" is a stretch of what's intuitive (it's more like "detachING" or half-detached). But I think psql should for sure show something more obvious to users. Esp. seeing as psql output isn't documented. Let's figure out what to show to users and then maybe rename the column that, too. > +PG_KEYWORD("finalize", FINALIZE, UNRESERVED_KEYWORD, BARE_LABEL) Instead of finalize .. deferred ? Or ?? ATExecDetachPartition: Doesn't this need to lock the table before testing for default partition ? I ended up with apparently broken constraint when running multiple loops around a concurrent detach / attach: while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& "p1_check" CHECK (true) "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2020-12-01T15:25:19Z
Hi Justin, Thanks for all the comments. I'll incorporate everything and submit an updated version later. On 2020-Nov-30, Justin Pryzby wrote: > On Tue, Nov 03, 2020 at 08:56:06PM -0300, Alvaro Herrera wrote: > > +++ b/src/bin/psql/describe.c > > - printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name, > > - partdef); > > + printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name, > > + partdef, > > + strcmp(detached, "t") == 0 ? " DETACHED" : ""); > > The attname "detached" is a stretch of what's intuitive (it's more like > "detachING" or half-detached). But I think psql should for sure show something > more obvious to users. Esp. seeing as psql output isn't documented. Let's > figure out what to show to users and then maybe rename the column that, too. OK. I agree that "being detached" is the state we want users to see, or maybe "detach pending", or "unfinisheddetach" (ugh). I'm not sure that pg_inherits.inhbeingdetached" is a great column name. Opinions welcome. > > +PG_KEYWORD("finalize", FINALIZE, UNRESERVED_KEYWORD, BARE_LABEL) > > Instead of finalize .. deferred ? Or ?? Well, I'm thinking that this has to be a verb in the imperative mood. The user is commanding the server to "finalize this detach operation". I'm not sure that DEFERRED fits that grammatical role. If there are other ideas, let's discuss them. ALTER TABLE tst DETACH PARTITION tst_1 FINALIZE <-- decent ALTER TABLE tst DETACH PARTITION tst_1 COMPLETE <-- I don't like it ALTER TABLE tst DETACH PARTITION tst_1 DEFERRED <-- grammatically faulty? > ATExecDetachPartition: > Doesn't this need to lock the table before testing for default partition ? Correct, it does. > I ended up with apparently broken constraint when running multiple loops around > a concurrent detach / attach: > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > "p1_check" CHECK (true) > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) Not good. -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Andy Fan <zhihui.fan1213@gmail.com> — 2020-12-25T08:02:05Z
On Tue, Aug 4, 2020 at 7:49 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote: > I've been working on the ability to detach a partition from a > partitioned table, without causing blockages to concurrent activity. > I think this operation is critical for some use cases. > > This would be a very great feature. When we can't handle thousands of partitions very well, and user agree to detach some old partitions automatically, the blocking issue here would be a big blocker for this solution. Thanks for working on this! > There was a lot of great discussion which ended up in Robert completing > a much sought implementation of non-blocking ATTACH. DETACH was > discussed too because it was a goal initially, but eventually dropped > from that patch altogether. Nonetheless, that thread provided a lot of > useful input to this implementation. Important ones: > > [1] > https://postgr.es/m/CA+TgmoYg4x7AH=_QSptvuBKf+3hUdiCa4frPkt+RvXZyjX1n=w@mail.gmail.com > [2] > https://postgr.es/m/CA+TgmoaAjkTibkEr=xJg3ndbRsHHSiYi2SJgX69MVosj=LJmug@mail.gmail.com > [3] > https://postgr.es/m/CA+TgmoY13KQZF-=HNTrt9UYWYx3_oYOQpu9ioNT49jGgiDpUEA@mail.gmail.com > > Attached is a patch that implements > ALTER TABLE ... DETACH PARTITION .. CONCURRENTLY. > > In the previous thread we were able to implement the concurrent model > without the extra keyword. For this one I think that won't work; my > implementation works in two transactions so there's a restriction that > you can't run it in a transaction block. Also, there's a wait phase > that makes it slower than the non-concurrent one. Those two drawbacks > make me think that it's better to keep both modes available, just like > we offer both CREATE INDEX and CREATE INDEX CONCURRENTLY. > > Why two transactions? The reason is that in order for this to work, we > make a catalog change (mark it detached), and commit so that all > concurrent transactions can see the change. A second transaction waits > for anybody who holds any lock on the partitioned table and grabs Access > Exclusive on the partition (which now no one cares about, if they're > looking at the partitioned table), where the DDL action on the partition > can be completed. > > ALTER TABLE is normally unable to run in two transactions. I hacked it > (0001) so that the relation can be closed and reopened in the Exec phase > (by having the rel as part of AlteredTableInfo: when ATRewriteCatalogs > returns, it uses that pointer to close the rel). It turns out that this > is sufficient to make that work. This means that ALTER TABLE DETACH > CONCURRENTLY cannot work as part of a multi-command ALTER TABLE, but > that's alreay enforced by the grammar anyway. > > DETACH CONCURRENTLY doesn't work if a default partition exists. It's > just too problematic a case; you would still need to have AEL on the > default partition. > > > I haven't yet experimented with queries running in a standby in tandem > with a detach. > > -- > Álvaro Herrera > -- Best Regards Andy Fan
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-01-08T19:14:33Z
On 2020-Dec-01, Alvaro Herrera wrote: > On 2020-Nov-30, Justin Pryzby wrote: > Thanks for all the comments. I'll incorporate everything and submit an > updated version later. Here's a rebased version 5, with the typos fixed. More comments below. > > The attname "detached" is a stretch of what's intuitive (it's more like > > "detachING" or half-detached). But I think psql should for sure show something > > more obvious to users. Esp. seeing as psql output isn't documented. Let's > > figure out what to show to users and then maybe rename the column that, too. > > OK. I agree that "being detached" is the state we want users to see, or > maybe "detach pending", or "unfinisheddetach" (ugh). I'm not sure that > pg_inherits.inhbeingdetached" is a great column name. Opinions welcome. I haven't changed this yet; I can't make up my mind about what I like best. Partition of: parent FOR VALUES IN (1) UNFINISHED DETACH Partition of: parent FOR VALUES IN (1) UNDER DETACH Partition of: parent FOR VALUES IN (1) BEING DETACHED > > ATExecDetachPartition: > > Doesn't this need to lock the table before testing for default partition ? > > Correct, it does. I failed to point out that by the time ATExecDetachPartition is called, the relation has already been locked by the invoking ALTER TABLE support code. > > I ended up with apparently broken constraint when running multiple loops around > > a concurrent detach / attach: > > > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > > > "p1_check" CHECK (true) > > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > Not good. Haven't had time to investigate this problem yet. -- Álvaro Herrera
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-01-10T22:15:41Z
On Fri, Jan 08, 2021 at 04:14:33PM -0300, Alvaro Herrera wrote: > > > I ended up with apparently broken constraint when running multiple loops around > > > a concurrent detach / attach: > > > > > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > > > > > "p1_check" CHECK (true) > > > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > > > Not good. > > Haven't had time to investigate this problem yet. I guess it's because you commited the txn and released lock in the middle of the command. -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-02-26T20:32:36Z
On 2021-Jan-10, Justin Pryzby wrote: > On Fri, Jan 08, 2021 at 04:14:33PM -0300, Alvaro Herrera wrote: > > > > I ended up with apparently broken constraint when running multiple loops around > > > > a concurrent detach / attach: > > > > > > > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > > > while psql -h /tmp postgres -c "ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2)" -c "ALTER TABLE p DETACH PARTITION p1 CONCURRENTLY"; do :; done& > > > > > > > > "p1_check" CHECK (true) > > > > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > > > > > Not good. > > > > Haven't had time to investigate this problem yet. > > I guess it's because you commited the txn and released lock in the middle of > the command. Hmm, but if we take this approach, then we're still vulnerable to the problem that somebody can do DETACH CONCURRENTLY and cancel the wait (or crash the server), then mess up the state before doing DETACH FINALIZE: when they cancel the wait, the lock will be released. I think the right fix is to disallow any action on a partition which is pending detach other than DETACH FINALIZE. (Didn't do that here.) Here's a rebase to current sources; there are no changes from v5. -- Álvaro Herrera Valdivia, Chile "No hay ausente sin culpa ni presente sin disculpa" (Prov. francés)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-11T16:26:51Z
Rebase to current sources, to appease CF bot; no other changes. -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-15T23:04:37Z
On 2021-Feb-26, Alvaro Herrera wrote: > Hmm, but if we take this approach, then we're still vulnerable to the > problem that somebody can do DETACH CONCURRENTLY and cancel the wait (or > crash the server), then mess up the state before doing DETACH FINALIZE: > when they cancel the wait, the lock will be released. > > I think the right fix is to disallow any action on a partition which is > pending detach other than DETACH FINALIZE. (Didn't do that here.) Here's a fixup patch to do it that way. I tried running the commands you showed and one of them immediately dies with the new error message; I can't cause the bogus constraint to show up anymore. I'll clean this up for a real submission tomorrow. -- Álvaro Herrera 39°49'30"S 73°17'W "The Gord often wonders why people threaten never to come back after they've been told never to return" (www.actsofgord.com)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-17T17:48:43Z
On 2021-Mar-15, Alvaro Herrera wrote: > Here's a fixup patch to do it that way. I tried running the commands > you showed and one of them immediately dies with the new error message; > I can't cause the bogus constraint to show up anymore. Actually, that was a silly fix that didn't actually work correctly, as I discovered immediately after sending it. The right fix is to forbid all commands other than DETACH PARTITION FINALIZE in a partition that's in the process of being detached. In the attached v8, I did that; I also added a ton more tests that hopefully show how the feature should work in concurrent cases, including one case in which the transaction doing the detach is cancelled. I also renamed "inhdetached" to "inhdetachpending", per previous discussion, including changing how to looks in psql. I am not aware of any other loose end in this patch; I consider this version final. Barring further problem reports, I'll get this pushed tomorrow morning. psql completion is missing. If somebody would like to contribute that, I'm grateful. -- Álvaro Herrera 39°49'30"S 73°17'W "If you have nothing to say, maybe you need just the right tool to help you not say it." (New York Times, about Microsoft PowerPoint)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-03-17T18:45:34Z
The v8 patch has the "broken constraint" problem. Also, it "fails to avoid" adding duplicate constraints: Check constraints: "c" CHECK (i IS NOT NULL AND i > 1 AND i < 2) "cc" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) "p1_check" CHECK (true) "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml > index 5c9f4af1d5..0cb846f408 100644 > --- a/doc/src/sgml/catalogs.sgml > +++ b/doc/src/sgml/catalogs.sgml > @@ -4485,6 +4485,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l > when using declarative partitioning. > </para></entry> > </row> > + > + <row> > + <entry role="catalog_table_entry"><para role="column_definition"> > + <structfield>inhdetachpending</structfield> <type>bool</type> > + </para> > + <para> > + Set to true for a partition that is in the process of being detached; > + false otherwise. > + </para></entry> > + </row> Remove "Set to" ? And say <literal>true</literal> and <literal>false</literal> Probably you'll hate the suggestion, but maybe it should be "pendingdetach". We already have pg_settings.pending_restart. > + If <literal>CONCURRENTLY</literal> is specified, this process runs in two > + transactions in order to avoid blocking other sessions that might be accessing > + the partitioned table. During the first transaction, a > + <literal>SHARE UPDATE EXCLUSIVE</literal> lock is taken on both parent table and > + partition, and its partition is marked detached; at that point, the transaction > + is committed and all transactions using the partitioned table are waited for. > + Once all those transactions are gone, the second stage acquires Instead of "gone", say "have completed" ? > +/* > + * MarkInheritDetached > + * > + * When a partition is detached from its parent concurrently, we don't > + * remove the pg_inherits row until a second transaction; as a preparatory > + * step, this function marks the entry as 'detached', so that other *pending detached > + * The strategy for concurrency is to first modify the partition catalog > + * rows to make it visible to everyone that the partition is detached, the inherits catalog? > + /* > + * In concurrent mode, the partition is locked with share-update-exclusive > + * in the first transaction. This allows concurrent transactions to be > + * doing DML to the partition. > + /* > + * Check inheritance conditions and either delete the pg_inherits row > + * (in non-concurrent mode) or just set the inhisdetached flag. detachpending -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-19T13:57:37Z
On 2021-Mar-17, Justin Pryzby wrote: > The v8 patch has the "broken constraint" problem. Yeah, I had misunderstood what the problem was. I think a good solution to this is to have get_partition_parent return the true parent even when a detach is pending, for one particular callsite. (This means adjusting all other callsites.) Notpatch attached (applies on top of v8). > Also, it "fails to avoid" adding duplicate constraints: > > Check constraints: > "c" CHECK (i IS NOT NULL AND i > 1 AND i < 2) > "cc" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > "p1_check" CHECK (true) > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) Do you mean the "cc" and "p1_i_check" one? I mean, if you already have a constraint in the partition that duplicates the partition constraint, then during attach we still create our new constraint? I guess a solution to this would be to scan all constraints and see if any equals the expression that the new one would have. Sounds easy enough now that write it out loud. Thanks -- Álvaro Herrera 39°49'30"S 73°17'W "Java is clearly an example of money oriented programming" (A. Stepanov)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-03-21T17:54:53Z
On Fri, Mar 19, 2021 at 10:57:37AM -0300, Alvaro Herrera wrote: > > Also, it "fails to avoid" adding duplicate constraints: > > > > Check constraints: > > "c" CHECK (i IS NOT NULL AND i > 1 AND i < 2) > > "cc" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > "p1_check" CHECK (true) > > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > Do you mean the "cc" and "p1_i_check" one? I mean, if you already have No, I started with c and cc, and it added the broken constraint p1_check (which you say you've fixed) and the redundant constraint p1_i_check. I guess that's what you meant. > a constraint in the partition that duplicates the partition constraint, > then during attach we still create our new constraint? I guess a > solution to this would be to scan all constraints and see if any equals > the expression that the new one would have. Sounds easy enough now that > write it out loud. But it looks like DetachAddConstraintIfNeeded already intended to do that: + if (equal(constraintExpr, thisconstr)) + return; Actually, it appears your latest notpatch resolves both these issues. But note that it doesn't check if an existing constraint "implies" the new constraint - maybe it should. -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-21T18:01:15Z
On 2021-Mar-21, Justin Pryzby wrote: > On Fri, Mar 19, 2021 at 10:57:37AM -0300, Alvaro Herrera wrote: > > > Also, it "fails to avoid" adding duplicate constraints: > > > > > > Check constraints: > > > "c" CHECK (i IS NOT NULL AND i > 1 AND i < 2) > > > "cc" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > > "p1_check" CHECK (true) > > > "p1_i_check" CHECK (i IS NOT NULL AND i >= 1 AND i < 2) > > > > Do you mean the "cc" and "p1_i_check" one? I mean, if you already have > > No, I started with c and cc, and it added the broken constraint p1_check (which > you say you've fixed) and the redundant constraint p1_i_check. I guess that's > what you meant. Yes, that's what I meant. > > a constraint in the partition that duplicates the partition constraint, > > then during attach we still create our new constraint? I guess a > > solution to this would be to scan all constraints and see if any equals > > the expression that the new one would have. Sounds easy enough now that > > write it out loud. > > But it looks like DetachAddConstraintIfNeeded already intended to do that: > > + if (equal(constraintExpr, thisconstr)) > + return; Hah, so I had already done it, but forgot. > Actually, it appears your latest notpatch resolves both these issues. Great. > But note that it doesn't check if an existing constraint "implies" the new > constraint - maybe it should. Hm, I'm not sure I want to do that, because that means that if I later have to attach the partition again with the same partition bounds, then I might have to incur a scan to recheck the constraint. I think we want to make the new constraint be as tight as possible ... -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-21T18:06:45Z
On 2021-Mar-19, Alvaro Herrera wrote: > diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c > index 0fe4f55b04..6dfa3fb4a8 100644 > --- a/src/backend/utils/cache/partcache.c > +++ b/src/backend/utils/cache/partcache.c > @@ -352,16 +352,9 @@ generate_partition_qual(Relation rel) > return copyObject(rel->rd_partcheck); > > /* > - * Obtain parent relid; if it's invalid, then the partition is being > - * detached. The constraint is NIL in that case, and let's cache that. > + * Obtain parent relid. XXX explain why we need this > */ > - parentrelid = get_partition_parent(RelationGetRelid(rel)); > - if (parentrelid == InvalidOid) > - { > - rel->rd_partcheckvalid = true; > - rel->rd_partcheck = NIL; > - return NIL; > - } > + parentrelid = get_partition_parent(RelationGetRelid(rel), true); One thing that makes me uneasy about this, is that I don't understand how does this happen with your test of two psqls doing attach/detach. (It is necessary for the case when the waiting concurrent detach is canceled, and so this fix is necessary anyway). In your test, no waiting transaction is ever cancelled; so what is the period during which the relation is not locked that causes this code to be hit? I fear that there's a bug in the lock protocol somewhere. -- Álvaro Herrera Valdivia, Chile "In Europe they call me Niklaus Wirth; in the US they call me Nickel's worth. That's because in Europe they call me by name, and in the US by value!" -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-03-21T18:14:20Z
On Sun, Mar 21, 2021 at 03:01:15PM -0300, Alvaro Herrera wrote: > > But note that it doesn't check if an existing constraint "implies" the new > > constraint - maybe it should. > > Hm, I'm not sure I want to do that, because that means that if I later > have to attach the partition again with the same partition bounds, then > I might have to incur a scan to recheck the constraint. I think we want > to make the new constraint be as tight as possible ... The ATTACH PARTITION checks if any existing constraint impilies the (proposed) partition bounds, not just if constraints are equal. So I'm suggesting to do the same here. -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-21T18:22:00Z
On 2021-Mar-21, Justin Pryzby wrote: > On Sun, Mar 21, 2021 at 03:01:15PM -0300, Alvaro Herrera wrote: > > > But note that it doesn't check if an existing constraint "implies" the new > > > constraint - maybe it should. > > > > Hm, I'm not sure I want to do that, because that means that if I later > > have to attach the partition again with the same partition bounds, then > > I might have to incur a scan to recheck the constraint. I think we want > > to make the new constraint be as tight as possible ... > > The ATTACH PARTITION checks if any existing constraint impilies the (proposed) > partition bounds, not just if constraints are equal. So I'm suggesting to do > the same here. So if we do that on DETACH, what would happen on ATTACH? -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-03-21T18:29:03Z
On Sun, Mar 21, 2021 at 03:22:00PM -0300, Alvaro Herrera wrote: > On 2021-Mar-21, Justin Pryzby wrote: > > > On Sun, Mar 21, 2021 at 03:01:15PM -0300, Alvaro Herrera wrote: > > > > But note that it doesn't check if an existing constraint "implies" the new > > > > constraint - maybe it should. > > > > > > Hm, I'm not sure I want to do that, because that means that if I later > > > have to attach the partition again with the same partition bounds, then > > > I might have to incur a scan to recheck the constraint. I think we want > > > to make the new constraint be as tight as possible ... > > > > The ATTACH PARTITION checks if any existing constraint impilies the (proposed) > > partition bounds, not just if constraints are equal. So I'm suggesting to do > > the same here. > > So if we do that on DETACH, what would happen on ATTACH? Do you mean what happens to the constraint that was already there ? Nothing, since it's not ours to mess with. Checking ImpliedBy() rather than equal() doesn't change that. I proposed this a few years ago for DETACH (without concurrently), specifically to avoid the partition scans. https://www.postgresql.org/message-id/20180601221428.GU5164@telsasoft.com |The docs say: if detaching/re-attach a partition, should first ADD CHECK to |avoid a slow ATTACH operation. Perhaps DETACHing a partition could |implicitly CREATE a constraint which is usable when reATTACHing? -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-21T19:07:12Z
On 2021-Mar-21, Justin Pryzby wrote: > On Sun, Mar 21, 2021 at 03:22:00PM -0300, Alvaro Herrera wrote: > > > So if we do that on DETACH, what would happen on ATTACH? > > Do you mean what happens to the constraint that was already there ? > Nothing, since it's not ours to mess with. Checking ImpliedBy() rather than > equal() doesn't change that. No, I meant what happens regarding checking existing values in the table: is the table scanned even if the partition constraint is implied by existing table constraints? > I proposed this a few years ago for DETACH (without concurrently), specifically > to avoid the partition scans. > https://www.postgresql.org/message-id/20180601221428.GU5164@telsasoft.com > |The docs say: if detaching/re-attach a partition, should first ADD CHECK to > |avoid a slow ATTACH operation. Perhaps DETACHing a partition could > |implicitly CREATE a constraint which is usable when reATTACHing? Well, I agree with you that we should add such a constraint. -- Álvaro Herrera Valdivia, Chile "The problem with the future is that it keeps turning into the present" (Hobbes)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-03-21T19:15:19Z
On Sun, Mar 21, 2021 at 01:14:20PM -0500, Justin Pryzby wrote: > On Sun, Mar 21, 2021 at 03:01:15PM -0300, Alvaro Herrera wrote: > > > But note that it doesn't check if an existing constraint "implies" the new > > > constraint - maybe it should. > > > > Hm, I'm not sure I want to do that, because that means that if I later > > have to attach the partition again with the same partition bounds, then > > I might have to incur a scan to recheck the constraint. I think we want > > to make the new constraint be as tight as possible ... > > The ATTACH PARTITION checks if any existing constraint impilies the (proposed) > partition bounds, not just if constraints are equal. So I'm suggesting to do > the same here. On Sun, Mar 21, 2021 at 04:07:12PM -0300, Alvaro Herrera wrote: > On 2021-Mar-21, Justin Pryzby wrote: > > On Sun, Mar 21, 2021 at 03:22:00PM -0300, Alvaro Herrera wrote: > > > So if we do that on DETACH, what would happen on ATTACH? > > > > Do you mean what happens to the constraint that was already there ? > > Nothing, since it's not ours to mess with. Checking ImpliedBy() rather than > > equal() doesn't change that. > > No, I meant what happens regarding checking existing values in the > table: is the table scanned even if the partition constraint is implied > by existing table constraints? I'm still not sure we're talking about the same thing. Your patch adds a CHECK constraint during DETACH CONCURRENTLY, and I suggested that it should avoid adding it if it's redundant with an existing constraint, even if not equal(). The current behavior (since v10) is this: postgres=# ALTER TABLE p ATTACH PARTITION p1 FOR VALUES FROM (1)TO(2); DEBUG: partition constraint for table "p1" is implied by existing constraints ALTER TABLE And that wouldn't change, except the CHECK constraint would be added automatically during detach (if it wasn't already implied). Maybe the CHECK constraint should be added without CONCURRENTLY, too. One fewer difference in behavior. -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-23T14:18:26Z
So I was about ready to get these patches pushed, when I noticed that in REPEATABLE READ isolation mode it is possible to insert rows violating an FK referencing the partition that is being detached. I'm not sure what is a good solution to this problem. The problem goes like this: /* setup */ drop table if exists d4_primary, d4_primary1, d4_fk; create table d4_primary (a int primary key) partition by list (a); create table d4_primary1 partition of d4_primary for values in (1); insert into d4_primary values (1); create table d4_fk (a int references d4_primary); /* session 1 */ begin isolation level repeatable read; select * from d4_primary; /* session 2 */ alter table d4_primary detach partition d4_primary1 concurrently; -- blocks -- Cancel wait: Ctrl-c /* session 1 */ insert into d4_fk values (1); commit; At this point, d4_fk contains the value (1) which is not present in d4_primary. This doesn't happen in READ COMMITTED mode; the INSERT at the final step fails with "insert or update in table f4_fk violates the foreign key", which is what I expected to happen here too. I had the idea that the RI code, in REPEATABLE READ mode, used a different snapshot for the RI queries than the transaction snapshot. Maybe I'm wrong about that. I'm looking into that now. -- Álvaro Herrera Valdivia, Chile "Cuando mañana llegue pelearemos segun lo que mañana exija" (Mowgli)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-23T14:55:54Z
On 2021-Mar-23, Alvaro Herrera wrote: > So I was about ready to get these patches pushed, when I noticed that in > REPEATABLE READ isolation mode it is possible to insert rows violating > an FK referencing the partition that is being detached. I'm not sure > what is a good solution to this problem. ... > I had the idea that the RI code, in REPEATABLE READ mode, used a > different snapshot for the RI queries than the transaction snapshot. I am definitely right about this. So why doesn't it work? The reason is that when SPI goes to execute the query, it obtains a new partition directory, and we tell it to include detached partitions precisely because we're in REPEATABLE READ mode. In other words, the idea that we can blanket use the snapshot-isolation condition to decide whether to include detached partitions or not, is bogus and needs at least the refinement that for any query that comes from the RI system, we need a partition directory that does not include detached partitions. -- Álvaro Herrera 39°49'30"S 73°17'W "El sabio habla porque tiene algo que decir; el tonto, porque tiene que decir algo" (Platon).
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-23T15:25:23Z
I'm coming around to the idea that the fact that you can cancel the wait phase of DETACH CONCURRENTLY creates quite a disaster, and it's not easy to get away from it. The idea that REPEATABLE READ mode means that you now see detached partitions as if they were in normal condition, is completely at odds with that behavior. I think a possible solution to this problem is that the "detach" flag in pg_inherits is not a boolean anymore, but an Xid (or maybe two Xids). Not sure exactly which Xid(s) yet, and I'm not sure what are the exact rules, but the Xid becomes a marker that indicates an horizon past which the partition is no longer visible. Then, REPEATABLE READ can see the partition, but only if its snapshot is older than the Xid. -- Álvaro Herrera Valdivia, Chile "La persona que no quería pecar / estaba obligada a sentarse en duras y empinadas sillas / desprovistas, por cierto de blandos atenuantes" (Patricio Vogel)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-25T15:50:39Z
On 2021-Mar-23, Alvaro Herrera wrote: > I think a possible solution to this problem is that the "detach" flag in > pg_inherits is not a boolean anymore, but an Xid (or maybe two Xids). > Not sure exactly which Xid(s) yet, and I'm not sure what are the exact > rules, but the Xid becomes a marker that indicates an horizon past which > the partition is no longer visible. Then, REPEATABLE READ can see the > partition, but only if its snapshot is older than the Xid. So a solution to this problem seems similar (but not quite the same) as pg_index.indcheckxmin: the partition is included in the partition directory, or not, depending on the pg_inherits tuple visibility for the active snapshot. This solves the problem because the RI query uses a fresh snapshot, for which the partition has already been detached, while the normal REPEATABLE READ query is using the old snapshot for which the 'detach-pending' row is still seen as in progress. With this, the weird hack in a couple of places that needed to check the isolation level is gone, which makes me a bit more comfortable. So attached is v9 with this problem solved. I'll add one more torture test, and if it works correctly I'll push it: have a cursor in the repeatable read transaction, which can read the referenced partition table and see the row in the detached partition, but the RI query must not see that row. Bonus: the RI query is run from another cursor that is doing UPDATE WHERE CURRENT OF that cursor. -- Álvaro Herrera 39°49'30"S 73°17'W "Estoy de acuerdo contigo en que la verdad absoluta no existe... El problema es que la mentira sí existe y tu estás mintiendo" (G. Lama)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-25T21:03:36Z
I added that test as promised, and I couldn't find any problems, so I have pushed it. Thanks! -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-03-25T21:08:27Z
On 2020-Nov-30, Justin Pryzby wrote: > On Tue, Nov 03, 2020 at 08:56:06PM -0300, Alvaro Herrera wrote: > > * On the first transaction (the one that marks the partition as > > detached), the partition is locked with ShareLock rather than > > ShareUpdateExclusiveLock. This means that DML is not allowed anymore. > > This seems a reasonable restriction to me; surely by the time you're > > detaching the partition you're not inserting data into it anymore. > > I don't think it's an issue with your patch, but FYI that sounds like something > I had to do recently: detach *all* partitions of various tabls to promote their > partition key column from timestamp to timestamptz. And we insert directly > into child tables, not routed via parent. > > I don't your patch is still useful, but not to us. So the documentation should > be clear about that. FWIW since you mentioned this detail specifically: I backed away from doing this (and use ShareUpdateExclusive), because it wasn't buying us anything anyway. The reason for it is that I wanted to close the hole for RI queries, and this seemed the simplest fix; but it really *wasn't* a fix anyway. My later games with the active snapshot (which are present in the version I pushed) better close this problem. So I don't think this would be a problem. -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-31T18:30:12Z
Alvaro Herrera <alvherre@alvh.no-ip.org> writes: > I added that test as promised, and I couldn't find any problems, so I > have pushed it. Buildfarm testing suggests there's an issue under CLOBBER_CACHE_ALWAYS: https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=trilobite&dt=2021-03-29%2018%3A14%3A24 specifically diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 @@ -324,6 +324,7 @@ 1 2 step s1insert: insert into d4_fk values (1); +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" step s1c: commit; starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c regards, tom lane
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-04-10T18:42:26Z
On Sun, Mar 21, 2021 at 03:01:15PM -0300, Alvaro Herrera wrote: > > But note that it doesn't check if an existing constraint "implies" the new > > constraint - maybe it should. > > Hm, I'm not sure I want to do that, because that means that if I later > have to attach the partition again with the same partition bounds, then > I might have to incur a scan to recheck the constraint. I think we want > to make the new constraint be as tight as possible ... If it *implies* the partition constraint, then it's at least as tight (and maybe tighter), yes ? I think you're concerned with the case that someone has a partition with "tight" bounds like (a>=200 and a<300) and a check constraint that's "less tight" like (a>=100 AND a<400). In that case, the loose check constraint doesn't imply the tighter partition constraint, so your patch would add a non-redundant constraint. I'm interested in the case that someone has a check constraint that almost but not exactly matches the partition constraint, like (a<300 AND a>=200). In that case, your patch adds a redundant constraint. I wrote a patch which seems to effect my preferred behavior - please check. -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-11T21:20:35Z
On 2021-Mar-31, Tom Lane wrote: > diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out > --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 > +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 > @@ -324,6 +324,7 @@ > 1 > 2 > step s1insert: insert into d4_fk values (1); > +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > step s1c: commit; > > starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c Hmm, actually, looking at this closely, I think the expected output is bogus and trilobite is doing the right thing by throwing this error here. The real question is why isn't this case behaving in that way in every *other* animal. -- Álvaro Herrera Valdivia, Chile "Puedes vivir sólo una vez, pero si lo haces bien, una vez es suficiente"
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Justin Pryzby <pryzby@telsasoft.com> — 2021-04-11T21:23:00Z
On Sun, Apr 11, 2021 at 05:20:35PM -0400, Alvaro Herrera wrote: > On 2021-Mar-31, Tom Lane wrote: > > > diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out > > --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 > > +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 > > @@ -324,6 +324,7 @@ > > 1 > > 2 > > step s1insert: insert into d4_fk values (1); > > +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > > step s1c: commit; > > > > starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c > > Hmm, actually, looking at this closely, I think the expected output is > bogus and trilobite is doing the right thing by throwing this error > here. The real question is why isn't this case behaving in that way in > every *other* animal. I was looking/thinking at it, and wondered whether it could be a race condition involving pg_cancel_backend() -- Justin
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-12T07:42:50Z
On Mon, Apr 12, 2021 at 6:20 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On 2021-Mar-31, Tom Lane wrote: > > > diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out > > --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 > > +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 > > @@ -324,6 +324,7 @@ > > 1 > > 2 > > step s1insert: insert into d4_fk values (1); > > +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > > step s1c: commit; > > > > starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c > > Hmm, actually, looking at this closely, I think the expected output is > bogus and trilobite is doing the right thing by throwing this error > here. The real question is why isn't this case behaving in that way in > every *other* animal. Indeed. I can see a wrong behavior of RelationGetPartitionDesc() in a case that resembles the above test case. drop table if exists d4_primary, d4_primary1, d4_fk, d4_pid; create table d4_primary (a int primary key) partition by list (a); create table d4_primary1 partition of d4_primary for values in (1); create table d4_primary2 partition of d4_primary for values in (2); insert into d4_primary values (1); insert into d4_primary values (2); create table d4_fk (a int references d4_primary); insert into d4_fk values (2); create table d4_pid (pid int); Session 1: begin isolation level serializable; select * from d4_primary; a --- 1 2 (2 rows) Session 2: alter table d4_primary detach partition d4_primary1 concurrently; <waits> Session 1: -- can see d4_primary1 as detached at this point, though still scans! select * from d4_primary; a --- 1 2 (2 rows) insert into d4_fk values (1); INSERT 0 1 end; Session 2: <alter-finishes> ALTER TABLE Session 1: -- FK violated select * from d4_primary; a --- 2 (1 row) select * from d4_fk; a --- 1 (1 row) The 2nd select that session 1 performs adds d4_primary1, whose detach it *sees* is pending, to the PartitionDesc, but without setting its includes_detached. The subsequent insert's RI query, because it uses that PartitionDesc as-is, returns 1 as being present in d4_primary, leading to the insert succeeding. When session 1's transaction commits, the waiting ALTER proceeds with committing the 2nd part of the DETACH, without having a chance again to check if the DETACH would lead to the foreign key of d4_fk being violated. It seems problematic to me that the logic of setting includes_detached is oblivious of the special check in find_inheritance_children() to decide whether "force"-include a detach-pending child based on cross-checking its pg_inherit tuple's xmin against the active snapshot. Maybe fixing that would help, although I haven't tried that myself yet. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-12T12:32:40Z
On Mon, Apr 12, 2021 at 4:42 PM Amit Langote <amitlangote09@gmail.com> wrote: > On Mon, Apr 12, 2021 at 6:20 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > > On 2021-Mar-31, Tom Lane wrote: > > > > > diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out > > > --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 > > > +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 > > > @@ -324,6 +324,7 @@ > > > 1 > > > 2 > > > step s1insert: insert into d4_fk values (1); > > > +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > > > step s1c: commit; > > > > > > starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c > > > > Hmm, actually, looking at this closely, I think the expected output is > > bogus and trilobite is doing the right thing by throwing this error > > here. The real question is why isn't this case behaving in that way in > > every *other* animal. > > Indeed. > > I can see a wrong behavior of RelationGetPartitionDesc() in a case > that resembles the above test case. > > drop table if exists d4_primary, d4_primary1, d4_fk, d4_pid; > create table d4_primary (a int primary key) partition by list (a); > create table d4_primary1 partition of d4_primary for values in (1); > create table d4_primary2 partition of d4_primary for values in (2); > insert into d4_primary values (1); > insert into d4_primary values (2); > create table d4_fk (a int references d4_primary); > insert into d4_fk values (2); > create table d4_pid (pid int); > > Session 1: > begin isolation level serializable; > select * from d4_primary; > a > --- > 1 > 2 > (2 rows) > > Session 2: > alter table d4_primary detach partition d4_primary1 concurrently; > <waits> > > Session 1: > -- can see d4_primary1 as detached at this point, though still scans! > select * from d4_primary; > a > --- > 1 > 2 > (2 rows) > insert into d4_fk values (1); > INSERT 0 1 > end; > > Session 2: > <alter-finishes> > ALTER TABLE > > Session 1: > -- FK violated > select * from d4_primary; > a > --- > 2 > (1 row) > select * from d4_fk; > a > --- > 1 > (1 row) > > The 2nd select that session 1 performs adds d4_primary1, whose detach > it *sees* is pending, to the PartitionDesc, but without setting its > includes_detached. The subsequent insert's RI query, because it uses > that PartitionDesc as-is, returns 1 as being present in d4_primary, > leading to the insert succeeding. When session 1's transaction > commits, the waiting ALTER proceeds with committing the 2nd part of > the DETACH, without having a chance again to check if the DETACH would > lead to the foreign key of d4_fk being violated. > > It seems problematic to me that the logic of setting includes_detached > is oblivious of the special check in find_inheritance_children() to > decide whether "force"-include a detach-pending child based on > cross-checking its pg_inherit tuple's xmin against the active > snapshot. Maybe fixing that would help, although I haven't tried that > myself yet. I tried that in the attached. It is indeed the above failing isolation test whose output needed to be adjusted. While at it, I tried rewording the comment around that special visibility check done to force-include detached partitions, as I got confused by the way it's worded currently. Actually, it may be a good idea to add some comments around the intended include_detached behavior in the places where PartitionDesc is used; e.g. set_relation_partition_info() lacks a one-liner on why it's okay for the planner to not see detached partitions. Or perhaps, a comment for includes_detached of PartitionDesc should describe the various cases in which it is true and the cases in which it is not. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-13T02:13:34Z
On Mon, Apr 12, 2021 at 6:23 AM Justin Pryzby <pryzby@telsasoft.com> wrote: > On Sun, Apr 11, 2021 at 05:20:35PM -0400, Alvaro Herrera wrote: > > On 2021-Mar-31, Tom Lane wrote: > > > > > diff -U3 /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out > > > --- /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/expected/detach-partition-concurrently-4.out 2021-03-29 20:14:21.258199311 +0200 > > > +++ /home/buildfarm/trilobite/buildroot/HEAD/pgsql.build/src/test/isolation/output_iso/results/detach-partition-concurrently-4.out 2021-03-30 18:54:34.272839428 +0200 > > > @@ -324,6 +324,7 @@ > > > 1 > > > 2 > > > step s1insert: insert into d4_fk values (1); > > > +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > > > step s1c: commit; > > > > > > starting permutation: s2snitch s1b s1s s2detach s1cancel s3vacfreeze s1s s1insert s1c > > > > Hmm, actually, looking at this closely, I think the expected output is > > bogus and trilobite is doing the right thing by throwing this error > > here. The real question is why isn't this case behaving in that way in > > every *other* animal. > > I was looking/thinking at it, and wondered whether it could be a race condition > involving pg_cancel_backend() I thought about it some and couldn't come up with an explanation as to why pg_cancel_backend() race might be a problem. Actually it occurred to me this morning that CLOBBER_CACHE_ALWAYS is what exposed this problem on this animal (not sure if other such animals did too though). With CLOBBER_CACHE_ALWAYS, a PartitionDesc will be built afresh on most uses. In this particular case, the RI query executed by the insert has to build a new one (for d4_primary), correctly excluding the detach-pending partition (d4_primary1) per the snapshot with which it is run. In normal builds, it would reuse the one built by an earlier query in the transaction, which does include the detach-pending partition, thus allowing the insert trying to insert a row referencing that partition to succeed. There is a provision in RelationGetPartitionDesc() to rebuild if any detach-pending partitions in the existing copy of PartitionDesc are not to be seen by the current query, but a bug mentioned in my earlier reply prevents that from kicking in. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-13T16:10:30Z
On 2021-Apr-13, Amit Langote wrote: > Actually it occurred to me this morning that CLOBBER_CACHE_ALWAYS is > what exposed this problem on this animal (not sure if other such > animals did too though). With CLOBBER_CACHE_ALWAYS, a PartitionDesc > will be built afresh on most uses. In this particular case, the RI > query executed by the insert has to build a new one (for d4_primary), > correctly excluding the detach-pending partition (d4_primary1) per the > snapshot with which it is run. In normal builds, it would reuse the > one built by an earlier query in the transaction, which does include > the detach-pending partition, thus allowing the insert trying to > insert a row referencing that partition to succeed. There is a > provision in RelationGetPartitionDesc() to rebuild if any > detach-pending partitions in the existing copy of PartitionDesc are > not to be seen by the current query, but a bug mentioned in my earlier > reply prevents that from kicking in. Right -- that explanation makes perfect sense: the problem stems from the fact that the cached copy of the partition descriptor is not valid depending on the visibility of detached partitions for the operation that requests the descriptor. I think your patch is a critical part to a complete solution, but one thing is missing: we don't actually know that the detached partitions we see now are the same detached partitions we saw a moment ago. After all, a partitioned table can have several partitions in the process of being detached; so if you just go with the boolean "does it have any detached or not" bit, you could end up with a descriptor that doesn't include/ignore *all* detached partitions, just the older one(s). I think you could fix this aspect easily by decreeing that you can only have only one partition-being-detached at one point. So if you try to DETACH CONCURRENTLY and there's another one in that state, raise an error. Maybe for simplicity we should do that anyway. But I think there's another hidden assumption in your patch, which is that the descriptor is rebuilt every now and then *anyway* because the flag for detached flips between parser and executor, and because we send invalidation messages for each detach. I don't think we would ever change things that would break this flipping (it sounds like planner and executor necessarily have to be doing things differently all the time), but it seems fragile as heck. I would feel much safer if we just avoided caching the wrong thing ... or perhaps keep a separate cache entry (one descriptor including detached, another one not), to avoid pointless rebuilds. -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-20T22:41:07Z
OK so after mulling this over for a long time, here's a patch for this. It solves the problem by making the partition descriptor no longer be cached if a detached partition is omitted. Any transaction that needs a partition descriptor that excludes detached partitions, will have to recreate the partdesc from scratch. To support this, I changed the output boolean semantics: instead of "does this partdesc include an detached partitions" as in your patch, it now is "are there any detached partitions". But whenever no detached partitions exist, or when all partitions including detached are requested, then the cached descriptor is returned (because that necessarily has to be correct). The main difference this has to your patch is that we always keep the descriptor in the cache and don't rebuild anything, unless a detached partition is present. I flipped the find_inheritance_children() input boolean, from "include_detached" to "omit_detached". This is more natural, given the internal behavior. You could argue to propagate that naming change to the partdesc.h API and PartitionDirectory, but I don't think there's a need for that. I ran all the detach-partition-concurrently tests under debug_invalidate_system_caches_always=1 and everything passes. I experimented with keeping a separate cached partition descriptor that omits the detached partition, but that brings back some trouble; I couldn't find a way to invalidate such a cached entry in a reasonable way. I have the patch for that, if somebody wants to play with it. -- Álvaro Herrera Valdivia, Chile "That sort of implies that there are Emacs keystrokes which aren't obscure. I've been using it daily for 2 years now and have yet to discover any key sequence which makes any sense." (Paul Thomas)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-21T00:46:49Z
Actually I had a silly bug in the version that attempted to cache a partdesc that omits detached partitions. This one, while not fully baked, seems to work correctly (on top of the previous one). The thing that I don't fully understand is why we have to require to have built the regular one first. -- Álvaro Herrera 39°49'30"S 73°17'W "This is what I like so much about PostgreSQL. Most of the surprises are of the "oh wow! That's cool" Not the "oh shit!" kind. :)" Scott Marlowe, http://archives.postgresql.org/pgsql-admin/2008-10/msg00152.php
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-21T20:38:55Z
While the approach in the previous email does pass the tests, I think (but couldn't find a test case to prove) it does so coincidentally, not because it is correct. If I make the test for "detached exist" use the cached omits-partitions-partdesc, it does fail, because we had previously cached one that was not yet omitting the partition. So what I said earlier in the thread stands: the set of partitions that are considered detached changes depending on what the active snapshot is, and therefore we *must not* cache any such descriptor. So I backtracked to my previous proposal, which saves in relcache only the partdesc that includes all partitions. If any partdesc is built that omits partitions being detached, that one must be rebuilt afresh each time. And to avoid potentially saving a lot of single-use partdescs in CacheMemoryContext, in the attached second patch (which I attach separately only to make it more obvious to review) I store such partdescs in PortalContext. Barring objections, I will get this pushed early tomorrow. -- Álvaro Herrera Valdivia, Chile "Just treat us the way you want to be treated + some extra allowance for ignorance." (Michael Brusser)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-21T22:12:48Z
On 2021-Apr-10, Justin Pryzby wrote: > If it *implies* the partition constraint, then it's at least as tight (and > maybe tighter), yes ? > > I think you're concerned with the case that someone has a partition with > "tight" bounds like (a>=200 and a<300) and a check constraint that's "less > tight" like (a>=100 AND a<400). In that case, the loose check constraint > doesn't imply the tighter partition constraint, so your patch would add a > non-redundant constraint. ... yeah, you're right, we can do as you suggest and it seems an improvement. I verified, as is obvious in hindsight, that the existing constraint makes a future ATTACH of the partition with the same bounds as before not scan the partition. I pushed the patch with a small change: PartConstraintImpliedByRelConstraint wants the constraint in implicit-AND form (that is, a list) which is what we already have, so we can postpone make_ands_explicit() until later. Pushed, thanks, -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-22T09:56:25Z
(Sorry about being away from this for over a week.) On Thu, Apr 22, 2021 at 5:39 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > While the approach in the previous email does pass the tests, I think > (but couldn't find a test case to prove) it does so coincidentally, not > because it is correct. If I make the test for "detached exist" use the > cached omits-partitions-partdesc, it does fail, because we had > previously cached one that was not yet omitting the partition. So what > I said earlier in the thread stands: the set of partitions that are > considered detached changes depending on what the active snapshot is, > and therefore we *must not* cache any such descriptor. > > So I backtracked to my previous proposal, which saves in relcache only > the partdesc that includes all partitions. If any partdesc is built > that omits partitions being detached, that one must be rebuilt afresh > each time. And to avoid potentially saving a lot of single-use > partdescs in CacheMemoryContext, in the attached second patch (which I > attach separately only to make it more obvious to review) I store such > partdescs in PortalContext. > > Barring objections, I will get this pushed early tomorrow. Thanks for updating the patch. I have mostly cosmetic comments. Reading through the latest one, seeing both include_detached and omit_detached being used in different parts of the code makes it a bit hard to keep in mind what a given code path is doing wrt detached partitions. How about making it all omit_detached? * Cope with partitions concurrently being detached. When we see a - * partition marked "detach pending", we only include it in the set of - * visible partitions if caller requested all detached partitions, or - * if its pg_inherits tuple's xmin is still visible to the active - * snapshot. + * partition marked "detach pending", we omit it from the returned + * descriptor if caller requested that and the tuple's xmin does not + * appear in progress to the active snapshot. It seems odd for a comment in find_inheritance_children() to talk about the "descriptor". Maybe the earlier "set of visible partitions" wording was fine? - * The reason for this check is that we want to avoid seeing the + * The reason for this hack is that we want to avoid seeing the * partition as alive in RI queries during REPEATABLE READ or <snip> + * SERIALIZABLE transactions. The comment doesn't quite make it clear why it is the RI query case that necessitates this hack in the first case. Maybe the relation to what's going on with the partdesc + if (likely(rel->rd_partdesc && + (!rel->rd_partdesc->detached_exist || include_detached))) + return rel->rd_partdesc; I think it would help to have a comment about what's going here, in addition to the description you already wrote for PartitionDescData.detached_exist. Maybe something along the lines of: === Under normal circumstances, we just return the partdesc that was already built. However, if the partdesc was built at a time when there existed detach-pending partition(s), which the current caller would rather not see (omit_detached), then we build one afresh omitting any such partitions and return that one. RelationBuildPartitionDesc() makes sure that any such partdescs will disappear when the query finishes. === That's maybe a bit verbose but I am sure you will find a way to write it more succinctly. BTW, I do feel a bit alarmed by the potential performance impact of this. If detached_exist of a cached partdesc is true, then RI queries invoked during a bulk DML operation would have to rebuild one for every tuple to be checked, right? I haven't tried an actual example yet though. -- Amit Langote EDB: http://www.enterprisedb.com -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-22T19:26:02Z
On 2021-Apr-22, Amit Langote wrote: > On Thu, Apr 22, 2021 at 5:39 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > Reading through the latest one, seeing both include_detached and > omit_detached being used in different parts of the code makes it a bit > hard to keep in mind what a given code path is doing wrt detached > partitions. How about making it all omit_detached? Yeah, I hesitated but wanted to do that too. Done. > * Cope with partitions concurrently being detached. When we see a > - * partition marked "detach pending", we only include it in the set of > - * visible partitions if caller requested all detached partitions, or > - * if its pg_inherits tuple's xmin is still visible to the active > - * snapshot. > + * partition marked "detach pending", we omit it from the returned > + * descriptor if caller requested that and the tuple's xmin does not > + * appear in progress to the active snapshot. > > It seems odd for a comment in find_inheritance_children() to talk > about the "descriptor". Maybe the earlier "set of visible > partitions" wording was fine? Absolutely -- done that way. > - * The reason for this check is that we want to avoid seeing the > + * The reason for this hack is that we want to avoid seeing the > * partition as alive in RI queries during REPEATABLE READ or > <snip> > + * SERIALIZABLE transactions. > > The comment doesn't quite make it clear why it is the RI query case > that necessitates this hack in the first case. I added "such queries use a different snapshot than the one used by regular (user) queries." I hope that's sufficient. > Maybe the relation to what's going on with the partdesc > > + if (likely(rel->rd_partdesc && > + (!rel->rd_partdesc->detached_exist || include_detached))) > + return rel->rd_partdesc; > > I think it would help to have a comment about what's going here, in > addition to the description you already wrote for > PartitionDescData.detached_exist. Maybe something along the lines of: > > === > Under normal circumstances, we just return the partdesc that was > already built. However, if the partdesc was built at a time when > there existed detach-pending partition(s), which the current caller > would rather not see (omit_detached), then we build one afresh > omitting any such partitions and return that one. > RelationBuildPartitionDesc() makes sure that any such partdescs will > disappear when the query finishes. > === > > That's maybe a bit verbose but I am sure you will find a way to write > it more succinctly. I added some text in this spot, and also wrote some more in the comment above RelationGetPartitionDesc and RelationBuildPartitionDesc. > BTW, I do feel a bit alarmed by the potential performance impact of > this. If detached_exist of a cached partdesc is true, then RI queries > invoked during a bulk DML operation would have to rebuild one for > every tuple to be checked, right? I haven't tried an actual example > yet though. Yeah, I was scared about that too (which is why I insisted on trying to add a cached copy of the partdesc omitting detached partitions). But AFAICS what happens is that the plan for the RI query gets cached after a few tries; so we do build the partdesc a few times at first, but later we use the cached plan and so we no longer use that one. So at least in the normal cases this isn't a serious problem that I can see. I pushed it now. Thanks for your help, -- Álvaro Herrera Valdivia, Chile "How strange it is to find the words "Perl" and "saner" in such close proximity, with no apparent sense of irony. I doubt that Larry himself could have managed it." (ncm, http://lwn.net/Articles/174769/)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-23T09:33:09Z
On Fri, Apr 23, 2021 at 4:26 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On 2021-Apr-22, Amit Langote wrote: > > - * The reason for this check is that we want to avoid seeing the > > + * The reason for this hack is that we want to avoid seeing the > > * partition as alive in RI queries during REPEATABLE READ or > > <snip> > > + * SERIALIZABLE transactions. > > > > The comment doesn't quite make it clear why it is the RI query case > > that necessitates this hack in the first case. > > I added "such queries use a different snapshot than the one used by > regular (user) queries." I hope that's sufficient. Yeah, that makes sense. > > Maybe the relation to what's going on with the partdesc (I had to leave my desk while in the middle of typing this, but I forget what I was going to add :() > > BTW, I do feel a bit alarmed by the potential performance impact of > > this. If detached_exist of a cached partdesc is true, then RI queries > > invoked during a bulk DML operation would have to rebuild one for > > every tuple to be checked, right? I haven't tried an actual example > > yet though. > > Yeah, I was scared about that too (which is why I insisted on trying to > add a cached copy of the partdesc omitting detached partitions). But > AFAICS what happens is that the plan for the RI query gets cached after > a few tries; so we do build the partdesc a few times at first, but later > we use the cached plan and so we no longer use that one. So at least in > the normal cases this isn't a serious problem that I can see. Actually, ri_trigger.c (or really plancache.c) is not very good at caching the plan when querying partitioned tables; it always chooses to replan because a generic plan, even with runtime pruning built into it, looks very expensive compared to a custom one. Now that's a problem we will have to fix sooner than later, but until then we have to work around it. Here is an example that shows the problem: create unlogged table pk_parted (a int primary key) partition by range (a); select 'create unlogged table pk_parted_' || i || ' partition of pk_parted for values from (' || (i-1) * 1000 + 1 || ') to (' || i * 1000 + 1 || ');' from generate_series(1, 1000) i; \gexec create unlogged table fk (a int references pk_parted); insert into pk_parted select generate_series(1, 10000); begin; select * from fk_parted where a = 1; In another session: alter table pk_parted detach partition pk_parted_1000 concurrently; <blocks; cancel using ctrl-c> Back in the 1st session: end; insert into fk select generate_series(1, 10000); INSERT 0 10000 Time: 47400.792 ms (00:47.401) The insert took unusually long, because the PartitionDesc for pk_parted had to be built exactly 10000 times, because there's a detach-pending partition lying around. There is also a danger of an OOM with such an insert because of leaking into PortalContext the memory of every PartitionDesc thus built, especially with larger counts of PK partitions and rows inserted into the FK table. Also, I noticed that all queries on pk_parted, not just the RI queries, have to build the PartitionDesc every time, so take that much longer: -- note the planning time explain analyze select * from pk_parted where a = 1; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- -------------- Index Only Scan using pk_parted_1_pkey on pk_parted_1 pk_parted (cost=0.28..8.29 rows=1 width=4) (actual time=0.016..0.017 ro ws=1 loops=1) Index Cond: (a = 1) Heap Fetches: 1 Planning Time: 7.543 ms Execution Time: 0.044 ms (5 rows) Finalizing the detach makes the insert and the query finish in normal time, because the PartitionDesc can be cached again: alter table pk_parted detach partition pk_parted_1000 finalize; insert into fk select generate_series(1, 10000); INSERT 0 10000 Time: 855.336 ms explain analyze select * from pk_parted where a = 1; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------- -------------- Index Only Scan using pk_parted_1_pkey on pk_parted_1 pk_parted (cost=0.28..8.29 rows=1 width=4) (actual time=0.033..0.036 ro ws=1 loops=1) Index Cond: (a = 1) Heap Fetches: 1 Planning Time: 0.202 ms Execution Time: 0.075 ms (5 rows) I am afraid we may have to fix this in the code after all, because there does not seem a good way to explain this away in the documentation. If I read correctly, you did try an approach of caching the PartitionDesc that we currently don't, no? -- Amit Langote EDB: http://www.enterprisedb.com -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-23T17:12:19Z
On 2021-Apr-23, Amit Langote wrote: > Back in the 1st session: > > end; > insert into fk select generate_series(1, 10000); > INSERT 0 10000 > Time: 47400.792 ms (00:47.401) I guess I was wrong about that ... the example I tried didn't have 1000s of partitions, and I used debug print-outs to show when a new partdesc was being rebuilt, and only six were occurring. I'm not sure why my case behaves so differently from yours, but clearly from the timing this is not good. > I am afraid we may have to fix this in the code after all, because > there does not seem a good way to explain this away in the > documentation. Yeah, looking at this case, I agree that it needs a fix of some kind. > If I read correctly, you did try an approach of caching the > PartitionDesc that we currently don't, no? I think the patch I posted was too simple. I think a real fix requires us to keep track of exactly in what way the partdesc is outdated, so that we can compare to the current situation in deciding to use that partdesc or build a new one. For example, we could keep track of a list of OIDs of detached partitions (and it simplifies things a lot if allow only a single partition in this situation, because it's either zero OIDs or one OID); or we can save the Xmin of the pg_inherits tuple for the detached partition (and we can compare that Xmin to our current active snapshot and decide whether to use that partdesc or not). I'll experiment with this a little more and propose a patch later today. I don't think it's too much of a problem to state that you need to finalize one detach before you can do the next one. After all, with regular detach, you'd have the tables exclusively locked so you can't do them in parallel anyway. (It also increases the chances that people will finalize detach operations that went unnoticed.) -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-23T23:31:44Z
On 2021-Apr-23, Alvaro Herrera wrote: > I think the patch I posted was too simple. I think a real fix requires > us to keep track of exactly in what way the partdesc is outdated, so > that we can compare to the current situation in deciding to use that > partdesc or build a new one. For example, we could keep track of a list > of OIDs of detached partitions (and it simplifies things a lot if allow > only a single partition in this situation, because it's either zero OIDs > or one OID); or we can save the Xmin of the pg_inherits tuple for the > detached partition (and we can compare that Xmin to our current active > snapshot and decide whether to use that partdesc or not). > > I'll experiment with this a little more and propose a patch later today. This (POC-quality) seems to do the trick. (I restored the API of find_inheritance_children, because it was getting a little obnoxious. I haven't thought this through but I think we should do something like it.) > I don't think it's too much of a problem to state that you need to > finalize one detach before you can do the next one. After all, with > regular detach, you'd have the tables exclusively locked so you can't do > them in parallel anyway. (It also increases the chances that people > will finalize detach operations that went unnoticed.) I haven't added a mechanism to verify this; but with asserts on, this patch will crash if you have more than one. I think the behavior is not necessarily sane with asserts off, since you'll get an arbitrary detach-Xmin assigned to the partdesc, depending on catalog scan order. -- Álvaro Herrera Valdivia, Chile "El hombre nunca sabe de lo que es capaz hasta que lo intenta" (C. Dickens)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-26T12:04:55Z
Hi Alvaro, On Sat, Apr 24, 2021 at 8:31 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On 2021-Apr-23, Alvaro Herrera wrote: > > I think the patch I posted was too simple. I think a real fix requires > > us to keep track of exactly in what way the partdesc is outdated, so > > that we can compare to the current situation in deciding to use that > > partdesc or build a new one. For example, we could keep track of a list > > of OIDs of detached partitions (and it simplifies things a lot if allow > > only a single partition in this situation, because it's either zero OIDs > > or one OID); or we can save the Xmin of the pg_inherits tuple for the > > detached partition (and we can compare that Xmin to our current active > > snapshot and decide whether to use that partdesc or not). > > > > I'll experiment with this a little more and propose a patch later today. > > This (POC-quality) seems to do the trick. Thanks for the patch. > (I restored the API of find_inheritance_children, because it was getting > a little obnoxious. I haven't thought this through but I think we > should do something like it.) +1. > > I don't think it's too much of a problem to state that you need to > > finalize one detach before you can do the next one. After all, with > > regular detach, you'd have the tables exclusively locked so you can't do > > them in parallel anyway. (It also increases the chances that people > > will finalize detach operations that went unnoticed.) That sounds reasonable. > I haven't added a mechanism to verify this; but with asserts on, this > patch will crash if you have more than one. I think the behavior is not > necessarily sane with asserts off, since you'll get an arbitrary > detach-Xmin assigned to the partdesc, depending on catalog scan order. Maybe this is an ignorant question but is the plan to add an elog() in this code path or a check (and an ereport()) somewhere in ATExecDetachPartition() to prevent more than one partition ending up in detach-pending state? Please allow me to study the patch a bit more closely and get back tomorrow. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-26T12:40:54Z
Hello Amit, On 2021-Apr-26, Amit Langote wrote: > On Sat, Apr 24, 2021 at 8:31 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > > I haven't added a mechanism to verify this; but with asserts on, this > > patch will crash if you have more than one. I think the behavior is not > > necessarily sane with asserts off, since you'll get an arbitrary > > detach-Xmin assigned to the partdesc, depending on catalog scan order. > > Maybe this is an ignorant question but is the plan to add an elog() in > this code path or a check (and an ereport()) somewhere in > ATExecDetachPartition() to prevent more than one partition ending up > in detach-pending state? Yeah, that's what I'm planning to do. > Please allow me to study the patch a bit more closely and get back tomorrow. Sure, thanks! -- Álvaro Herrera Valdivia, Chile "But static content is just dynamic content that isn't moving!" http://smylers.hates-software.com/2007/08/15/fe244d0c.html -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-26T19:44:46Z
On 2021-Apr-26, Alvaro Herrera wrote: > > Please allow me to study the patch a bit more closely and get back tomorrow. > > Sure, thanks! Here's a more polished version. After trying the version with the elog(ERROR) when two detached partitions are present, I decided against it; it is unhelpful because it doesn't let you build partition descriptors for anything. So I made it an elog(WARNING) (not an ereport, note), and keep the most recent pg_inherits.xmin value. This is not great, but it lets you out of the situation by finalizing one of the detaches. The other check (at ALTER TABLE .. DETACH time) is an ereport(ERROR) and should make the first one unreachable. -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-27T00:04:21Z
Sorry, I forgot to update some comments in that version. Fixed here. -- Álvaro Herrera 39°49'30"S 73°17'W
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-27T07:34:30Z
Thanks for the updated patch. I've been reading it, but I noticed a bug in 8aba9322511f, which I thought you'd want to know to make a note of when committing this one. So we decided in 8aba9322511f that it is okay to make the memory context in which a transient partdesc is allocated a child of PortalContext so that it disappears when the portal does. But PortalContext is apparently NULL when the planner runs, at least in the "simple" query protocol, so any transient partdescs built by the planner would effectively leak. Not good. With this patch, partdesc_nodetached is no longer transient, so the problem doesn't exist. I will write more about the updated patch in a bit... -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-27T14:06:09Z
On Tue, Apr 27, 2021 at 4:34 PM Amit Langote <amitlangote09@gmail.com> wrote: > Thanks for the updated patch. I've been reading it, but I noticed a > bug in 8aba9322511f, which I thought you'd want to know to make a note > of when committing this one. > > So we decided in 8aba9322511f that it is okay to make the memory > context in which a transient partdesc is allocated a child of > PortalContext so that it disappears when the portal does. But > PortalContext is apparently NULL when the planner runs, at least in > the "simple" query protocol, so any transient partdescs built by the > planner would effectively leak. Not good. > > With this patch, partdesc_nodetached is no longer transient, so the > problem doesn't exist. > > I will write more about the updated patch in a bit... The first thing that struck me about partdesc_nodetached is that it's not handled in RelationClearRelation(), where we (re)set a regular partdesc to NULL so that the next RelationGetPartitionDesc() has to build it from scratch. I think partdesc_nodetached and the xmin should likewise be reset. Also in load_relcache_init_file(), although nothing serious there. That said, I think I may have found a couple of practical problems with partdesc_nodetached, or more precisely with having it side-by-side with regular partdesc. Maybe they can be fixed, so the problems are not as such deal breakers for the patch's main idea. The problems can be seen when different queries in a serializable transaction have to use both the regular partdesc and partdesc_detached in a given relcache. For example, try the following after first creating a situation where the table p has a detach-pending partition p2 (for values in (2) and a live partition p1 (for values in (1)). begin isolation level serializable; insert into p values (1); select * from p where a = 1; insert into p values (1); The 1st insert succeeds but the 2nd fails with: ERROR: no partition of relation "p" found for row DETAIL: Partition key of the failing row contains (a) = (1). I haven't analyzed this error very closely but there is another situation which causes a crash due to what appears to be a conflict with rd_pdcxt's design: -- run in a new session begin isolation level serializable; select * from p where a = 1; insert into p values (1); select * from p where a = 1; The 2nd select crashes: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Failed. The crash occurs because the planner gets handed a stale copy of partdesc_nodetached for the 2nd select. It gets stale, because the context it's allocated in gets made a child of rd_pdcxt, which is in turn assigned the context of the regular partdesc when it is built for the insert query. Any child contexts of rd_pdcxt are deleted as soon as the Relation's refcount goes to zero, taking it with partdesc_nodetached. Note it is this code in RelationBuildPartitionDesc(): /* * But first, a kluge: if there's an old rd_pdcxt, it contains an old * partition descriptor that may still be referenced somewhere. * Preserve it, while not leaking it, by reattaching it as a child * context of the new rd_pdcxt. Eventually it will get dropped by * either RelationClose or RelationClearRelation. */ if (rel->rd_pdcxt != NULL) MemoryContextSetParent(rel->rd_pdcxt, new_pdcxt); rel->rd_pdcxt = new_pdcxt; I think we may need a separate context for partdesc_nodetached, likely with the same kludges as rd_pdcxt. Maybe the first problem will go away with that as well. Few other minor things I noticed: + * Store it into relcache. For snapshots built excluding detached + * partitions, which we save separately, we also record the + * pg_inherits.xmin of the detached partition that was omitted; this + * informs a future potential user of such a cached snapshot. The "snapshot" in the 1st and the last sentence should be "partdesc"? + * We keep two partdescs in relcache: rd_partdesc_nodetached excludes + * partitions marked concurrently being detached, while rd_partdesc includes + * them. IMHO, describing rd_partdesc first in the sentence would be better. Like: rd_partdesc includes all partitions including any that are being concurrently detached, while rd_partdesc_nodetached excludes them. -- Amit Langote EDB: http://www.enterprisedb.com -
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-27T15:47:33Z
On 2021-Apr-27, Amit Langote wrote: > On Tue, Apr 27, 2021 at 4:34 PM Amit Langote <amitlangote09@gmail.com> wrote: > I think we may need a separate context for partdesc_nodetached, likely > with the same kludges as rd_pdcxt. Maybe the first problem will go > away with that as well. Ooh, seems I completely misunderstood what RelationClose was doing. I thought it was deleting the whole rd_pdcxt, *including* the "current" partdesc. But that's not at all what it does: it only deletes the *children* memcontexts, so the partdesc that is currently valid remains valid. I agree that your proposed fix appears to be promising, in that a separate "context tree" rd_pddcxt (?) can be used for this. I'll try it out now. > Few other minor things I noticed: > > + * Store it into relcache. For snapshots built excluding detached > + * partitions, which we save separately, we also record the > + * pg_inherits.xmin of the detached partition that was omitted; this > + * informs a future potential user of such a cached snapshot. > > The "snapshot" in the 1st and the last sentence should be "partdesc"? Doh, yeah. > + * We keep two partdescs in relcache: rd_partdesc_nodetached excludes > + * partitions marked concurrently being detached, while rd_partdesc includes > + * them. > > IMHO, describing rd_partdesc first in the sentence would be better. > Like: rd_partdesc includes all partitions including any that are being > concurrently detached, while rd_partdesc_nodetached excludes them. Makes sense. -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-27T16:32:26Z
This v3 handles things as you suggested and works correctly AFAICT. I'm going to add some more tests cases to verify the behavior in the scenarios you showed, and get them to run under cache-clobber options to make sure it's good. Thanks! -- Álvaro Herrera Valdivia, Chile
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-27T23:32:11Z
On 2021-Apr-27, Alvaro Herrera wrote: > This v3 handles things as you suggested and works correctly AFAICT. I'm > going to add some more tests cases to verify the behavior in the > scenarios you showed, and get them to run under cache-clobber options to > make sure it's good. Yep, it seems to work. Strangely, the new isolation case doesn't actually crash before the fix -- it merely throws a memory allocation error. -- Álvaro Herrera Valdivia, Chile "Linux transformó mi computadora, de una `máquina para hacer cosas', en un aparato realmente entretenido, sobre el cual cada día aprendo algo nuevo" (Jaime Salinas)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-28T14:21:04Z
On Wed, Apr 28, 2021 at 8:32 AM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On 2021-Apr-27, Alvaro Herrera wrote: > > > This v3 handles things as you suggested and works correctly AFAICT. I'm > > going to add some more tests cases to verify the behavior in the > > scenarios you showed, and get them to run under cache-clobber options to > > make sure it's good. > > Yep, it seems to work. Strangely, the new isolation case doesn't > actually crash before the fix -- it merely throws a memory allocation > error. Thanks. Yeah, it does seem to work. I noticed that rd_partdesc_nodetached_xmin can sometimes end up with value 0. While you seem to be already aware of that, because otherwise you wouldn't have added TransactionIdIsValid(...) in condition in RelationGetPartitionDesc(), the comments nearby don't mention why such a thing might happen. Also, I guess it can't be helped that the partdesc_nodetached will have to be leaked when the xmin is 0, but that shouldn't be as problematic as the case we discussed earlier. + /* + * But first, a kluge: if there's an old context for this type of + * descriptor, it contains an old partition descriptor that may still be + * referenced somewhere. Preserve it, while not leaking it, by + * reattaching it as a child context of the new one. Eventually it will + * get dropped by either RelationClose or RelationClearRelation. + * + * We keep the regular partdesc in rd_pdcxt, and the partdesc-excluding- + * detached-partitions in rd_pddcxt. + */ + context = is_omit ? &rel->rd_pddcxt : &rel->rd_pdcxt; + if (*context != NULL) + MemoryContextSetParent(*context, new_pdcxt); + *context = new_pdcxt; Would it be a bit more readable to just duplicate this stanza in the blocks that assign to rd_partdesc_nodetached and rd_partdesc, respectively? That's not much code to duplicate and it'd be easier to see which context is for which partdesc. + TransactionId rd_partdesc_nodetached_xmin; /* xmin for the above */ Could you please expand this description a bit? -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-28T16:11:12Z
Thanks for re-reviewing! This one I hope is the last version. On Wed, Apr 28, 2021, at 10:21 AM, Amit Langote wrote: > I noticed that rd_partdesc_nodetached_xmin can sometimes end up with > value 0. While you seem to be already aware of that, because otherwise > you wouldn't have added TransactionIdIsValid(...) in condition in > RelationGetPartitionDesc(), the comments nearby don't mention why such > a thing might happen. Also, I guess it can't be helped that the > partdesc_nodetached will have to be leaked when the xmin is 0, but > that shouldn't be as problematic as the case we discussed earlier. The only case I am aware where that can happen is if the pg_inherits tuple is frozen. (That's exactly what the affected test case was testing, note the "VACUUM FREEZE pg_inherits" there). So that test case blew up immediately; but I think the real-world chances that people are going to be doing that are pretty low, so I'm not really concerned about the leak. > Would it be a bit more readable to just duplicate this stanza in the > blocks that assign to rd_partdesc_nodetached and rd_partdesc, > respectively? That's not much code to duplicate and it'd be easier to > see which context is for which partdesc. Sure .. that's how I first wrote this code. We don't use that style much, so I'm OK with backing out of it. > + TransactionId rd_partdesc_nodetached_xmin; /* xmin for the above */ > > Could you please expand this description a bit? Done.
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-04-28T19:49:47Z
Pushed that now, with a one-line addition to the docs that only one partition can be marked detached. -- Álvaro Herrera 39°49'30"S 73°17'W "That sort of implies that there are Emacs keystrokes which aren't obscure. I've been using it daily for 2 years now and have yet to discover any key sequence which makes any sense." (Paul Thomas)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-04-30T13:57:02Z
(Thanks for committing the fix.) On Thu, Apr 29, 2021 at 1:11 AM Álvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On Wed, Apr 28, 2021, at 10:21 AM, Amit Langote wrote: > I noticed that rd_partdesc_nodetached_xmin can sometimes end up with > value 0. While you seem to be already aware of that, because otherwise > you wouldn't have added TransactionIdIsValid(...) in condition in > RelationGetPartitionDesc(), the comments nearby don't mention why such > a thing might happen. Also, I guess it can't be helped that the > partdesc_nodetached will have to be leaked when the xmin is 0, but > that shouldn't be as problematic as the case we discussed earlier. > > > The only case I am aware where that can happen is if the pg_inherits tuple is frozen. (That's exactly what the affected test case was testing, note the "VACUUM FREEZE pg_inherits" there). So that test case blew up immediately; but I think the real-world chances that people are going to be doing that are pretty low, so I'm not really concerned about the leak. The case I was looking at is when a partition detach appears as in-progress to a serializable transaction. If the caller wants to omit detached partitions, such a partition ends up in rd_partdesc_nodetached, with the corresponding xmin being set to 0 due to the way find_inheritance_children_extended() sets *detached_xmin. The next query in the transaction that wants to omit detached partitions, seeing rd_partdesc_nodetached_xmin being invalid, rebuilds the partdesc, again including that partition because the snapshot wouldn't have changed, and so on until the transaction ends. Now, this can perhaps be "fixed" by making find_inheritance_children_extended() set the xmin outside the snapshot-checking block, but maybe there's no need to address this on priority. Rather, a point that bothers me a bit is that we're including a detached partition in the partdesc labeled "nodetached" in this particular case. Maybe we should avoid that by considering in this scenario that no detached partitions exist for this transactions and so initialize rd_partdesc, instead of rd_partdesc_nodetached. That will let us avoid the situations where the xmin is left in invalid state. Maybe like the attached (it also fixes a couple of typos/thinkos in the previous commit). Note that we still end up in the same situation as before where each query in the serializable transaction that sees the detach as in-progress to have to rebuild the partition descriptor omitting the detached partitions, even when it's clear that the detach-in-progress partition will be included every time. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Pavel Luzanov <p.luzanov@postgrespro.ru> — 2021-05-05T10:58:59Z
Hello, I found this in the documentation, section '5.11.3. Partitioning Using Inheritance'[1]: "Some operations require a stronger lock when using declarative partitioning than when using table inheritance. For example, removing a partition from a partitioned table requires taking an ACCESS EXCLUSIVE lock on the parent table, whereas a SHARE UPDATE EXCLUSIVE lock is enough in the case of regular inheritance." This point is no longer valid with some restrictions. If the table has a default partition, then removing a partition still requires taking an ACCESS EXCLUSIVE lock. May be make sense to add some details about DETACH CONCURRENTLY to the section '5.11.2.2. Partition Maintenance' and completely remove this point? 1. https://www.postgresql.org/docs/devel/ddl-partitioning.html#DDL-PARTITIONING-USING-INHERITANCE Pavel Luzanov Postgres Professional: https://postgrespro.com The Russian Postgres Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-05-06T05:35:34Z
On Wed, May 5, 2021 at 7:59 PM Pavel Luzanov <p.luzanov@postgrespro.ru> wrote: > I found this in the documentation, section '5.11.3. Partitioning Using Inheritance'[1]: > "Some operations require a stronger lock when using declarative partitioning than when using table inheritance. For example, removing a partition from a partitioned table requires taking an ACCESS EXCLUSIVE lock on the parent table, whereas a SHARE UPDATE EXCLUSIVE lock is enough in the case of regular inheritance." > > This point is no longer valid with some restrictions. If the table has a default partition, then removing a partition still requires taking an ACCESS EXCLUSIVE lock. > > May be make sense to add some details about DETACH CONCURRENTLY to the section '5.11.2.2. Partition Maintenance' and completely remove this point? > > 1. https://www.postgresql.org/docs/devel/ddl-partitioning.html#DDL-PARTITIONING-USING-INHERITANCE That makes sense, thanks for noticing. How about the attached? -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Pavel Luzanov <p.luzanov@postgrespro.ru> — 2021-05-06T11:22:56Z
On 06.05.2021 08:35, Amit Langote wrote: > On Wed, May 5, 2021 at 7:59 PM Pavel Luzanov > <p.luzanov@postgrespro.ru> wrote: >> I found this in the documentation, section '5.11.3. Partitioning >> Using Inheritance'[1]: "Some operations require a stronger lock when >> using declarative partitioning than when using table inheritance. For >> example, removing a partition from a partitioned table requires >> taking an ACCESS EXCLUSIVE lock on the parent table, whereas a SHARE >> UPDATE EXCLUSIVE lock is enough in the case of regular inheritance." >> This point is no longer valid with some restrictions. If the table >> has a default partition, then removing a partition still requires >> taking an ACCESS EXCLUSIVE lock. May be make sense to add some >> details about DETACH CONCURRENTLY to the section '5.11.2.2. Partition >> Maintenance' and completely remove this point? 1. >> https://www.postgresql.org/docs/devel/ddl-partitioning.html#DDL-PARTITIONING-USING-INHERITANCE > That makes sense, thanks for noticing. How about the attached? I like it. Especially the link to the ALTER TABLE, this avoids duplication of all the nuances of the the DETACH .. CONCURRENTLY. -- Pavel Luzanov Postgres Professional: https://postgrespro.com The Russian Postgres Company
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-05-06T17:13:47Z
On 2021-Apr-30, Amit Langote wrote: > The case I was looking at is when a partition detach appears as > in-progress to a serializable transaction. Yeah, I was exceedingly sloppy on my reasoning about this, and you're right that that's what actually happens rather than what I said. > If the caller wants to omit detached partitions, such a partition ends > up in rd_partdesc_nodetached, with the corresponding xmin being set to > 0 due to the way find_inheritance_children_extended() sets > *detached_xmin. The next query in the transaction that wants to omit > detached partitions, seeing rd_partdesc_nodetached_xmin being invalid, > rebuilds the partdesc, again including that partition because the > snapshot wouldn't have changed, and so on until the transaction ends. > Now, this can perhaps be "fixed" by making > find_inheritance_children_extended() set the xmin outside the > snapshot-checking block, but maybe there's no need to address this on > priority. Hmm. See below. > Rather, a point that bothers me a bit is that we're including a > detached partition in the partdesc labeled "nodetached" in this > particular case. Maybe we should avoid that by considering in this > scenario that no detached partitions exist for this transactions and > so initialize rd_partdesc, instead of rd_partdesc_nodetached. That > will let us avoid the situations where the xmin is left in invalid > state. Maybe like the attached (it also fixes a couple of > typos/thinkos in the previous commit). Makes sense -- applied, thanks. > Note that we still end up in the same situation as before where each > query in the serializable transaction that sees the detach as > in-progress to have to rebuild the partition descriptor omitting the > detached partitions, even when it's clear that the detach-in-progress > partition will be included every time. Yeah, you're right that there is a performance hole in the case where a partition pending detach exists and you're using repeatable read transactions. I didn't see it as terribly critical since it's supposed to be very transient, but I may be wrong. -- Álvaro Herrera Valdivia, Chile "Hay quien adquiere la mala costumbre de ser infeliz" (M. A. Evans)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-05-06T17:32:08Z
On 2021-May-05, Pavel Luzanov wrote: > Hello, > > I found this in the documentation, section '5.11.3. Partitioning Using > Inheritance'[1]: > "Some operations require a stronger lock when using declarative partitioning > than when using table inheritance. For example, removing a partition from a > partitioned table requires taking an ACCESS EXCLUSIVE lock on the parent > table, whereas a SHARE UPDATE EXCLUSIVE lock is enough in the case of > regular inheritance." > > This point is no longer valid with some restrictions. If the table has a > default partition, then removing a partition still requires taking an ACCESS > EXCLUSIVE lock. Hmm, are there any other operations for which the partitioning command takes a strong lock than the legacy inheritance corresponding command? If there aren't any, then it's okay to delete this paragraph as in the proposed patch. But if there are any, then I think we should change the example to mention that other operation. I'm not sure what's a good way to verify that, though. Also, it remains true that without CONCURRENTLY the DETACH operation takes AEL. I'm not sure it's worth pointing this out in this paragraph. > May be make sense to add some details about DETACH CONCURRENTLY to the > section '5.11.2.2. Partition Maintenance' and completely remove this point? Maybe you're right, though. -- Álvaro Herrera 39°49'30"S 73°17'W "Las mujeres son como hondas: mientras más resistencia tienen, más lejos puedes llegar con ellas" (Jonas Nightingale, Leap of Faith)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-05-06T20:48:29Z
On 2021-May-06, Amit Langote wrote: > That makes sense, thanks for noticing. > > How about the attached? I tweaked the linkage; as submitted, the text in the link contained what is in the <term> tag, so literally it had: ... see DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ] for details ... which didn't look very nice. So I made it use <link> instead of xref and wrote the "ALTER TABLE .. DETACH PARTITION" text. I first tried to fix it by adding an "xreflabel" attrib, but I didn't like it because the text was not set in fixed width font. I also tweaked the wording to match the surrounding text a bit better, at least IMO. Feel free to suggest improvements. Thanks! -- Álvaro Herrera 39°49'30"S 73°17'W "That sort of implies that there are Emacs keystrokes which aren't obscure. I've been using it daily for 2 years now and have yet to discover any key sequence which makes any sense." (Paul Thomas)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-05-13T04:24:36Z
On Fri, May 7, 2021 at 2:13 AM Álvaro Herrera <alvherre@alvh.no-ip.org> wrote: > On 2021-Apr-30, Amit Langote wrote: > > > The case I was looking at is when a partition detach appears as > > in-progress to a serializable transaction. > > Yeah, I was exceedingly sloppy on my reasoning about this, and you're > right that that's what actually happens rather than what I said. > > > If the caller wants to omit detached partitions, such a partition ends > > up in rd_partdesc_nodetached, with the corresponding xmin being set to > > 0 due to the way find_inheritance_children_extended() sets > > *detached_xmin. The next query in the transaction that wants to omit > > detached partitions, seeing rd_partdesc_nodetached_xmin being invalid, > > rebuilds the partdesc, again including that partition because the > > snapshot wouldn't have changed, and so on until the transaction ends. > > Now, this can perhaps be "fixed" by making > > find_inheritance_children_extended() set the xmin outside the > > snapshot-checking block, but maybe there's no need to address this on > > priority. > > Hmm. See below. > > > Rather, a point that bothers me a bit is that we're including a > > detached partition in the partdesc labeled "nodetached" in this > > particular case. Maybe we should avoid that by considering in this > > scenario that no detached partitions exist for this transactions and > > so initialize rd_partdesc, instead of rd_partdesc_nodetached. That > > will let us avoid the situations where the xmin is left in invalid > > state. Maybe like the attached (it also fixes a couple of > > typos/thinkos in the previous commit). > > Makes sense -- applied, thanks. Thank you. > > Note that we still end up in the same situation as before where each > > query in the serializable transaction that sees the detach as > > in-progress to have to rebuild the partition descriptor omitting the > > detached partitions, even when it's clear that the detach-in-progress > > partition will be included every time. > > Yeah, you're right that there is a performance hole in the case where a > partition pending detach exists and you're using repeatable read > transactions. I didn't see it as terribly critical since it's supposed > to be very transient, but I may be wrong. Yeah, I'd hope so too. I think RR transactions would have to be concurrent with an interrupted DETACH CONCURRENTLY to suffer the performance hit and that does kind of make this a rarely occurring case. -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Noah Misch <noah@leadboat.com> — 2021-05-24T09:07:12Z
On Wed, Apr 21, 2021 at 04:38:55PM -0400, Alvaro Herrera wrote: [fix to let CLOBBER_CACHE_ALWAYS pass] > Barring objections, I will get this pushed early tomorrow. prairiedog and wrasse failed a $SUBJECT test after this (commit 8aba932). Also, some non-CLOBBER_CACHE_ALWAYS animals failed a test before the fix. These IsolationCheck failures match detach-partition-concurrently[^\n]*FAILED: sysname │ snapshot │ branch │ bfurl ────────────┼─────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────── hyrax │ 2021-03-27 07:29:34 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-03-27%2007%3A29%3A34 topminnow │ 2021-03-28 20:37:38 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=topminnow&dt=2021-03-28%2020%3A37%3A38 trilobite │ 2021-03-29 18:14:24 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=trilobite&dt=2021-03-29%2018%3A14%3A24 hyrax │ 2021-04-01 07:21:10 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-01%2007%3A21%3A10 dragonet │ 2021-04-01 19:48:03 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=dragonet&dt=2021-04-01%2019%3A48%3A03 avocet │ 2021-04-05 15:45:56 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=avocet&dt=2021-04-05%2015%3A45%3A56 hyrax │ 2021-04-06 07:15:16 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-06%2007%3A15%3A16 hyrax │ 2021-04-11 07:25:50 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-11%2007%3A25%3A50 hyrax │ 2021-04-20 18:25:37 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-20%2018%3A25%3A37 wrasse │ 2021-04-21 10:38:32 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=wrasse&dt=2021-04-21%2010%3A38%3A32 prairiedog │ 2021-04-25 22:05:48 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prairiedog&dt=2021-04-25%2022%3A05%3A48 wrasse │ 2021-05-11 03:43:40 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=wrasse&dt=2021-05-11%2003%3A43%3A40 (12 rows)
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Amit Langote <amitlangote09@gmail.com> — 2021-05-24T11:39:16Z
On Mon, May 24, 2021 at 6:07 PM Noah Misch <noah@leadboat.com> wrote: > On Wed, Apr 21, 2021 at 04:38:55PM -0400, Alvaro Herrera wrote: > > [fix to let CLOBBER_CACHE_ALWAYS pass] > > > Barring objections, I will get this pushed early tomorrow. > > prairiedog and wrasse failed a $SUBJECT test after this (commit 8aba932). > Also, some non-CLOBBER_CACHE_ALWAYS animals failed a test before the fix. > These IsolationCheck failures match detach-partition-concurrently[^\n]*FAILED: FWIW, all 4 detach-partition-concurrently suites passed for me on a build of the latest HEAD, with CPPFLAGS = -DRELCACHE_FORCE_RELEASE -DCATCACHE_FORCE_RELEASE -DCLOBBER_CACHE_ALWAYS -D_GNU_SOURCE -- Amit Langote EDB: http://www.enterprisedb.com
-
Re: ALTER TABLE .. DETACH PARTITION CONCURRENTLY
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2021-05-25T19:31:58Z
On 2021-May-24, Noah Misch wrote: > prairiedog and wrasse failed a $SUBJECT test after this (commit 8aba932). > Also, some non-CLOBBER_CACHE_ALWAYS animals failed a test before the fix. > These IsolationCheck failures match detach-partition-concurrently[^\n]*FAILED: > > sysname │ snapshot │ branch │ bfurl > ────────────┼─────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────── Checking this list, these three failures can be explained by the detach-partition-concurrently-3 that was just patched. > wrasse │ 2021-04-21 10:38:32 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=wrasse&dt=2021-04-21%2010%3A38%3A32 > prairiedog │ 2021-04-25 22:05:48 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=prairiedog&dt=2021-04-25%2022%3A05%3A48 > wrasse │ 2021-05-11 03:43:40 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=wrasse&dt=2021-05-11%2003%3A43%3A40 Next there's a bunch whose error message is the same that we had seen earlier in this thread; these animals are all CLOBBER_CACHE_ALWAYS: step s1insert: insert into d4_fk values (1); +ERROR: insert or update on table "d4_fk" violates foreign key constraint "d4_fk_a_fkey" > hyrax │ 2021-03-27 07:29:34 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-03-27%2007%3A29%3A34 > trilobite │ 2021-03-29 18:14:24 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=trilobite&dt=2021-03-29%2018%3A14%3A24 > hyrax │ 2021-04-01 07:21:10 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-01%2007%3A21%3A10 > avocet │ 2021-04-05 15:45:56 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=avocet&dt=2021-04-05%2015%3A45%3A56 > hyrax │ 2021-04-06 07:15:16 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-06%2007%3A15%3A16 > hyrax │ 2021-04-11 07:25:50 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-11%2007%3A25%3A50 > hyrax │ 2021-04-20 18:25:37 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hyrax&dt=2021-04-20%2018%3A25%3A37 This is fine, because the fix commit 8aba932 is dated April 22 and these failures all predate that. And finally there's these two: > topminnow │ 2021-03-28 20:37:38 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=topminnow&dt=2021-03-28%2020%3A37%3A38 > dragonet │ 2021-04-01 19:48:03 │ HEAD │ http://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=dragonet&dt=2021-04-01%2019%3A48%3A03 (animals not CCA) which are exposing the same problem in detach-partition-concurrently-4 that we just fixed in detach-partition-concurrently-3, so we should apply the same fix: add a no-op step right after the cancel to prevent the error report from changing. I'll go do that after grabbing some coffee. Thanks for digging into the reports! -- Álvaro Herrera 39°49'30"S 73°17'W "Cada quien es cada cual y baja las escaleras como quiere" (JMSerrat)