pg.ddx.io pgsql-bugs@postgresql.org mailing list archive
help / color / mirror / Atom feedpostgres 10.3 no response
3+ messages / 2 participants
[nested] [flat]
* postgres 10.3 no response
@ 2018-04-22 04:28 Сергей <gtm7@yandex.ru>
0 siblings, 1 reply; 3+ messages in thread
From: Сергей @ 2018-04-22 04:28 UTC (permalink / raw)
To: pgsql-bugs
Hello!
In versions 10.1 and 10.3, an error was detected.
When performing parallel loading of data into a partitioned table (300
partitions), the PostgreSQL DBMS enters the "no response" state. No new
connections are possible.
All processes are waiting for the release of the semaphore.
OS version
Linux asu-oko-test 3.0.101-77-ppc64 #1 SMP Tue Jun 14 20:33:58 UTC 2016
(a082ea6) ppc64 ppc64 ppc64 GNU/Linux
Postgresql version
PostgreSQL 10.3 on powerpc64-unknown-linux-gnu, compiled by gcc-5 (SUSE
Linux) 5.3.1 20160301 [gcc-5-branch revision 233849], 64-bit
Test case in the attachment
Attachments:
bug_test.tgz (application/octet-stream, 6.2K) download
^ permalink raw reply [nested|flat] 3+ messages in thread
* postgres 10.3 no response
@ 2018-04-22 05:02 Сергей <gtm7@yandex.ru>
parent: Сергей <gtm7@yandex.ru>
0 siblings, 0 replies; 3+ messages in thread
From: Сергей @ 2018-04-22 05:02 UTC (permalink / raw)
To: pgsql-bugs
Hello!
In versions 10.1 and 10.3, an error was detected.
When performing parallel loading of data into a partitioned table (300
partitions), the PostgreSQL DBMS enters the "no response" state. No new
connections are possible.
All processes are waiting for the release of the semaphore.
OS version
Linux asu-oko-test 3.0.101-77-ppc64 #1 SMP Tue Jun 14 20:33:58 UTC 2016
(a082ea6) ppc64 ppc64 ppc64 GNU/Linux
Postgresql version
PostgreSQL 10.3 on powerpc64-unknown-linux-gnu, compiled by gcc-5 (SUSE
Linux) 5.3.1 20160301 [gcc-5-branch revision 233849], 64-bit
Test case in the attachment
Attachments:
bug_test.tgz (application/octet-stream, 6.2K) download
^ permalink raw reply [nested|flat] 3+ messages in thread
* [PATCH v3 2/3] Fix possible HOT corruption when RECENTLY_DEAD changes to DEAD while pruning.
@ 2021-11-22 17:51 Andres Freund <andres@anarazel.de>
0 siblings, 0 replies; 3+ messages in thread
From: Andres Freund @ 2021-11-22 17:51 UTC (permalink / raw)
Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
src/backend/access/heap/pruneheap.c | 71 +++++++++++++++----
.../expected/prune-recently-dead.out | 37 +++++-----
2 files changed, 75 insertions(+), 33 deletions(-)
inline patch 1: src/backend/access/heap/pruneheap.c, src/test/isolation/expected/prune-recently-dead.out (+75/-33 lines) download inline-1.patch
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 5c0b60319d8..d304fd3dcdb 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -58,12 +58,18 @@ typedef struct
OffsetNumber nowunused[MaxHeapTuplesPerPage];
/* marked[i] is true if item i is entered in one of the above arrays */
bool marked[MaxHeapTuplesPerPage + 1];
+
+ /* htsv is only computed once for each tuple */
+ int8 htsv[MaxHeapTuplesPerPage + 1];
} PruneState;
/* Local functions */
static int heap_prune_chain(Buffer buffer,
OffsetNumber rootoffnum,
PruneState *prstate);
+static HTSV_Result heap_prune_satisfies_vacuum(PruneState *prstate,
+ HeapTuple tup,
+ Buffer buffer);
static void heap_prune_record_prunable(PruneState *prstate, TransactionId xid);
static void heap_prune_record_redirect(PruneState *prstate,
OffsetNumber offnum, OffsetNumber rdoffnum);
@@ -252,6 +258,7 @@ heap_page_prune(Relation relation, Buffer buffer,
OffsetNumber offnum,
maxoff;
PruneState prstate;
+ HeapTupleData tup;
/*
* Our strategy is to scan the page and make lists of items to change,
@@ -274,6 +281,51 @@ heap_page_prune(Relation relation, Buffer buffer,
prstate.nredirected = prstate.ndead = prstate.nunused = 0;
memset(prstate.marked, 0, sizeof(prstate.marked));
+ /*
+ * Determine HTSV for all tuples.
+ *
+ * This is required for correctness to deal with cases where running HTSV
+ * twice could result in different results (e.g. RECENTLY_DEAD can turn to
+ * DEAD if another checked item causes GlobalVisTestIsRemovableFullXid()
+ * to update the horizon, or INSERT_IN_PROGRESS can change to DEAD if the
+ * inserting transaction aborts). That in turn could cause
+ * heap_prune_chain() to behave incorrectly if a tuple is reached twice,
+ * once directly via a heap_prune_chain() and once by following a HOT chain.
+ *
+ * It's also good for performance. Most commonly tuples within a page are
+ * stored at decreasing offsets (while the items are stored at increasing
+ * offsets). When processing all tuples on a page this leads to reading
+ * memory at decreasing offsets within a page, with a variable
+ * stride. That's hard for CPU prefetchers to deal with. Processing the
+ * items in reverse order (and thus the tuples in increasing order)
+ * increases prefetching efficiency significantly / decreases the number
+ * of cache misses.
+ */
+ tup.t_tableOid = RelationGetRelid(prstate.rel);
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (offnum = maxoff;
+ offnum >= FirstOffsetNumber;
+ offnum = OffsetNumberPrev(offnum))
+ {
+ ItemId itemid;
+ HeapTupleHeader htup;
+
+ /* Nothing to do if slot doesn't contain tuple */
+ itemid = PageGetItemId(page, offnum);
+ if (!ItemIdIsNormal(itemid))
+ {
+ prstate.htsv[offnum] = -1;
+ continue;
+ }
+
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+ tup.t_data = htup;
+ tup.t_len = ItemIdGetLength(itemid);
+ ItemPointerSet(&(tup.t_self), BufferGetBlockNumber(buffer), offnum);
+
+ prstate.htsv[offnum] = heap_prune_satisfies_vacuum(&prstate, &tup, buffer);
+ }
+
/* Scan the page */
maxoff = PageGetMaxOffsetNumber(page);
for (offnum = FirstOffsetNumber;
@@ -531,9 +583,6 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
OffsetNumber chainitems[MaxHeapTuplesPerPage];
int nchain = 0,
i;
- HeapTupleData tup;
-
- tup.t_tableOid = RelationGetRelid(prstate->rel);
rootlp = PageGetItemId(dp, rootoffnum);
@@ -542,12 +591,9 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
*/
if (ItemIdIsNormal(rootlp))
{
+ Assert(prstate->htsv[rootoffnum] != -1);
htup = (HeapTupleHeader) PageGetItem(dp, rootlp);
- tup.t_data = htup;
- tup.t_len = ItemIdGetLength(rootlp);
- ItemPointerSet(&(tup.t_self), BufferGetBlockNumber(buffer), rootoffnum);
-
if (HeapTupleHeaderIsHeapOnly(htup))
{
/*
@@ -568,8 +614,8 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
* either here or while following a chain below. Whichever path
* gets there first will mark the tuple unused.
*/
- if (heap_prune_satisfies_vacuum(prstate, &tup, buffer)
- == HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup))
+ if (prstate->htsv[rootoffnum] == HEAPTUPLE_DEAD &&
+ !HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
HeapTupleHeaderAdvanceLatestRemovedXid(htup,
@@ -636,12 +682,9 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
break;
Assert(ItemIdIsNormal(lp));
+ Assert(prstate->htsv[offnum] != -1);
htup = (HeapTupleHeader) PageGetItem(dp, lp);
- tup.t_data = htup;
- tup.t_len = ItemIdGetLength(lp);
- ItemPointerSet(&(tup.t_self), BufferGetBlockNumber(buffer), offnum);
-
/*
* Check the tuple XMIN against prior XMAX, if any
*/
@@ -659,7 +702,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
*/
tupdead = recent_dead = false;
- switch (heap_prune_satisfies_vacuum(prstate, &tup, buffer))
+ switch (prstate->htsv[offnum])
{
case HEAPTUPLE_DEAD:
tupdead = true;
diff --git a/src/test/isolation/expected/prune-recently-dead.out b/src/test/isolation/expected/prune-recently-dead.out
index 02ef2713d86..e69822c29ce 100644
--- a/src/test/isolation/expected/prune-recently-dead.out
+++ b/src/test/isolation/expected/prune-recently-dead.out
@@ -157,11 +157,11 @@ t |ShareUpdateExclusiveLock|relation|many_updates
step s9_rollback: ROLLBACK;
step vac_vacuum: <... completed>
step mon_page: SELECT lp, lp_off, lp_flags, lp_len, /* t_xmin, t_xmax, */ t_field3, t_ctid, t_infomask2, t_infomask, mask.raw_flags, mask.combined_flags, t_hoff, t_bits FROM heap_page_items(get_raw_page('many_updates', 0)), heap_tuple_infomask_flags(t_infomask, t_infomask2) AS mask
-lp|lp_off|lp_flags|lp_len|t_field3|t_ctid|t_infomask2|t_infomask|raw_flags|combined_flags|t_hoff|t_bits
---+------+--------+------+--------+------+-----------+----------+---------+--------------+------+------
- 1| 3| 2| 0| | | | | | | |
- 2| 0| 3| 0| | | | | | | |
- 3| 0| 0| 0| | | | | | | |
+lp|lp_off|lp_flags|lp_len|t_field3|t_ctid|t_infomask2|t_infomask|raw_flags |combined_flags|t_hoff|t_bits
+--+------+--------+------+--------+------+-----------+----------+----------------------------------------------------------------------------------------+--------------+------+------
+ 1| 3| 2| 0| | | | | | | |
+ 2| 0| 3| 0| | | | | | | |
+ 3| 8152| 1| 36| 0|(0,3) | 40963| 9472|{HEAP_XMIN_COMMITTED,HEAP_XMAX_COMMITTED,HEAP_UPDATED,HEAP_KEYS_UPDATED,HEAP_ONLY_TUPLE}|{} | 24|
(3 rows)
step mon_view:
@@ -181,10 +181,9 @@ step mon_verify:
SELECT * FROM verify_heapam('many_updates');
SELECT * FROM bt_index_parent_check('many_updates_id_idx', true, true);
-blkno|offnum|attnum|msg
------+------+------+---------------------------------------------------
- 0| 1| |line pointer redirection to unused item at offset 3
-(1 row)
+blkno|offnum|attnum|msg
+-----+------+------+---
+(0 rows)
bt_index_parent_check
---------------------
@@ -195,22 +194,22 @@ step s1_insert_17:
INSERT INTO many_updates (id) VALUES(17);
step mon_page: SELECT lp, lp_off, lp_flags, lp_len, /* t_xmin, t_xmax, */ t_field3, t_ctid, t_infomask2, t_infomask, mask.raw_flags, mask.combined_flags, t_hoff, t_bits FROM heap_page_items(get_raw_page('many_updates', 0)), heap_tuple_infomask_flags(t_infomask, t_infomask2) AS mask
-lp|lp_off|lp_flags|lp_len|t_field3|t_ctid|t_infomask2|t_infomask|raw_flags |combined_flags|t_hoff|t_bits
---+------+--------+------+--------+------+-----------+----------+-------------------+--------------+------+------
- 1| 3| 2| 0| | | | | | | |
- 2| 0| 3| 0| | | | | | | |
- 3| 8152| 1| 36| 0|(0,3) | 3| 2048|{HEAP_XMAX_INVALID}|{} | 24|
-(3 rows)
+lp|lp_off|lp_flags|lp_len|t_field3|t_ctid|t_infomask2|t_infomask|raw_flags |combined_flags|t_hoff|t_bits
+--+------+--------+------+--------+------+-----------+----------+----------------------------------------------------------------------------------------+--------------+------+------
+ 1| 3| 2| 0| | | | | | | |
+ 2| 0| 3| 0| | | | | | | |
+ 3| 8152| 1| 36| 0|(0,3) | 40963| 9472|{HEAP_XMIN_COMMITTED,HEAP_XMAX_COMMITTED,HEAP_UPDATED,HEAP_KEYS_UPDATED,HEAP_ONLY_TUPLE}|{} | 24|
+ 4| 8112| 1| 36| 0|(0,4) | 3| 2048|{HEAP_XMAX_INVALID} |{} | 24|
+(4 rows)
step s1_select_1:
SET LOCAL enable_seqscan = false;
SELECT ctid, /*xmin, xmax, */ id FROM many_updates WHERE id = 1;
RESET enable_seqscan;
-ctid |id
------+--
-(0,3)|17
-(1 row)
+ctid|id
+----+--
+(0 rows)
step mon_verify:
SELECT * FROM verify_heapam('many_updates');
--
2.34.0
--gfxrd4rbvltax4sz
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v3-0003-Verify-redirect-items-are-still-correct-after-hea.patch"
^ permalink raw reply [nested|flat] 3+ messages in thread
end of thread, other threads:[~2021-11-22 17:51 UTC | newest]
Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2018-04-22 04:28 postgres 10.3 no response Сергей <gtm7@yandex.ru>
2018-04-22 05:02 ` Сергей <gtm7@yandex.ru>
2021-11-22 17:51 [PATCH v3 2/3] Fix possible HOT corruption when RECENTLY_DEAD changes to DEAD while pruning. Andres Freund <andres@anarazel.de>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox