Optimizer picking a poor plan for Serializable Transaction Isolation - Mailing list pgsql-bugs

From Tyler Rockwood
Subject Optimizer picking a poor plan for Serializable Transaction Isolation
Date
Msg-id CAO5aQPU-Ngp3VqA=tiQ8t5jM1HOfGh8SHvKZBLG=SOQq=Y-a1g@mail.gmail.com
Whole thread Raw
Responses Re: Optimizer picking a poor plan for Serializable Transaction Isolation  (Tomas Vondra <tomas.vondra@enterprisedb.com>)
List pgsql-bugs
We introduced our first query hint into our code today, and based on this wiki page 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.

pgsql-bugs by date:

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