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 1w2vmi-000jwm-1v for pgsql-performance@arkaria.postgresql.org; Wed, 18 Mar 2026 18:38:24 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w2vmg-00DKwI-05 for pgsql-performance@arkaria.postgresql.org; Wed, 18 Mar 2026 18:38:22 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w2vmf-00DKwA-1u for pgsql-performance@lists.postgresql.org; Wed, 18 Mar 2026 18:38:21 +0000 Received: from mail-qv1-xf2a.google.com ([2607:f8b0:4864:20::f2a]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w2vmc-00000000PFj-0X5V for pgsql-performance@lists.postgresql.org; Wed, 18 Mar 2026 18:38:20 +0000 Received: by mail-qv1-xf2a.google.com with SMTP id 6a1803df08f44-89c5446f3caso3142856d6.2 for ; Wed, 18 Mar 2026 11:38:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773859099; cv=none; d=google.com; s=arc-20240605; b=jJjYEPZCguCb/dlyU8kq4e8+e9qvLJX+QRFzizF6oQMtA4gIp4uuAyjyYf+dWKp4OH zi309Oeemmqiw7Jf9UAOz9G3+WKILdVytcUafLEM/2OAefeucecKfzUEnY5+Jp8hn0nZ xbJJp2jizv+01Gu2xkkim9/0oSSIDJyJShQbOwH4FDrfPv/SEOA+VRmVHO7zCh5x1q3z x20pqOQ2cveTt9O2rgOExf8LV4QtqY2hs47t1rfNf8WmvLz2kqftHV6O9KZaxTh9VqPp Og0LomDyhdQkJsGz3/EOjGUG+nvY52gLO5xgPWwuK30HsFS5ghC0EJjO6ohfqtC1TBQH 65Hg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=2YF4aO5NUibczQjeAFOigQMRFnLz5JLnRS6ys1lGclo=; fh=YogXftLEJieqKmXRXHc4wqeI6WFh+TMd6EMRBsLK5ww=; b=ZLS0bgUKwuwpcN1XSkYveyFv2PPWHy0AZgbSHiDeSK7p58L31iayUQfj1vI1OOkGtw 5Rg/xVCrpB8aAcZrBAquU7xeUqFWc+UVokC6aJvOc5vfJ2Jx73FrihWkXaJ1bT8Kg+ZC McXlFx7TFjnJjNCM9uamfYgnMF9UoeKRYSF4ZPA37QEggSzWMUQyTeDhkBp0h3ljKPaj joNiSQerKBA+11g/e1Kl/zlyJNFvu108ZL3JqnOeiXURPBkpoqOA+TwrTbB+ssDfCS9S uq7C4XUQY1ecIRWSWc0Q6hRKsia5dWMuZcB0b7F+UJWjkhaqTgqCxq632db/4rEVBGTV 1lWw==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773859099; x=1774463899; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=2YF4aO5NUibczQjeAFOigQMRFnLz5JLnRS6ys1lGclo=; b=HPUCXnLXZL0koSfeuYZLqjzPV2GDZO5aeRuQjTPjvVVYOr8uEca/zeOCq27Ue9+kKz xOx2CUZQ21ah6bYR6k582ITtKcQu5y4NpFt6euxUrO+JivO/fdSKeypiECXa9fMuiIg2 SKFYu/b4Yy8XjL731a/GqTvjAGKeWBdpB8qXs3riv9TkDy6tZO9mIwOKRGYHtX1HH8ES eHHRMxVYLbJBj6QHNYe0IUMpAr/Ea5Vz9imLeFZzRM/SniWaWeTbCBmpfWoOlisYdjR1 3cAhZ/IVzOV6mB/VKcih8h54v+1dEIuw+pUxBX9vSGzwG787mpWG1RDQN/LLQnVt/YJ7 vPpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773859099; x=1774463899; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=2YF4aO5NUibczQjeAFOigQMRFnLz5JLnRS6ys1lGclo=; b=To9fxWNr8eQaRorL+RE2kx8uwF9+JwZgzF8PcKjOA5/Ex8+kt+RtKN9JcS7uiLMEkR G01vvzPS0YDSjEppRxvTJXUi8BrBtaQrecDZUQQZUA5+kj6U1CovtWP3UZG41ca+Cvrg DtUzx/7C5MneeTlKyPLm/TCjGZAd5y/jZKtA2o6UOU5f80GYB05R8Q8aFQaVmsQu8/jz TX+Te2dwcsdHC+AE1y+yCshL6PTi50la1ti1Jb2hwh2tSqFLPmXvwK9RXQO9zTIDxou4 0ELelRHLx0VGXGLVxo6DQEPgBCdA+DG+nX3v2kaHTGUsROGIh+qXxGRUidXQrGLAtYGg 64HQ== X-Forwarded-Encrypted: i=1; AJvYcCUjmh69Pey6hDPPuA4IZqZwKwX7Wyd+B55zTRH7F8BEuEXGsEjj29sX/gbXzMiZ1FargUj9bZ/SFrUrQLHdeulPMg==@lists.postgresql.org X-Gm-Message-State: AOJu0YzH9aX3IYsMtjp8tPSosSUw8rrwl3wrrKDS3CTvaJ56MlA56ocU Kk2bqZ5ooxWIryVfh9MHzGOMmpbwN0leEeYiOliRvd2Tlm+QyFIWizn4pXDQton3aFVeDD5SYYg 6q1O8Mpt7K9Hc4yYhvOLea9cc98bleZA/ifwD X-Gm-Gg: ATEYQzz/sZRwEAkSYvv7mGkX8EcwCI3QQ65lym/BLzYkcK/orY4t0iub6eY/w76Ftsu CgBVXIb0t2Y60RqtAS36jW+MjAUNs506RYTR1w3A5MsbYuMEJ7UuLNIomSWmeic1h2m7MIsITAP U3eX9tZO1LPPlSHftBnSzFPqGZqofo2L4yw9JIIf65eYS3QIINQoV5ILEddE4lymsK8iiRtulUq fRjBQNn2J5C5o3UbEdCTP9ptHzcvuBsr6MdtfCzz/lftK5JCI9V5b0Q0pokQSw2a12lINeWhYJq O4Rq2M4g6UbcKmHvbGTu4w7fC/3Ksh9bG6Juk05WCaS7irr/+0TEgXzut8PCKL3PWeAQup6ETg= = X-Received: by 2002:a05:6214:4e02:b0:896:fbdd:ef14 with SMTP id 6a1803df08f44-89c6b484259mr57664316d6.12.1773859098586; Wed, 18 Mar 2026 11:38:18 -0700 (PDT) MIME-Version: 1.0 References: <574349.1773786268@sss.pgh.pa.us> <381e7d47-8021-458d-9a64-0cdb80b004f0@gmail.com> In-Reply-To: <381e7d47-8021-458d-9a64-0cdb80b004f0@gmail.com> From: Merlin Moncure Date: Wed, 18 Mar 2026 12:38:07 -0600 X-Gm-Features: AaiRm52A7Uc1pMLVvqZgdb6AW1z2PU1H_XXAuJ5iu6R1QY82KxoHxpEYLj-Y30U Message-ID: Subject: Re: postgres chooses objectively wrong index To: Alexey Ermakov Cc: Tom Lane , pgsql-performance@lists.postgresql.org Content-Type: multipart/alternative; boundary="00000000000073f063064d50c11e" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000073f063064d50c11e Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Tue, Mar 17, 2026 at 11:27=E2=80=AFPM 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_e= xecution_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=3D> 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 make= s > statistic for the planner): > > create index concurrently on task_task_execution_state_idx async.task usi= ng 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) =3D '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. @Tom Lane I'm pretty sure you were following me, but my abstraction earlier was a bit off; Simplified, the query is: > SELECT * FROM foo WHERE a=3D? AND b=3DK ORDER BY c, d LIMIT N; > CREATE INDEX ON foo(a,b,c) WHERE b=3DK; Should have been: SELECT * FROM foo WHERE a=3D? AND d=3DK ORDER BY b, c LIMIT N; CREATE INDEX ON foo(a,b,c) WHERE d=3DK; Point being, the index match in on (=3D, order, order). If a contains any less than 100% of the total records, and N is small relative to table size, this ought to be the ideal index for just about any case, the exact match on partial qual is just gravy. I think the planner is not giving enough bonus for an exact match versus an inexact match on partial index mathcing, (A=3DA should be better than A IN(A,B,C)), and it's unclear why the planner things bitmap heap + sort is outperforming a raw read off the index base on marginal estimated row counts. Lowering random_page_cost definitely biases the plan I like, but it skews both estimates. @Alexey Ermakov If you're interested in more context, see: pgasync pgflow graph example merlin --00000000000073f063064d50c11e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Tue, Mar 17, 2026 at = 11:27=E2=80=AFPM Alexey Ermakov <alexius.work@gmail.com> wrote:
=20 =20 =20
On 2026-03-18 04:52, Merlin Moncure wrote:
=20
Hello. I think planner doesn't have info= rmation about distribution of=C2=A0async.task_execution_state(task)=C2=A0unless 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 o=
n ((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).
=C2=A0
orchestrator_service_user@orchestrator=3D> create statistics =C2=A0task_stats (mcv) on (async.task_execution_state(task)) from async.task;
ERROR: =C2=A0statistics 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.t=
ask using btree ((async.task_execution_state(task)));
analyze async.task;

Th= is can't help performance, as the index...
CREATE INDEX ON as= ync.task(concurrency_pool, priority, entered)
WHERE async.task_execution= _state(task) =3D 'READY';

...is very preci= sely configured to provide exactly what's needed; I need tasks for that= exact pool in that exact order if and only if ready.=C2=A0 The partial pre= dicate is designed to keep the index nice and small since only a small port= ion of tasks would be eligible at any specific time.

@Tom Lane=C2=A0I&#= 39;m pretty sure you were following me, but my abstraction earlier was a bi= t off;

Simplified, the query is:
SELECT * FROM foo WHERE a=3D? AND b= =3DK ORDER BY c, d LIMIT N;
CREATE INDEX ON foo(a,b,c) WHERE b=3DK;

Should have been:
SELECT *= FROM foo WHERE a=3D? AND d=3DK ORDER BY b, c LIMIT N;
CREATE IND= EX ON foo(a,b,c) WHERE d=3DK;

Point being, t= he index match in on (=3D, order, order). If a contains any less than 100% = of the total records, and N is small relative to table size, this ought to = be the ideal index for just about any case, the exact match on partial qual= is just gravy.

I think the planner is not giving = enough bonus for an exact match versus an inexact match on partial index ma= thcing,=C2=A0 (A=3DA should be better than A IN(A,B,C)), and it's uncle= ar why the planner things bitmap heap=C2=A0+ sort is outperforming a raw re= ad off the index base on marginal estimated row counts.=C2=A0 Lowering rand= om_page_cost definitely biases the plan I like, but=C2=A0it skews both esti= mates.


If you're interested in mor= e context, see:


merlin

<= div>=C2=A0
--00000000000073f063064d50c11e--