Problem/Motivation

PrivateMessageThreadAccessControlHandler::checkAccess() answers view by calling filterUserDeletedMessages($account) and checking count(). That loads every message entity in the thread to find out whether at least one is newer than the member's last delete. Every thread page and every poll for new messages pays for it, and the cost grows with the thread. On our site the check took 54 ms on a 164-message thread.

Proposed resolution

Add PrivateMessageThreadInterface::hasUserVisibleMessages(AccountInterface $account): one entity query over the thread's message ids for anything created after the account's last delete, limited to one row. It keeps the rule from getMessages() that messages from users the current user banned do not count, except the thread's first message. The access handler uses it in place of loading the messages.

On the same thread the check went from 54 ms to 6 ms. It gave the same answer as count(filterUserDeletedMessages()) for all 235 member and thread pairs on our data.

I have a patch with a kernel test, ThreadViewAccessTest, which checks that view access loads no message entity and that the answers still follow deletes and bans. I will open an MR.

API changes

PrivateMessageThreadInterface::hasUserVisibleMessages() is new.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

loze created an issue. See original summary.

loze’s picture

Status: Active » Needs review

!223 is up with the fix and ThreadViewAccessTest. The test checks that view access loads no message entity, and that the answer still follows deletes and bans, including the rule that the thread's first message always counts.