1

Topic: extern.php query

I've brought this up before, but I think I've finally tracked down all the last nagging problems. Basically, issues arise with a larger forum and the extern.php file. The RSS feeds hadn't been a huge issue because not all that many people use them, and they weren't getting hit constantly. However, when I went to place a listing of active topics on our front website, including extern.php would prevent the page from loading for 10-20 seconds, and it would eventually stall the entire server.

Here's a basic query for reference:

SELECT t.id, t.subject
    FROM topics AS t
        INNER JOIN forums AS f ON f.id=t.forum_id
        LEFT JOIN forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id=3)
    WHERE (fp.read_forum IS NULL OR fp.read_forum=1)
        AND t.moved_to IS NULL
        AND f.id IN(1,2,3,4,5)
    ORDER BY t.last_post DESC
    LIMIT 15

The first problem with this is that with punbb's default indexes, the query is using the index placed on the moved_to column. This makes sense, but when it comes to sorting the results by either the last_post or posted columns, things take a very long time.

My first instinct was to place a multi column index with both moved_to and last_post. However, the problem with this, is that if you sort by the posted column, that index won't work (or vice-versa). And simply creating another multi-column index won't work either, since it picks the first index it finds that contains moved_to as the first column.

After a bit of head-banging, I found the solution is to join the topics table onto itself, so no multi-column indexes are needed.

With that taken care of, and indexes placed on both the last_post  and posted columns, queries without a restriction on the forums perform quite nicely. However, as soon as you throw in a where clause on the forums, things break down once again. The solution is to change the forum's join from an inner to a left join.

So all in all, here are the changes I made to extern.php...

The $order_by variable becomes:

$order_by = ($_GET['action'] == 'active') ? 't2.last_post' : 't2.posted';

The RSS query becomes:

$result = $db->query('SELECT t.id, t.poster, t.subject, t.posted, t.last_post, f.id AS fid, f.forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'topics AS t2 ON t.id = t2.id LEFT JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id=3) WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.moved_to IS NULL'.$forum_sql.' ORDER BY '.$order_by.' DESC LIMIT 15') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());

The HTML query becomes:

$result = $db->query('SELECT t.id, t.subject FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'topics AS t2 ON t.id = t2.id LEFT JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id=3) WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.moved_to IS NULL'.$forum_sql.' ORDER BY '.$order_by.' DESC LIMIT '.$show) or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());

And then the following indexes added:

ALTER TABLE `topics` ADD INDEX `topics_posted_idx` (`posted`)
ALTER TABLE `topics` ADD INDEX `topics_last_post_idx` (`last_post`)

Yeah, I know, two additional indexes, boo.. but it seems well worth it. Queries now only take a fraction of a second, instead of 20 seconds. Smaller forums may not encounter this problem now, but it's only a matter of time.

Hard work may not kill you, but why take chances?

Re: extern.php query

As always, thanks for the thorough debugging! I will definately look into this myself. If I come to the same conclusion, I can't see any reason why this shouldn't be in 1.3.

"Programming is like sex: one mistake and you have to support it for the rest of your life."

3 (edited by khalido 2005-06-24 13:10)

Re: extern.php query

Yes, i am using extern.php, and notice a small delay when loading a page which calls extern.php. It would be nice if it could be sped up.