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 1nN4xE-0003Of-VG for pgsql-performance@arkaria.postgresql.org; Thu, 24 Feb 2022 03:38:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1nN4xD-0000R3-TG for pgsql-performance@arkaria.postgresql.org; Thu, 24 Feb 2022 03:38:07 +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 1nN4xD-0000Qu-Jn for pgsql-performance@lists.postgresql.org; Thu, 24 Feb 2022 03:38:07 +0000 Received: from mr85p00im-ztdg06011201.me.com ([17.58.23.181]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nN4xB-0004Ce-46 for pgsql-performance@lists.postgresql.org; Thu, 24 Feb 2022 03:38:07 +0000 Received: from [192.168.20.20] (119-17-153-133.771199.mel.nbn.aussiebb.net [119.17.153.133]) by mr85p00im-ztdg06011201.me.com (Postfix) with ESMTPSA id 40163960375 for ; Thu, 24 Feb 2022 03:38:02 +0000 (UTC) Message-ID: <6a1865a0-0379-d575-a7d7-87b01852d997@manse.cloud> Date: Thu, 24 Feb 2022 14:37:59 +1100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Thunderbird/98.0 Content-Language: en-US To: pgsql-performance@lists.postgresql.org From: Mark Saward Subject: Slow plan choice with prepared query Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Proofpoint-Virus-Version: =?UTF-8?Q?vendor=3Dfsecure_engine=3D1.1.170-22c6f66c430a71ce266a39bfe25bc?= =?UTF-8?Q?2903e8d5c8f:6.0.138,18.0.572,17.0.605.474.0000000_definitions?= =?UTF-8?Q?=3D2020-02-14=5F11:2020-02-14=5F02,2020-02-14=5F11,2020-01-23?= =?UTF-8?Q?=5F02_signatures=3D0?= X-Proofpoint-Spam-Details: rule=notspam policy=default score=1 suspectscore=0 mlxlogscore=193 spamscore=1 malwarescore=0 phishscore=0 clxscore=1030 mlxscore=1 bulkscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2202240017 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk 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);