Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9lTD-00012W-1D for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 11:48:55 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1q9lTA-0008KH-OJ for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 11:48:52 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9lTA-0008Fd-Af for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 11:48:52 +0000 Received: from mail-ej1-x62a.google.com ([2a00:1450:4864:20::62a]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1q9lT6-002L4s-IA for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 11:48:50 +0000 Received: by mail-ej1-x62a.google.com with SMTP id a640c23a62f3a-982a0232bdcso100188866b.1 for ; Thu, 15 Jun 2023 04:48:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cybertec-at.20221208.gappssmtp.com; s=20221208; t=1686829726; x=1689421726; h=mime-version:user-agent:content-transfer-encoding:date:to:from :subject:message-id:from:to:cc:subject:date:message-id:reply-to; bh=t9pQ3K7c2S6k/ethDhPctHY4iy+n2sNtyfLUg3NuP7I=; b=00nuO/yXGGSR/7lEFqc66rOEVKCrHbuJVcO7qoDYJVwCX7vP7RLPHvPYGUYiAMFhsw N86z+m6z3ogsV4hE1fgcHTFpPAX9kWExSbCgaYt7DOkZ0YwcnaFVjZWF9/W2u46T/4vh rs6z6T31/6lDznBju9E9/JK6JA4PsAAnkAp5HejSfl784e+OFyRStl8XopVk8akey6je lkl+VefB+79d8kFXq9NAmXTHmIkmlEwV3NAMN1vNyBL0zlmxl6+7pR0EP9MEu/YuSeye s9SPLkKUbMXrM58bWb+7bIl/CgtdtV1S4xeY3U2QTKVH/adElH90mU8VfY1CjPx9dwBQ c+Jg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686829726; x=1689421726; h=mime-version:user-agent:content-transfer-encoding:date:to:from :subject:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=t9pQ3K7c2S6k/ethDhPctHY4iy+n2sNtyfLUg3NuP7I=; b=lytN70sFxnRbTSWFN/atKeUXoTab6JHWNTj8DL5vnK01jHxY8IM7Byu1MXz25Jffm2 xcSikjMd9Mn5ZOaWtuM/K9P/PkzbjLZJxHaoh56ipLzw1r8OdLyHQTiiS/9JKrQAK3TC AjIFZgF/ER65XpYSOJvCv/p0/Em6TYRG+B0a6WXdFaPSG99Y5tSKB6+G7oXREGaKaif9 ZaqaOB4+Tk0JbAE0mze1ErEGYA/f5zb+JDG00gwENU2hudd8Ubo+qJ/ihlt90Cspm42G BQKJGwCTiExiMxwYuDZa3LTPNiRmxbZDvtvu90LYDAQ0vPyn/sdJsGt2t4SdNf8iijix y/3w== X-Gm-Message-State: AC+VfDym6hq4ihqTijpbIzUiwMCWTs6TMnQZIfWJ6Ll8Gjbos5wFUo5Z S8kiCYhThLSYhRiCZGZ9oKY1rgk3szWj5u+buXQ= X-Google-Smtp-Source: ACHHUZ424qGIoHXq+vcclxZr4lTYYdSlk08vdZhZFHeZ/bo0uJkKgUCvmNPRcY7v8AaQi3JdXulNvQ== X-Received: by 2002:a17:906:da87:b0:978:930e:2276 with SMTP id xh7-20020a170906da8700b00978930e2276mr18325885ejb.31.1686829725561; Thu, 15 Jun 2023 04:48:45 -0700 (PDT) Received: from localhost.localdomain (178-190-178-29.adsl.highway.telekom.at. [178.190.178.29]) by smtp.gmail.com with ESMTPSA id g19-20020a17090613d300b0097889c33582sm9320058ejc.215.2023.06.15.04.48.45 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Jun 2023 04:48:45 -0700 (PDT) Message-ID: Subject: Memory leak in incremental sort re-scan From: Laurenz Albe To: pgsql-hackers@lists.postgresql.org Date: Thu, 15 Jun 2023 13:48:44 +0200 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable User-Agent: Evolution 3.46.4 (3.46.4-1.fc37) MIME-Version: 1.0 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ExecIncrementalSort() calls tuplesort_begin_common(), which creates the "Tu= pleSort main" and "TupleSort sort" memory contexts, and ExecEndIncrementalSort() calls tu= plesort_end(), which destroys them. But ExecReScanIncrementalSort() only resets the memory contexts. Since the= next call to ExecIncrementalSort() will create them again, we end up leaking these conte= xts for every re-scan. Here is a reproducer with the regression test database: SET enable_sort =3D off; SET enable_hashjoin =3D off; SET enable_mergejoin =3D off; SET enable_material =3D off; SELECT t.unique2, t2.r FROM tenk1 AS t=20 JOIN (SELECT unique1,=20 row_number() OVER (ORDER BY hundred, thousand) AS r=20 FROM tenk1=20 OFFSET 0) AS t2=20 ON t.unique1 + 0 =3D t2.unique1 WHERE t.unique1 < 1000; The execution plan: Nested Loop Join Filter: ((t.unique1 + 0) =3D tenk1.unique1) -> Bitmap Heap Scan on tenk1 t Recheck Cond: (unique1 < 1000) -> Bitmap Index Scan on tenk1_unique1 Index Cond: (unique1 < 1000) -> WindowAgg -> Incremental Sort Sort Key: tenk1.hundred, tenk1.thousand Presorted Key: tenk1.hundred -> Index Scan using tenk1_hundred on tenk1 A memory context dump at the end of the execution looks like this: ExecutorState: 262144 total in 6 blocks; 74136 free (29 chunks); 1880= 08 used TupleSort main: 32832 total in 2 blocks; 7320 free (0 chunks); 2551= 2 used TupleSort sort: 8192 total in 1 blocks; 7928 free (0 chunks); 264= used Caller tuples: 8192 total in 1 blocks (0 chunks); 7984 free (0 = chunks); 208 used TupleSort main: 32832 total in 2 blocks; 7256 free (0 chunks); 2557= 6 used TupleSort sort: 8192 total in 1 blocks; 7928 free (0 chunks); 264= used Caller tuples: 8192 total in 1 blocks (0 chunks); 7984 free (0 = chunks); 208 used TupleSort main: 32832 total in 2 blocks; 7320 free (0 chunks); 2551= 2 used TupleSort sort: 8192 total in 1 blocks; 7928 free (0 chunks); 264= used Caller tuples: 8192 total in 1 blocks (0 chunks); 7984 free (0 = chunks); 208 used [many more] 1903 more child contexts containing 93452928 total in 7597 blocks; = 44073240 free (0 chunks); 49379688 used The following patch fixes the problem for me: --- a/src/backend/executor/nodeIncrementalSort.c +++ b/src/backend/executor/nodeIncrementalSort.c @@ -1145,21 +1145,16 @@ ExecReScanIncrementalSort(IncrementalSortState *nod= e) node->execution_status =3D INCSORT_LOADFULLSORT; =20 /* - * If we've set up either of the sort states yet, we need to reset them= . - * We could end them and null out the pointers, but there's no reason t= o - * repay the setup cost, and because ExecIncrementalSort guards presort= ed - * column functions by checking to see if the full sort state has been - * initialized yet, setting the sort states to null here might actually - * cause a leak. + * Release tuplesort resources. */ if (node->fullsort_state !=3D NULL) { - tuplesort_reset(node->fullsort_state); + tuplesort_end(node->fullsort_state); node->fullsort_state =3D NULL; } if (node->prefixsort_state !=3D NULL) { - tuplesort_reset(node->prefixsort_state); + tuplesort_end(node->prefixsort_state); node->prefixsort_state =3D NULL; } =20 The original comment hints that this might mot be the correct thing to do..= . Yours, Laurenz Albe