Revision [6715]

This is an old revision of WikkaOptimization made by IanAndolina on 2005-03-15 10:00:28.

 

How to optimize Wikka?


Optimizing the Number of Database Queries

OK, I installed Wikka on a new host and observed quite slow page generation times, especially for pages with quite a number of wiki words and usernames (e.g. RecentChanges). I turned on sql_debugging and to my horror saw Wikka performing nearly 60 database queries when constructing the RecentChanges page! Looking at the code it was immediately obvious why; recentchanges.php performs a database query for every edit to see if the user is registered or not and a query to check if the page ACL gives permission to show a link. So if you have 50 recent changes you can assume at least 100 queries!!! The answer is to cache the list of users, ACL names (and page names too for good measure) the first time a query is performed, and then use the cached version from then on. So I've modified $wakka->ExistsPage, $wakka->LoadAllACLs and created $wakka->ExistsUser functions which cache results:
function ExistsPage($page)
    {
        if (!isset($this->tagCache))
        {
            $this->tagCache = array();
            $query = "SELECT DISTINCT tag FROM ".$this->config['table_prefix']."pages";
            if ($r = $this->Query($query))
            {
                while($row = mysql_fetch_row($r))
                {
                    $this->tagCache[]=strtolower($row[0]);
                }
                mysql_free_result($r);
            }
        }      
        return is_int(array_search(strtolower($page),$this->tagCache));
    }

    function ExistsUser($name) {
        if (!isset($this->userCache))
        {
            $this->userCache = array();
            $query = "SELECT DISTINCT name FROM ".$this->config['table_prefix']."users";
            if ($r = $this->Query($query))
            {
                while($row = mysql_fetch_row($r))
                {
                    $this->userCache[]=strtolower($row[0]);
                }
                mysql_free_result($r);
            }
        }
        return is_int(array_search(strtolower($name),$this->userCache));
    }

The new $wakka->LoadAllACLs loads just the page_tag values from the acls table (which only stores values different to the defaults). Only if the tag being asked for is one of those pages with modified ACLs will it load the ACL values; otherwise it uses the defalts and avoids a query. Before this change, it ALWAYS did a query on the database even if the page ACL wasn't there!
    function LoadAllACLs($tag, $useDefaults = 1)
    {
        if (!isset($this->aclnameCache))
        {
            $this->aclnameCache = array();
            $query = "SELECT page_tag FROM ".$this->config['table_prefix']."acls";
            if ($r = $this->Query($query))
            {
                while($row = mysql_fetch_row($r))
                {
                    $this->aclnameCache[]=strtolower($row[0]);
                }
                mysql_free_result($r);
            }
        }
        if (is_int(array_search(strtolower($tag),$this->aclnameCache)) && $usedefaults!==1)
        {
            $acl = $this->LoadSingle("SELECT * FROM ".$this->config["table_prefix"]."acls WHERE page_tag = '".mysql_real_escape_string($tag)."' LIMIT 1");
        }
        else
        {
            $acl = array("page_tag" => $tag, "read_acl" => $this->GetConfigValue("default_read_acl"), "write_acl" => $this->GetConfigValue("default_write_acl"), "comment_acl" => $this->GetConfigValue("default_comment_acl"));
        }
        return $acl;
    }

Normally, $wakka->link uses $wakka->LoadPage to check if a page is an existing wiki page or not. LoadPage does kind of have a cache, but the whole page is cached, which with a lot of big pages will take up much more memory etc. So now we have a much more light-weight and speedy ExistsPage and ExistsUser lets modify $wakka->Link and actions/recentchanges.php and see what we can improve.

$wakka->Link — we just change $this->LoadPage to $this->ExistsPage and $linkedPage['tag'] to $tag
    else
    {
        // it's a wiki link
        if ($_SESSION["linktracking"] && $track) $this->TrackLinkTo($tag);
        $linkedPage = $this->ExistsPage($tag);
        // return ($linkedPage ? "<a href=\"".$this->Href($method, $linkedPage['tag'])."\">".$text."</a>" : "<span class=\"missingpage\">".$text."</span><a href=\"".$this->Href("edit", $tag)."\" title=\"Create this page\">?</a>");
        return ($linkedPage>=0 ? "<a href=\"".$this->Href($method, $tag)."\" title=\"$title\">".$text."</a>" : "<a class=\"missingpage\" href=\"".$this->Href("edit", $tag)."\" title=\"Create this page\">".$text."</a>");
    }

And actions/recentchanges.php to use our new ExistsUser:
            $timeformatted = date("H:i T", strtotime($page["time"]));
            $page_edited_by = $page["user"];
            if (!$this->ExistsUser($page_edited_by)) $page_edited_by .= " (unregistered user)";


Benchmarks:
OK, I have two sites which use the same database and one is a stock wikka install and one is my modified wikka (you can see the database queries at the bottom of the page):

http://nontroppo.dreamhosters.com/wikka2/RecentChanges - the standard wikka
http://nontroppo.dreamhosters.com/wikka/RecentChanges - the optimized wikka

On the recent changes page, the optimizations have reduced the database queries from 61 to just 6!:

Standard: 61 queries take an average (5 reloads) of 0.8769seconds
Optimized: 6 queries take an average (5 reloads) of 0.2481seconds >70% faster

On the PageIndex, the changes have reduced the database queries from 29 to just 6!:

Standard: 29 queries take an average (5 reloads) of 0.3907seconds
Optimized: 5 queries take an average (5 reloads) of 0.1628seconds >50% faster

IanAndolina

There is one comment on this page. [Display comment]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki