0001-Fix-extreme-skew-detection-in-Parallel-Hash-Join.patch
text/x-patch
Filename: 0001-Fix-extreme-skew-detection-in-Parallel-Hash-Join.patch
Type: text/x-patch
Part: 0
Patch
Format: format-patch
Series: patch 0001
Subject: Fix extreme skew detection in Parallel Hash Join.
| File | + | − |
|---|---|---|
| src/backend/executor/nodeHash.c | 12 | 5 |
From aa570569cf16cdc1fe03b07c22d54da84745d041 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Wed, 16 Oct 2024 14:38:54 +1300
Subject: [PATCH] Fix extreme skew detection in Parallel Hash Join.
When repartitioning the inner side of a hash join that would exceed the
allowed size, we check if all the tuples from a parent partition moved
to one child partition. This is strong evidence that it contains
duplicate keys and later attempts to repartition will also fail, so we
should give up trying to limit memory (for lack of a better fallback
strategy).
A thinko prevented the check from working correctly in partition 0 (the
one that is partially loaded into memory already). After
repartitioning, we should check for extreme skew if the *parent*
partition's space_exhausted flag was set, not the child partition's.
The consequence was a repartitioning loop that ran until the array
holding per-partition data exceeded reasonable size. (We could also
handle high partition count failures and cleanup better, patches to
follow.)
This problem became more likely when PostgreSQL 16 introduced support
for Parallel Hash Right/Full Join, so that a NULL key could be in the
hash table. Partitioning always leaves NULL in partition 0, no matter
how many times you do it, which would be very unlikely for non-NULL
values.
Back-patch to all supported releases.
Reported-by: Craig Milhiser <craig@milhiser.com>
Analyzed-by: Andrei Lepikhov <lepihov@gmail.com>
Discussion: https://postgr.es/m/CA%2BwnhO1OfgXbmXgC4fv_uu%3DOxcDQuHvfoQ4k0DFeB0Qqd-X-rQ%40mail.gmail.com
---
src/backend/executor/nodeHash.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 570a90ebe15..0456a017dc6 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -1228,6 +1228,7 @@ ExecParallelHashIncreaseNumBatches(HashJoinTable hashtable)
if (BarrierArriveAndWait(&pstate->grow_batches_barrier,
WAIT_EVENT_HASH_GROW_BATCHES_DECIDE))
{
+ ParallelHashJoinBatch *old_batches;
bool space_exhausted = false;
bool extreme_skew_detected = false;
@@ -1235,25 +1236,31 @@ ExecParallelHashIncreaseNumBatches(HashJoinTable hashtable)
ExecParallelHashEnsureBatchAccessors(hashtable);
ExecParallelHashTableSetCurrentBatch(hashtable, 0);
+ old_batches = dsa_get_address(hashtable->area, pstate->old_batches);
+
/* Are any of the new generation of batches exhausted? */
for (int i = 0; i < hashtable->nbatch; ++i)
{
- ParallelHashJoinBatch *batch = hashtable->batches[i].shared;
+ ParallelHashJoinBatch *batch;
+ ParallelHashJoinBatch *old_batch;
+ int parent;
+ batch = hashtable->batches[i].shared;
if (batch->space_exhausted ||
batch->estimated_size > pstate->space_allowed)
- {
- int parent;
-
space_exhausted = true;
+ parent = i % pstate->old_nbatch;
+ old_batch = NthParallelHashJoinBatch(old_batches, parent);
+ if (old_batch->space_exhausted ||
+ batch->estimated_size > pstate->space_allowed)
+ {
/*
* Did this batch receive ALL of the tuples from its
* parent batch? That would indicate that further
* repartitioning isn't going to help (the hash values
* are probably all the same).
*/
- parent = i % pstate->old_nbatch;
if (batch->ntuples == hashtable->batches[parent].shared->old_ntuples)
extreme_skew_detected = true;
}
--
2.46.2