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 1q9mtJ-0004qP-5o for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 13:19:57 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1q9mtG-0004Yq-Nq for pgsql-hackers@arkaria.postgresql.org; Thu, 15 Jun 2023 13:19:54 +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 1q9mtG-0004YR-Dk for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 13:19:54 +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 1q9mtC-002LvR-RT for pgsql-hackers@lists.postgresql.org; Thu, 15 Jun 2023 13:19:52 +0000 Received: by mail-ej1-x62a.google.com with SMTP id a640c23a62f3a-982b1a18daeso69941066b.2 for ; Thu, 15 Jun 2023 06:19:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=enterprisedb.com; s=google; t=1686835188; x=1689427188; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id:from :to:cc:subject:date:message-id:reply-to; bh=Z6s2Rq7DEHxV+EPRqzmoe6H2pBrtlpc11q6WvlX3ED4=; b=gkDYDpUABvFgXE0y7pSQj9J/6BAQcO4DQUiSgai0XHjIFcNRz3MMaxS2Gaaa4t2/mo fJ31SxML2DqJEOTjDyoRFtyMB6uvtD0VP/LCSTxk/6IjwHOqDiUzmPTPwgv3Nit6UxEl shqE3eqOpdNFlJyqWXdx24Mu6U8Zm8Iyu4TC5DpLT93wpGrAnBosDArCK4TMrSyCsV7H tQIrISCHtoNsyhFe5ThMyS6JRzZ8X5XC9cdYP1JzgtvvyWbbg2V3/mwmOioYl1n42zOY OJ92MnIM0EEwxSxYYvurx+7iuZ6Rl6BSKRs87813o8a8H4kh4JoSS+jh7WTBZulRrKJC ZhBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686835188; x=1689427188; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Z6s2Rq7DEHxV+EPRqzmoe6H2pBrtlpc11q6WvlX3ED4=; b=VteJlPFOgBFIauffUUVZpo6BhO0lzS3D+GJXZKKn0TfKi6sRFW1uDuJDqHLhx2yer/ 9D7wXfKulNZVHe/WD2I+vUOJtFbVI9yzahp8Komts7BrDjdkNlvwshUbwLWEANu9jF9b mhPp9Am1pj34n4rUZdK93Qr9HxCiOFA0uLdUDrMzfTUPih0XrX79K3GxWVhELc5SPQOP y80i02Y1JYSCR87/tujiVwaQepvD3fbLQk/2MpsEudYi1h+4LH+WzkRbBR8Fto7vu2pf Sb8/bJp6Eh9PBEFgVFsbA6qIUdEToIe+24MpkaJDydE12vbkfZNQjhbMi5vBEB4712zc HTWA== X-Gm-Message-State: AC+VfDzzfB+n0eLuuur2C9KGlMAcj+Gi0ICMfRTaqJp/t2o2KAJrrHt+ jMKtrUdQl1xndxYPAjRzQVnK0w== X-Google-Smtp-Source: ACHHUZ6bT3b7aP2iQDUU1KQsb8er/YdPAfWTNzjaTpvvjOB6uOC4HzWEgcb6vZN+Bvk2HYi3uf1McQ== X-Received: by 2002:a17:906:7951:b0:94e:e97b:c65 with SMTP id l17-20020a170906795100b0094ee97b0c65mr19887073ejo.60.1686835188447; Thu, 15 Jun 2023 06:19:48 -0700 (PDT) Received: from [10.137.0.17] (static-84-42-175-93.bb.vodafone.cz. [84.42.175.93]) by smtp.gmail.com with ESMTPSA id sd15-20020a170906ce2f00b0094ebc041e20sm9242243ejb.46.2023.06.15.06.19.47 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 15 Jun 2023 06:19:47 -0700 (PDT) Message-ID: <99e584a1-2f55-a56d-efb1-d38c57eb2beb@enterprisedb.com> Date: Thu, 15 Jun 2023 15:19:47 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: Memory leak in incremental sort re-scan To: Laurenz Albe , pgsql-hackers@lists.postgresql.org References: Content-Language: en-US From: Tomas Vondra In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, On 6/15/23 13:48, Laurenz Albe wrote: > ExecIncrementalSort() calls tuplesort_begin_common(), which creates the "TupleSort main" > and "TupleSort sort" memory contexts, and ExecEndIncrementalSort() calls tuplesort_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 contexts for every > re-scan. > > Here is a reproducer with the regression test database: > > SET enable_sort = off; > SET enable_hashjoin = off; > SET enable_mergejoin = off; > SET enable_material = off; > > SELECT t.unique2, t2.r > FROM tenk1 AS t > JOIN (SELECT unique1, > row_number() OVER (ORDER BY hundred, thousand) AS r > FROM tenk1 > OFFSET 0) AS t2 > ON t.unique1 + 0 = t2.unique1 > WHERE t.unique1 < 1000; > > The execution plan: > > Nested Loop > Join Filter: ((t.unique1 + 0) = 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); 188008 used > TupleSort main: 32832 total in 2 blocks; 7320 free (0 chunks); 25512 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); 25576 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); 25512 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 *node) > node->execution_status = INCSORT_LOADFULLSORT; > > /* > - * 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 to > - * repay the setup cost, and because ExecIncrementalSort guards presorted > - * 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 != NULL) > { > - tuplesort_reset(node->fullsort_state); > + tuplesort_end(node->fullsort_state); > node->fullsort_state = NULL; > } > if (node->prefixsort_state != NULL) > { > - tuplesort_reset(node->prefixsort_state); > + tuplesort_end(node->prefixsort_state); > node->prefixsort_state = NULL; > } > > > The original comment hints that this might mot be the correct thing to do... > I think it's correct, but I need to look at the code more closely - it's been a while. The code is a bit silly, as it resets the tuplesort and then throws away all the pointers - so what could the _end() break? AFAICS the comment says that we can't just do tuplesort_reset and keep the pointers, because some other code depends on them being NULL. In hindsight, that's a bit awkward - it'd probably be better to have a separate flag, which would allow us to just reset the tuplesort. regards -- Tomas Vondra EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company