Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2xFd-000lLO-1v for pgsql-performance@arkaria.postgresql.org; Wed, 18 Mar 2026 20:12:21 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2xFc-00E5nk-1X for pgsql-performance@arkaria.postgresql.org; Wed, 18 Mar 2026 20:12:20 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2xFc-00E5nc-02 for pgsql-performance@lists.postgresql.org; Wed, 18 Mar 2026 20:12:20 +0000 Received: from mail-lf1-x12d.google.com ([2a00:1450:4864:20::12d]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2xFZ-00000000zsU-1ht9 for pgsql-performance@lists.postgresql.org; Wed, 18 Mar 2026 20:12:19 +0000 Received: by mail-lf1-x12d.google.com with SMTP id 2adb3069b0e04-59e4a04f059so269304e87.2 for ; Wed, 18 Mar 2026 13:12:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773864736; x=1774469536; darn=lists.postgresql.org; h=in-reply-to:from:content-language:references:cc:to:subject :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=jv5GIaSWotRtnuXPxCWXUH5ZNhQYKM/JOvy3MGepD7E=; b=k1K8juAkrUBbrUbb16niScq9UKcdc8/52DsW3VL9xNtQ3O/T1sp8rSNPQYRgPvlXLG zIAz5xpqP2EOYpPSg801TSy/Zi1OVdcgBRPD4SNm4IpZJkOdnchyebuXnXIUD/qQaptE 4FX3QuwbfNSkXajg5VUdqL5e/MEffho+S6CMZnonzS+/cuyzC/2HXwlQMUE3lyUbPvFx 1OOvPidJ6SOPvvxxcKSJf2hg2jA35imBkpy0AdWWorT5yko1IT4yw1XmD5+j7Js5/Nvh qQemK1j4OwjmzeLzn4xeV53p20RZO1RzrJRcUTtuySliGrXr/7+6DdJYUQPcI60IV470 7fbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773864736; x=1774469536; h=in-reply-to:from:content-language:references:cc:to:subject :user-agent:mime-version:date:message-id:x-gm-gg:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=jv5GIaSWotRtnuXPxCWXUH5ZNhQYKM/JOvy3MGepD7E=; b=WZAhbbDmMY4UuBvmnG8MJObBRJ4hoGxZmlhhA7v05Gq9amNP2adlPbUg2LuqTW5032 05NdIVaDvn2rApDfQJK32Nhid3o2VdWogIGL9Dg99B5rbDISAZQxNZWqarwtD4gM7s6F 7sUUNvJ/CgsZr2qxSS5eFDzSpTbvzrNVqDWhw3CHg5qlvlHKVsUja+KQH+r5Ywo6Abkj cBMHmzpitb0PAO1yHyFHxvqhwVGzdojIdcGAl/Zc73Ho4hXYwJ+jL9wiNjMoEJMOPJ1K 95YUniomlCsFZNmLP/uqDedQCsvYDEj6FAjPq/CyMQrrolP25VCSYjsxqlVi0ve+80qK shBg== X-Forwarded-Encrypted: i=1; AJvYcCVTAYo4YqyXqn6sbGE3id+L6MbVCwEMl6kL/hh5ntbIfaV0dXnwEvbwpbxbI1NW6Pmyflo7H6+FMSN+bCsUho7ITQ==@lists.postgresql.org X-Gm-Message-State: AOJu0YxWImZRB0bTMI5oZ2jCyTNyJ2GlamP5lkmmc8yKbeNijRZJaqqN 4JUusdovbb1Ijp4dMmZQWV4uJi8VpGj23ch6kyOLEJBkbKu8r77Ok9Ld X-Gm-Gg: ATEYQzxi6oFB3gR4n8rS1mO3YO8/nEr2/XRNJxcJV9lIh6WewmHi3kFS5DNEbxASxxy bUz/5q4uwnnqAeFDJk75D6YpOqcUdJv+TSTU2XJZMZOjc1Jizdku87yFVMSsg23OwWsf2oPDjRo 0lqpUpub6rTCHGdP1b9l52heDyxe6z59GmQ022YIcFO8hprRywONxv99iuNOIBZPaM7TCu47REb W3JPAJUQoeY7SrgHG+dzEFfLzE0/UNnRuZoDHJdWdGt0VHN/Vm0vpgBNOlWi5VovXb3QkrJ9b4C 37SOuoNibT90NrNi4Ajs/kv+22LAZgq3u1QTc6CPV17NHLTTH5+VFgSBeHQtPBf435gZI8uGxS3 EKMJk4l40HSo747mNHBhJcAc4dDEtClSewEtZp4bQNUcqASo8H0apO/4BuHtBXprE/3GyUJusHd qWegm7dPOAXn+5eSB4Qud5byj6vX2K3iklNmLxRrkQ X-Received: by 2002:a05:6512:1253:b0:5a2:7b89:4c60 with SMTP id 2adb3069b0e04-5a27b894d1dmr1108011e87.7.1773864735611; Wed, 18 Mar 2026 13:12:15 -0700 (PDT) Received: from [192.168.1.233] ([46.233.216.84]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5a279c743b1sm722013e87.58.2026.03.18.13.12.14 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 18 Mar 2026 13:12:14 -0700 (PDT) Content-Type: multipart/alternative; boundary="------------d1P55AfiRpc8Xj38FPurpBdN" Message-ID: <37499019-dab7-4a6b-933a-d78f88d2664c@gmail.com> Date: Thu, 19 Mar 2026 02:12:13 +0600 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: postgres chooses objectively wrong index To: Merlin Moncure Cc: Tom Lane , pgsql-performance@lists.postgresql.org References: <574349.1773786268@sss.pgh.pa.us> <381e7d47-8021-458d-9a64-0cdb80b004f0@gmail.com> Content-Language: en-US From: Alexey Ermakov In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------d1P55AfiRpc8Xj38FPurpBdN Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 2026-03-19 00:38, Merlin Moncure wrote: > On Tue, Mar 17, 2026 at 11:27 PM Alexey Ermakov > wrote: > > On 2026-03-18 04:52, Merlin Moncure wrote: >> >> Hello. I think planner doesn't have information about >> distribution of *async.task_execution_state(task)* unless >> it's part of any full index. I would try to give that with >> extended statistics (postgresql 14+): >> >> create statistics (mcv) task_task_execution_state_stat on ((async.task_execution_state(task))) from async.task; >> analyze async.task; >> >> If that won't help - please show distribution from >> pg_stats_ext view for extended statistic above. >> >> >> This unfortunately fails, probably because the table type >> includes system columns (despite not using them). >> orchestrator_service_user@orchestrator=> create statistics >>  task_stats (mcv) on (async.task_execution_state(task)) from >> async.task; >> ERROR:  statistics creation on system columns is not supported >> >> This would require some refactoring to fix. > > Interesting... In that case functional index should help (as it > also makes statistic for the planner): > > create index concurrently on task_task_execution_state_idx async.task using btree ((async.task_execution_state(task))); > > analyze async.task; > > > This can't help performance, as the index... > CREATE INDEX ON async.task(concurrency_pool, priority, entered) > WHERE async.task_execution_state(task) = 'READY'; > > ...is very precisely configured to provide exactly what's needed; I > need tasks for that exact pool in that exact order if and only if > ready.  The partial predicate is designed to keep the index nice and > small since only a small portion of tasks would be eligible at any > specific time. The index I suggested was not intended to be used by such queries, it's only a way to provide statistics for the planner as `create statistics` on expression is not working here. It might not be enough and require additional columns (in that case it will be replacement for your index) or perhaps elevated statistics target. It could even make things worse but I'm sure you have ways to test that before putting it on an important database. It should help if planner underestimate number of rows but even then total estimation won't be perfect when we have 2 conditions on state that obviously statistically dependent. Combining both conditions on application side would work much better if that is possible... What would also might help there - output of `explain (analyze, buffers)` of a query that really had a bad plan and executes in seconds with sizes of indexes. And same output but with `set enable_sort = off` to see plan that supposed to be better. And just in case number of live/dead tuples in that table from pg_stat_user_tables. -- Alexey Ermakov --------------d1P55AfiRpc8Xj38FPurpBdN Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit
On 2026-03-19 00:38, Merlin Moncure wrote:
On Tue, Mar 17, 2026 at 11:27 PM Alexey Ermakov <alexius.work@gmail.com> wrote:
On 2026-03-18 04:52, Merlin Moncure wrote:
Hello. I think planner doesn't have information about distribution of async.task_execution_state(task) unless it's part of any full index. I would try to give that with extended statistics (postgresql 14+):
create statistics (mcv) task_task_execution_state_stat on ((async.task_execution_state(task))) from async.task;
analyze async.task;

If that won't help - please show distribution from pg_stats_ext view for extended statistic above.


This unfortunately fails, probably because the table type includes system columns (despite not using them).
 
orchestrator_service_user@orchestrator=> create statistics  task_stats (mcv) on (async.task_execution_state(task)) from async.task;
ERROR:  statistics creation on system columns is not supported

This would require some refactoring to fix.

Interesting... In that case functional index should help (as it also makes statistic for the planner):

create index concurrently on task_task_execution_state_idx async.task using btree ((async.task_execution_state(task)));
analyze async.task;

This can't help performance, as the index...
CREATE INDEX ON async.task(concurrency_pool, priority, entered)
WHERE async.task_execution_state(task) = 'READY';

...is very precisely configured to provide exactly what's needed; I need tasks for that exact pool in that exact order if and only if ready.  The partial predicate is designed to keep the index nice and small since only a small portion of tasks would be eligible at any specific time.

The index I suggested was not intended to be used by such queries, it's only a way to provide statistics for the planner as `create statistics` on expression is not working here.

It might not be enough and require additional columns (in that case it will be replacement for your index) or perhaps elevated statistics target. It could even make things worse but I'm sure you have ways to test that before putting it on an important database.

It should help if planner underestimate number of rows but even then total estimation won't be perfect when we have 2 conditions on state that obviously statistically dependent. Combining both conditions on application side would work much better if that is possible...

What would also might help there - output of `explain (analyze, buffers)` of a query that really had a bad plan and executes in seconds with sizes of indexes. And same output but with `set enable_sort = off` to see plan that supposed to be better. And just in case number of live/dead tuples in that table from pg_stat_user_tables.

--

Alexey Ermakov


--------------d1P55AfiRpc8Xj38FPurpBdN--