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 1nNKDN-0002lg-8d for pgsql-performance@arkaria.postgresql.org; Thu, 24 Feb 2022 19:55:49 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1nNKDL-0007JJ-HQ for pgsql-performance@arkaria.postgresql.org; Thu, 24 Feb 2022 19:55:47 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nNKDL-0007Gj-7Y for pgsql-performance@lists.postgresql.org; Thu, 24 Feb 2022 19:55:47 +0000 Received: from mout.perfora.net ([74.208.4.194]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nNKDI-0004Et-K5 for pgsql-performance@lists.postgresql.org; Thu, 24 Feb 2022 19:55:46 +0000 Received: from [192.168.0.165] ([98.169.214.209]) by mrelay.perfora.net (mreueus004 [74.208.5.2]) with ESMTPSA (Nemesis) id 1MwPs1-1oCXt61a9F-00sQMD; Thu, 24 Feb 2022 20:55:37 +0100 Subject: Re: Slow plan choice with prepared query From: MichaelDBA To: Mark Saward Cc: pgsql-performance@lists.postgresql.org References: <6a1865a0-0379-d575-a7d7-87b01852d997@manse.cloud> <22689907-c5f1-eb8a-f567-6d55abfaf163@sqlexec.com> Message-ID: Date: Thu, 24 Feb 2022 14:55:34 -0500 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 PostboxApp/7.0.54 MIME-Version: 1.0 In-Reply-To: <22689907-c5f1-eb8a-f567-6d55abfaf163@sqlexec.com> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-Language: en-US X-Provags-ID: V03:K1:o/VyQvr+Wyob5EzbJAaPCYQGoRB3kXIziZqioFKiYV5XHFdpSVz sRrLWt6BEhI2NKv+h422rhL/T5o5qibMpmBOgfVvJKkzBMJ8Kp2WRkYvOZzEqA1XPwFQIsy e6ba01pyyOWegFt302OphbJb/UDoDryc5C2xH4trn2TKFJeJEO2BA074CVQWt3UMuS3Nc29 sOfg/iH60ESVHSpYmllHA== X-Spam-Flag: NO X-UI-Out-Filterresults: notjunk:1;V03:K0:f1BAMwr9j8E=:X/dtK6V0lcMsiZQb0sBroV WV3OkzYN9M66aSqQY/oz/1w0sADNY7kUOHr1SxrjXeqSYGpl4quabSB31k3vNnCimV95dGSey zO2uiM3lxz6JVCNcF42h8QDWPd/NVF7yShIwelPEIMqy16qbVXV9loBI7sCUr+U6r4VBdU3q9 O43MJrUPlZXhHAKdzpHWHic68wnu6CKyLCeTkimovfaZ7mT/42bMCvZbuj6b/TRD3MY8Hed52 esa1tSzLFwCvxKcfbYFO4ezjX7eYuX4CS35D3v189x6ErEY1GyfpdbRc0bocd041u3bqQUQW2 qPyXO7HHF2M1ox3067Rg3xctghIAexvUzrtOFhGsXj6GbTFml8VJPdj5t/b3R548Fw/5lpTnD 675XvOP6fUgPaXSYo5lyr5laNntZpU3n3aVKiv4UBL9fy+EfB4ipGM7gvPl82iqHc05uJr0lD 1CwI7spSE1Rpw4gTScDmnlHOQrprP/vi/Q4ILceznmfgIoXHrtlotIT9cBmqNWQmO7MW9s7Ap Olb5RBb2PXa6NgPd6i0M8qWW7wqqjAJAThZE3wtG7xSeW2a8COmzxqAYW2ufUUuKrgw/q52a+ iLpfM1ZE4svst0U8bICMMGNPGb3J7OqeotSe4U2SkaJlGh7gldsrpaPjDiJZh0qRAuE5vo3e8 v6tgbzwHoJdV0hiucEsklZm4z4hRppK79S1KaC+y4ee/MLttNA7RWXQ838rZ7hVQm370D5cAV 49ylyi0qh8FjYpbv List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk As per PG official documentation on PREPARE, it is working as expected.  Use custom plan, but after 5th iteration compare cost of custom plan vs generic plan and use the one with the less cost which is the generic plan even though it is not as performant. Look at explain output to see the diffs between 5th iteration and 6th one:  explain (analyze, summary, buffers true) execute foo_test(null, null, 5, 500, true); It appears the SORT is the problem and a mismatch between text and integer for base.text_id? --> WHERE  base.test_id = $4 Regards, Michael Vitale MichaelDBA wrote on 2/24/2022 1:45 PM: > Dag, if you ain't right!  I can duplicate this on the ones I tested > with: PG v11 and v14.  Gonna start diving into this myself... > > Regards, > Michael Vitale > > > Mark Saward wrote on 2/23/2022 10:37 PM: >> Hi, >> >> >> I've experienced a situation where the planner seems to make a very >> poor choice with a prepared query after the first five executions. >> Looking at the documentation, I think this happens because it >> switches from a custom plan to a generic one, and doesn't make a good >> choice for the generic one. >> >> Postgres version: running in docker, reports to be 'Debian >> 14.1-1.pgdg110+1' >> >> If I force it to use a custom plan via 'set local plan_cache_mode = >> force_custom_plan', then I don't notice any slowdown.  Without it, >> the 6th and onwards calls can take 1 second to 15 seconds each, as >> opposed to about 10ms. >> >> Since I have a workaround, I don't necessarily need assistance, but >> posting this here in case it's of value as a test case. Here's a test >> case that reliably duplicates this issue for me: >> >> ---- >> >> create table test ( >>   test_id serial primary key, >>   data text >> ); >> >> insert into test (data) (select data from (select >> generate_series(1,10000) AS id, md5(random()::text) AS data) x); >> >> prepare foo_test(text, text, int, text, bool) as SELECT * FROM (SELECT >>   *, >>   count(*) OVER () > $3 AS has_more, >>   row_number() OVER () >>   FROM ( >>     WITH counted AS ( >>       SELECT count(*) AS total >>       FROM   (select test_id::text, data >> from test >> where >>   (cast($1 as text) is null or lower(data) like '%' || lower($1) || '%') >> and >>   (cast($2 as text) is null or lower(test_id::text) like '%' || >> lower($2) || '%')) base >>     ), cursor_row AS ( >>       SELECT base.test_id >>       FROM   (select test_id::text, data >> from test >> where >>   (cast($1 as text) is null or lower(data) like '%' || lower($1) || '%') >> and >>   (cast($2 as text) is null or lower(test_id::text) like '%' || >> lower($2) || '%')) base >>       WHERE  base.test_id = $4 >>     ) >>     SELECT counted.*, base.* >>       FROM   (select test_id::text, data >> from test >> where >>   (cast($1 as text) is null or lower(data) like '%' || lower($1) || '%') >> and >>   (cast($2 as text) is null or lower(test_id::text) like '%' || >> lower($2) || '%')) base >>       LEFT JOIN   cursor_row ON true >>       LEFT JOIN   counted ON true >>       WHERE (( >>             $4 IS NULL OR cast($5 as bool) IS NULL >>           ) OR ( >>             (base.test_id) >>               > (cursor_row.test_id) >>           )) >>       ORDER BY base.test_id ASC >>       LIMIT $3 + 1 >> ) xy LIMIT $3 ) z ORDER BY row_number ASC; >> >> \timing >> >> execute foo_test(null, null, 5, 500, true); >> execute foo_test(null, null, 5, 500, true); >> execute foo_test(null, null, 5, 500, true); >> execute foo_test(null, null, 5, 500, true); >> execute foo_test(null, null, 5, 500, true); >> >> -- This one should be slower: >> execute foo_test(null, null, 5, 500, true); >> >> >> > > >