diff options
author | Andres Freund <andres@anarazel.de> | 2018-07-04 14:53:30 -0700 |
---|---|---|
committer | Andres Freund <andres@anarazel.de> | 2018-07-04 14:58:25 -0700 |
commit | 3a01f68e35a3584431ac5381c6ed75b1b39aaf2a (patch) | |
tree | 5737acf61d3bdfecd3e78d55990fb5b19ba03264 /src | |
parent | b41669118c00e25376a6c9ac991e0d074990484a (diff) | |
download | postgresql-3a01f68e35a3584431ac5381c6ed75b1b39aaf2a.tar.gz postgresql-3a01f68e35a3584431ac5381c6ed75b1b39aaf2a.zip |
Check for interrupts inside the nbtree page deletion code.
When deleting pages the nbtree code has to walk through siblings of a
tree node. When those sibling links are corrupted that can lead to
endless loops - which are currently not interruptible. This is
especially problematic if autovacuum is repeatedly blocked on such
indexes, as it can be hard to get out of that situation without
resorting to single user mode.
Thus add interrupt checks to appropriate places in such
loops. Unfortunately in one of the cases it's it's not easy to do so.
Between 9.3 and 9.4 the page deletion (and page split) code changed
significantly. Before it was significantly less robust against
interruptions. Therefore don't backpatch to 9.3.
Author: Andres Freund
Discussion: https://postgr.es/m/20180627191629.wkunw2qbibnvlz53@alap3.anarazel.de
Backpatch: 9.4-
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/nbtree/nbtpage.c | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index a24e64156ab..2e959da5f85 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1443,6 +1443,7 @@ _bt_pagedel(Relation rel, Buffer buf) rightsib_empty = false; while (P_ISHALFDEAD(opaque)) { + /* will check for interrupts, once lock is released */ if (!_bt_unlink_halfdead_page(rel, buf, &rightsib_empty)) { /* _bt_unlink_halfdead_page already released buffer */ @@ -1456,6 +1457,12 @@ _bt_pagedel(Relation rel, Buffer buf) _bt_relbuf(rel, buf); /* + * Check here, as calling loops will have locks held, preventing + * interrupts from being processed. + */ + CHECK_FOR_INTERRUPTS(); + + /* * The page has now been deleted. If its right sibling is completely * empty, it's possible that the reason we haven't deleted it earlier * is that it was the rightmost child of the parent. Now that we @@ -1708,6 +1715,12 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) LockBuffer(leafbuf, BUFFER_LOCK_UNLOCK); /* + * Check here, as calling loops will have locks held, preventing + * interrupts from being processed. + */ + CHECK_FOR_INTERRUPTS(); + + /* * If the leaf page still has a parent pointing to it (or a chain of * parents), we don't unlink the leaf page yet, but the topmost remaining * parent in the branch. Set 'target' and 'buf' to reference the page @@ -1766,6 +1779,14 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty) /* step right one page */ leftsib = opaque->btpo_next; _bt_relbuf(rel, lbuf); + + /* + * It'd be good to check for interrupts here, but it's not easy to + * do so because a lock is always held. This block isn't + * frequently reached, so hopefully the consequences of not + * checking interrupts aren't too bad. + */ + if (leftsib == P_NONE) { elog(LOG, "no left sibling (concurrent deletion?) of block %u in \"%s\"", |