ACM Modules

Discussion of general topics related to the new version and its place in the world. Don't discuss new features, report bugs, ask for support, et cetera. Don't use this to spam for other boards or attack those boards!
Forum rules
Discussion of general topics related to the new release and its place in the world. Don't discuss new features, report bugs, ask for support, et cetera. Don't use this to spam for other boards or attack those boards!
Post Reply
User avatar
DavidMJ
Registered User
Posts: 932
Joined: Thu Jun 16, 2005 1:14 am
Location: Great Neck, NY

Re: ACM Modules

Post by DavidMJ »

My bet is something is wonky with your memcache setup as the caches do not affect how queries get executed or which ones get executed. What can happen is that memcache is not given enough memory and must drop things out of the cache, causing phpBB to update the cache and thus generating more queries.
Freedom from fear

Martindale
Registered User
Posts: 9
Joined: Fri Nov 10, 2006 9:47 pm

Re: ACM Modules

Post by Martindale »

I'm running in to some serious performance issues, and I've done as much MySQL and Apache tuning as possible, and I'm using memcache.

We get ~500 posts, ~50 new registrations, and just about 50,000 pageviews per day. It looks like the largest portion of our time is spent on database queries, (5s out of 6s) and I was wondering what sort of recommendations you have for tuning and management of the SQL service?

Memcache barely uses 2% of the available memory (I've given memcache 512MB of memory, but it barely uses any of it.) while MySQL continues to grind away at memory and CPU (it idles at 20% of our CPU, and 16% of our RAM). What's going on here? Shouldn't memcache be taking most of the load?

Is there any way I can manually control how long a query is cached if I write a customization for my board? How about for posts? Shouldn't _all_ posts be cached UNTIL there is an edit?

Canadaka
Registered User
Posts: 4
Joined: Sat May 17, 2008 12:28 am

Re: ACM Modules

Post by Canadaka »

DavidMJ wrote:My bet is something is wonky with your memcache setup as the caches do not affect how queries get executed or which ones get executed. What can happen is that memcache is not given enough memory and must drop things out of the cache, causing phpBB to update the cache and thus generating more queries.
I gave memcache 1GB of memory and it used very little of it. I'm certain it was working, I did some tests. I also have a script by "Harun Yayli" that shows my stats on memcache usage and I could see the phpbb3 items there. http://livebookmark.net/journal/2008/05 ... ke-apcphp/

I did find this article comparing various caching methods, and there benchmarks shows that memcache is slower that file cache.
http://www.mysqlperformanceblog.com/200 ... omparison/
So i guess its only worth using memcache if you have a HIUGE site that is run over multiple servers and you need shared memory. I would run APC but it doesn't run stable for me, never has. There also isn't a binary for the newest PHP version floating around and none for the Non-Thread-Safe PHP.

On a seperate but related note, how can I increase the expire length of cached items? Some seems to be fairly short. On a typical viewtopic page i have 4 cached queries, if i wait as little as a few minutes and refresh its down to 2 cached queries. I know there is an optinal variable in the put() function to specify a TTL, but i never see it being used in the cache.php script.

User avatar
DavidMJ
Registered User
Posts: 932
Joined: Thu Jun 16, 2005 1:14 am
Location: Great Neck, NY

Re: ACM Modules

Post by DavidMJ »

I suggest experimenting with xcache, it seems to provide a pretty good variable cache.
Freedom from fear

Martindale
Registered User
Posts: 9
Joined: Fri Nov 10, 2006 9:47 pm

Re: ACM Modules

Post by Martindale »

Xcache is working amazing! I compiled the module and installed it, and so far we've seen an average of 400% improvement over memcache. However, it looks like memcache runs as a separate process, while xcache is a php extension. Would this let us write a custom ACM for using _both_ at the same time? It looks like when we start moving to clustered servers, memcache would be an optimal solution for load balancing and distributed database caching.

ToonArmy
Registered User
Posts: 335
Joined: Fri Mar 26, 2004 7:31 pm
Location: Bristol, UK
Contact:

Re: ACM Modules

Post by ToonArmy »

Martindale wrote:Xcache is working amazing! I compiled the module and installed it, and so far we've seen an average of 400% improvement over memcache. However, it looks like memcache runs as a separate process, while xcache is a php extension. Would this let us write a custom ACM for using _both_ at the same time? It looks like when we start moving to clustered servers, memcache would be an optimal solution for load balancing and distributed database caching.
No reason why not. Check xcache first if that fails, fall through to memcache.
Chris SmithBlogXMOOhlohArea51WikiNo support via PM/IM
Image

Martindale
Registered User
Posts: 9
Joined: Fri Nov 10, 2006 9:47 pm

Re: ACM Modules

Post by Martindale »

Well, if we look at the architecture we can identify what the approach should be as phpBB3 boards get larger. (PS, I also wrote a post about tuning large phpBB3 forums, but it isn't complete and only details a small portion of my experiences)

A database can only reside on one machine, correct? I don't know if this is the case or not, but it would appear that above and beyond a Beowulf cluster installation of MySQL, the best approach would be to have a cluster of memcache servers hosting memory for the MySQL master server.

If we use xcache as the first caching point for phpBB3, this will provide application-layer power and flexibility. However behind xcache would sit the memcache layer: all queries that would have directly hit the MySQL server after passing through xcache (writes, reads, updates) would instead be going through memcache.

How would such a module be coded? It looks like I can adjust these ACM modules to use conditional statements (if, elsE) and essentially merge the memcache and xcache files, but this doesn't seem to be an architecturally sound method of doing this.

User avatar
DavidMJ
Registered User
Posts: 932
Joined: Thu Jun 16, 2005 1:14 am
Location: Great Neck, NY

Re: ACM Modules

Post by DavidMJ »

The database should be able to be replicated...

What you want to achieve must be done at the ACM layer, how you factor beyond that is up to your own discretion. What you could do is make a new ACM module that depends on the memcache and xcache modules. This way, you can swap around modules at will...
Freedom from fear

pinkflozd
Registered User
Posts: 4
Joined: Thu Jun 14, 2007 7:26 pm
Location: Piran, Slovenia
Contact:

Re: ACM Modules

Post by pinkflozd »

Hello. First i want to say thanks for the acm modules i use them for a year now ... and the performance is great ... as i have a old home server :P
Lately i've been using xcache as i noticed it gives the best performance ...
But ... I host 2 sites on my server, with 2 phpbb3 forums, and only one can use the xcache acm module. I have also 2 wordpresses with such a extension and they work both... Is there maybe a trick to make them work both on the same server with a acm extension?

Thank you verry much

Luka

Martindale
Registered User
Posts: 9
Joined: Fri Nov 10, 2006 9:47 pm

Re: ACM Modules

Post by Martindale »

One of our developers sat down and merged the memcache and xcache ACM modules; but we haven't used this in any production environment, so use at your own risk:

Code: Select all

<?php
/** 
*
* @package acm
* @version $Id: acm_xcache_memcache.php,v 0.9 2009/05/12 00:00:00 Artellos Exp $
* @copyright (c) 2005 phpBB Group 
* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
*
*/

/**
* This file is a merge of the following caching systems:
* ACM Memcache Based Caching
* ACM XCache Based Caching
* @package acm
*/

class acm
{
        var $vars = array();
        var $is_modified = false;

        var $sql_rowset = array();
        var $sql_row_pointer = array();

        // taken from XCache
        var $cache_dir = '';
        
        // taken from Memcache
        var $memcache;

        /**
        * Set cache path
        */
        function acm()
        {
                global $phpbb_root_path;
                // taken from XCache
                $this->cache_dir = $phpbb_root_path . 'cache/';
                
                // taken from Memcache
                $this->memcache = memcache_connect('localhost', 11211);
        }

        /**
        * Load global cache
        */
        function load()
        {
                global $phpEx;

                // taken from XCache
                // grab the global cache
                if (xcache_isset('global')) {
                    // We were able to find XCache's global variable, use it.
                    
                    $this->vars = xcache_get('global');
                    return true;
                    
                } else {
                    
                    // We didn't load Xcache, so let's try memcache.
                    
                    // taken from Memcache
                    // grab the global cache
                    $temp = memcache_get($this->memcache, 'global');
                    
                    if ($temp !== false) {
                         $this->vars = $temp;
                         
                         return true;
                    }               

                }

                return false;
                
        }

        /**
        * Unload cache object
        */
        function unload()
        {
                $this->save();
                unset($this->vars);
                unset($this->sql_rowset);
                unset($this->sql_row_pointer);
        }

        /**
        * Save modified objects
        */
        function save() 
        {
                if (!$this->is_modified)
                {
                        return;
                }

                // taken from XCache
                xcache_set('global', $this->vars, 31536000);
                
                // taken from Memcache
                memcache_set($this->memcache, 'global', $this->vars, 0, 2592000);

                $this->is_modified = false;
        }

        /**
        * Tidy cache
        */
        function tidy()
        {
                global $phpEx;

                // cache has auto GC, no need to have any code here :)

                set_config('cache_last_gc', time(), true);
        }

        /**
        * Get saved cache object
        */
        function get($var_name)
        {
                if ($var_name[0] == '_')
                {
                        global $phpEx;

                        if (xcache_isset($var_name)) {
                            return xcache_get($var_name);
                        } elseif ($memcache_result = memcache_get($this->memcache, $var_name)) {
                            return $memcache_result;
                        }
                        
                        return false;
                }
                else
                {
                        if (!sizeof($this->vars))
                        {
                                $this->load();
                        }
                        return (isset($this->vars[$var_name])) ? $this->vars[$var_name] : false;
                }
        }

        /**
        * Put data into cache
        */
        function put($var_name, $var, $ttl = 31536000)
        {
                if ($var_name[0] == '_')
                {
                        xcache_set($var_name, $var, $ttl);
                        memcache_set($this->memcache, $var_name, $var, 0, $ttl);
                }
                else
                {
                        $this->vars[$var_name] = $var;
                        $this->is_modified = true;
                }
        }

        /**
        * Purge cache data
        */
        function purge()
        {
                // Purge all phpbb cache files
                $dir = @opendir($this->cache_dir);

                if (!$dir)
                {
                        return;
                }

                while (($entry = readdir($dir)) !== false)
                {
                        if (strpos($entry, 'sql_') !== 0 && strpos($entry, 'data_') !== 0 && strpos($entry, 'ctpl_') !== 0 && strpos($entry, 'tpl_') !== 0)
                        {
                                continue;
                        }

                        @unlink($this->cache_dir . $entry);
                }
                closedir($dir);

                $n = xcache_count(XC_TYPE_VAR);
                for ($i = 0; $i < $n; $i++)
                {
                        xcache_clear_cache(XC_TYPE_VAR, $i);
                }

                memcache_flush($this->memcache);

                unset($this->vars);
                unset($this->sql_rowset);
                unset($this->sql_row_pointer);

                $this->is_modified = false;
        }

        /**
        * Destroy cache data
        */
        function destroy($var_name, $table = '')
        {
                global $phpEx;

                if ($var_name == 'sql' && !empty($table))
                {
                        if (!is_array($table))
                        {
                                $table = array($table);
                        }

                        foreach ($table as $table_name)
                        {
                                // gives us the md5s that we want
                                if (!xcache_isset('sql_' . $table_name))
                                {
                                        continue;
                                }
                                $temp = xcache_get('sql_' . $table_name);
                                
                                $temp = memcache_get($this->memcache, 'sql_' . $table_name);
                                if ($temp === false)
                                {
                                        continue;
                                }

                                // delete each query ref
                                foreach ($temp as $md5_id => $void)
                                {
                                        xcache_unset('sql_' . $md5_id);
                                        memcache_delete($this->memcache, 'sql_' . $md5_id);
                                }

                                // delete the table ref
                                xcache_unset('sql_' . $table_name);
                                memcache_delete($this->memcache, 'sql_' . $table_name);
                        }

                        return;
                }

                if ($var_name[0] == '_')
                {
                        xcache_unset($var_name);
                        memcache_delete($this->memcache, $var_name);
                }
                else if (isset($this->vars[$var_name]))
                {
                        $this->is_modified = true;
                        unset($this->vars[$var_name]);

                        // We save here to let the following cache hits succeed
                        $this->save();
                }
        }

        /**
        * Load cached sql query
        */
        function sql_load($query)
        {
                global $phpEx;

                // Remove extra spaces and tabs
                $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
                $query_id = sizeof($this->sql_rowset);

                if (xcache_isset('sql_' . md5($query))) {

                    $this->sql_rowset[$query_id] = xcache_get('sql_' . md5($query));
                    $this->sql_row_pointer[$query_id] = 0;
                    return $query_id;

                } elseif (memcache_get($this->memcache, 'sql_' . md5($query))) {

                    $this->sql_rowset[$query_id] = $temp;
                    $this->sql_row_pointer[$query_id] = 0;
                    return $query_id;

                }
                return false;
        }

        /**
        * Save sql query
        */
        function sql_save($query, &$query_result, $ttl)
        {
                global $db, $phpEx;

                // Remove extra spaces and tabs
                $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);

                // determine which tables this query belongs to
                preg_match('/FROM \\(?(\\w+(?: \\w+)?(?:, ?\\w+(?: \\w+)?)*)\\)?/', $query, $regs);
                $tables = array_map('trim', explode(',', $regs[1]));

                foreach ($tables as $table_name)
                {
                        if (($pos = strpos($table_name, ' ')) !== false)
                        {
                                $table_name = substr($table_name, 0, $pos);
                        }

                        if (xcache_isset('sql_' . $table_name))
                        {
                                $temp = xcache_get('sql_' . $table_name);
                        }

                        else
                        {
                                $temp = memcache_get($this->memcache, 'sql_' . $table_name);
                                if ($temp === false)
                                {
                                        $temp = array();
                                }
                                else
                                {
                                        $temp = array();
                                }
                        }

                        $temp[md5($query)] = true;
                        xcache_set('sql_' . $table_name, $temp, $ttl);
                        memcache_set($this->memcache, 'sql_' . $table_name, $temp, 0, $ttl);
                }

                // store them in the right place
                $query_id = sizeof($this->sql_rowset);
                $this->sql_rowset[$query_id] = array();
                $this->sql_row_pointer[$query_id] = 0;

                while ($row = $db->sql_fetchrow($query_result))
                {
                        $this->sql_rowset[$query_id][] = $row;
                }
                $db->sql_freeresult($query_result);

                xcache_set('sql_' . md5($query), $this->sql_rowset[$query_id], $ttl);

                $query_result = $query_id;
        }

        /**
        * Ceck if a given sql query exist in cache
        */
        function sql_exists($query_id)
        {
                return isset($this->sql_rowset[$query_id]);
        }

        /**
        * Fetch row from cache (database)
        */
        function sql_fetchrow($query_id)
        {
                if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
                {
                        return $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++];
                }

                return false;
        }

        /**
        * Fetch a field from the current row of a cached database result (database)
        */
        function sql_fetchfield($query_id, $field)
        {
                if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
                {
                        return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field] : false;
                }

                return false;
        }

        /**
        * Seek a specific row in an a cached database result (database)
        */
        function sql_rowseek($rownum, $query_id)
        {
                if ($rownum >= sizeof($this->sql_rowset[$query_id]))
                {
                        return false;
                }

                $this->sql_row_pointer[$query_id] = $rownum;
                return true;
        }

        /**
        * Free memory used for a cached database result (database)
        */
        function sql_freeresult($query_id)
        {
                if (!isset($this->sql_rowset[$query_id]))
                {
                        return false;
                }

                unset($this->sql_rowset[$query_id]);
                unset($this->sql_row_pointer[$query_id]);

                return true;
        }
}

?>

Post Reply