Optimizer picking a poor plan for Serializable Transaction Isolation

From: Tyler Rockwood <tyler(at)shortwave(dot)com>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: Optimizer picking a poor plan for Serializable Transaction Isolation
Date: 2022-02-17 18:55:14
Message-ID: CAO5aQPU-Ngp3VqA=tiQ8t5jM1HOfGh8SHvKZBLG=SOQq=Y-a1g@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

We introduced our first query hint into our code today, and based on this
wiki page <https://wiki.postgresql.org/wiki/OptimizerHintsDiscussion> I
figured I report it as a bug.

Our table looks something like:

CREATE TABLE IF NOT EXISTS threads (
account_id VARCHAR(64),
group_id BIGINT,
thread_id VARCHAR(64),
"timestamp" BIGINT NOT NULL,
has_unread BOOLEAN NULL,
CONSTRAINT pk_threads PRIMARY KEY (account_id, group_id, thread_id)
);
CREATE UNIQUE INDEX threads_by_owner_group_time_id ON threads
(account_id, group_id, "timestamp", thread_id);
CREATE UNIQUE INDEX threads_by_owner_group_unread_time_id ON threads (
account_id,
group_id,
has_unread,
"timestamp",
thread_id
);

We have a query that is a lookup - essentially this:

SELECT * FROM threads WHERE account_id = ? AND group_id = ? AND thread_id = ?;

Usually everything is fine and this does a query on the primary key index -
but sometimes postgres decides to pick the
threads_by_owner_group_unread_time_id index.

Picking the index is an issue because it ends up scanning essentially the
whole index range for a given account_id and group_id. Due to the way
upgrading predicate locks works - this almost always ends up locking the
entire table if that group has enough threads. This causes a ton of
contention in our database as this query happens for every incoming message
(we have to book keep the has_unread column properly) and the predicate
lock on the whole table interferes with every other active account's
processing (we processing all events for a single account sequentially).

I *think* this index is being picked over the primary key because it's
queried frequently and is likely to be cached or something. Anyways at any
other isolation level this would be fine, but at serializability it ends up
hurting throughput because we now retry a ton transactions unnecessarily.

I think there are two possible improvements here:

1. Improving predicate locks to be smarter about handing singular
lookups instead of treating this as a range scan. This has been discussed
before here:
https://www.postgresql.org/message-id/20110503064807.GB85173%40csail.mit.edu
2. Pick the primary key in this case (which is what our hint does).

Happy to answer specific questions about this.

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Daniel Shelepanov 2022-02-17 21:09:48 pg_visibility's pg_check_visible() yields false positive when working in parallel with autovacuum
Previous Message Japin Li 2022-02-17 17:30:46 Re: BUG #17409: Unable to alter data type of clustered column which is referenced by foreign key