We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 24719
    • 194 Posts
    hi,

    i’m considering running memcache daemon on a site, so i’m just trying to get my head around caching objects. i’ve got a few questions:


    • do i have to manually add things to the cache with xpdo->toCache() and xpdo->fromCache(), or do i only need to specify cache time-to-live values as part of xpdo->getObject()?
    • do i need to initialise the cache manager first? From the docs i can see that xpdo->$_cacheEnabled is false by default - how do i enable caching? do i just set $xpdo->$_cacheEnabled = true?
    • i can see the xpdo->getCacheManager method, but how could i override it with my own that inherits from xpdoCacheManager? to enable use of memcache do i just have to set $xpdo->config[’cache_db_handler’] to Memcache?

    are there any examples of this in the 0.9.7 code or anywhere else i can have a look at?

    cheers
      • 28215
      • 4,149 Posts
      Quote from: redman at Apr 10, 2008, 04:16 AM

      hi,

      i’m considering running memcache daemon on a site, so i’m just trying to get my head around caching objects. i’ve got a few questions:


      • do i have to manually add things to the cache with xpdo->toCache() and xpdo->fromCache(), or do i only need to specify cache time-to-live values as part of xpdo->getObject()?
      • do i need to initialise the cache manager first? From the docs i can see that xpdo->$_cacheEnabled is false by default - how do i enable caching? do i just set $xpdo->$_cacheEnabled = true?
      • i can see the xpdo->getCacheManager method, but how could i override it with my own that inherits from xpdoCacheManager? to enable use of memcache do i just have to set $xpdo->config[’cache_db_handler’] to Memcache?

      are there any examples of this in the 0.9.7 code or anywhere else i can have a look at?

      cheers

      To turn on DB caching, in your xPDO constructor, make sure the $config param (the 4th one) has this key:

      XPDO_OPT_CACHE_DB => true


      As for using a custom CacheManager, it’s pretty easy. Just create a class for your framework that extends the xPDO class, and then override the getCacheManager function. For example, I have one in my framework that goes like this:

      class LTC extends xPDO {
          function __construct() {    
              
              $this->_loadConfig();
              $this->config = array_merge(array(
                  XPDO_OPT_CACHE_DB => $this->config['db_cache'],
                  XPDO_OPT_CACHE_PATH => $this->config['lib_path'].'cache/',
                  XPDO_OPT_HYDRATE_FIELDS => true,
                  XPDO_OPT_HYDRATE_ADHOC_FIELDS => true,
                  XPDO_OPT_HYDRATE_RELATED_OBJECTS => true,
                  XPDO_OPT_TABLE_PREFIX => '',
                  XPDO_OPT_VALIDATOR_CLASS => 'validation.iltcvalidator',
                  XPDO_OPT_VALIDATE_ON_SAVE => true,
              ),$this->config);
      
             $host = $this->config['db_type'] .
                      ':host='.$this->config['db_host'] .
                      ';dbname='.$this->config['db_name'];
      	parent::__construct(
      		$host,
      		$this->config['db_user'],
      		$this->config['db_pass'],
      		$this->config,
      		array(
      			PDO_ATTR_ERRMODE => PDO_ERRMODE_SILENT,
      			PDO_ATTR_PERSISTENT => false,
      			PDO_MYSQL_ATTR_USE_BUFFERED_QUERY => true
      		));
      
      	$this->setPackage('ltc',$this->config['lib_path'].'/model/');
          }
      
         ...
      
          function getCacheManager$class = 'cache.xPDOCacheManager') {
              if (isset($this->config['cache_disabled']) && $this->config['cache_disabled']) {
                  $this->_cacheEnabled = false;
                  return null;
              }
              if ($this->cacheManager === null) {
                  if ($this->loadClass($class, XPDO_CORE_PATH, true, true)) {
                      // can override the cache manager, if you want
                      $cacheManagerClass = isset($this->config['CacheManager.class']) ? $this->config['CacheManager.class'] : 'cache.ltcCacheManager';
                      if ($className = $this->loadClass($cacheManagerClass, '', false, true)) {
                          if ($this->cacheManager = new $className($this)) {
                              $this->_cacheEnabled = true;
                          }
                      } else {
                          $this->_log(XPDO_LOG_LEVEL_FATAL,sprintf($ltc->lexicon['cachemanager_err_nf'],$class));
                      }
                  }
              }
              return $this->cacheManager;
          }
      }
      


      Then you can use this object anywhere, instead of $xpdo = new xPDO, use it like: $ltc = new LTC

      which then you can customize the object there to override/utilize xPDO methods to your framework.

      Hope that helps!


        shaun mccormick | bigcommerce mgr of software engineering, former modx co-architect | github | splittingred.com
        • 24719
        • 194 Posts
        thanks, i’ll give this a go.
          • 24719
          • 194 Posts
          thought i’d post a follow-up for anyone who wanted to do this too.

          here’s the really simple way of implementing memcache daemon with xpdo:

          1) install memcached and the memcache pecl package.
          2) start memcached with a command like:
          memcached -d -m 1024 -l 10.0.0.1 -p 11211
          note: change 10.0.0.1 to the IP address of the server you’ve started memcached on, and 11211 is the port.

          3) extend the xPDO object. in the constructor of your custom class, initialise and connect to memcached as follows:
          class MyXPDO extends xPDO
          {
              function __construct($dsn, $username= '', $password= '', $tablePrefix= '', $driverOptions= null) 
          	{
          		parent::__construct($dsn, $username, $password, $tablePrefix, $driverOptions);
          
          		// set the name of the caching system to use to Memcache.
          		$this->config = array_merge(array('cache_db_handler' => 'Memcache'), $this->config);
          
          		// connect to the cache manager.
          		if ($cacheManager = $this->getCacheManager())
          			$cacheManager->objcache->connect('10.0.0.1', 11211);
          	}
          }
          


          Now, when you want to cache an object, just specify a time-to-live parameter, e.g.
            // retrieve user and cache their object for half an hour
            $user = $xpdo->get('User', array('id' => $modx->getUserLoginID()), 1800);
          


          xpdo will handle the details of caching the object with memcached as long as a time-to-live is given, and it will automatically retrieve objects from the cache too if objects are still fresh.

          hope this helps someone...
            • 27376
            • 576 Posts
            Wow, this is absolutely awesome. I had no idea that the xPDOFileCache methods were named after the Memcache class methods. Every day I work with xPDO I am amazed!
              • 22303 MODX Staff
              • 10,725 Posts
              As an aside, for more recent versions of xPDO from SVN, the constructor has changed slightly, though it still works if you just pass a string table_prefix. Here is an alternative constructor for an extended xPDO class if you are passing an options array:
              <?php
              class MyXPDO extends xPDO {
                  function __construct($dsn, $username= '', $password= '', $options= array(), $driverOptions= null) {
                      // set the name of the caching system to use to Memcache.
                      $options[XPDO_OPT_DB_CACHE_HANDLER]= 'Memcache';
              
                      parent::__construct($dsn, $username, $password, $options, $driverOptions);
              
                      // connect to the cache manager.
                      if ($this->getCacheManager()) {
                          $this->cacheManager->objcache->connect('10.0.0.1', 11211);
              	}
              }
              ?>

              I also modified it so that in PHP 4, the objcache->connect() will be called on the reference to the cache manager, rather than a copy (not an issue in PHP 5).
                • 24719
                • 194 Posts
                here’s a follow-up to my follow-up:

                you aren’t restricted to only caching objects. if you have say, a blog, and want to cache headlines to display in a most recent entries box, you could cache the entire html output, so your function could be as follows:

                // snippet to return latest articles
                include_once(/path/to/MyXPDO.class.php)
                $xpdo = $MyXPDO();
                
                echo getRecentArticles($xpdo, 5);
                
                // params are an xPDO object, and the number of recent articles to return
                function getRecentArticles($xpdo, $num)
                {
                	// we can't cache this using xPDO directly since we aren't retrieving an object. So we'll do it ourselves.
                	// first, check whether there is data in the cache
                	if($cacheManager = $xpdo->getCacheManager())
                	{
                		// creating a key like this means a unique key will be created depending on the parameters the function was called with.
                		$key = md5('blogs::getRecentArticles: num: '.$num);
                		
                		// if the html isn't already in the cache, set a flag so we can cache the result after generating it.
                		if ($html = $cacheManager->get($key))
                		{
                			return $html;
                		}
                		else
                		{
                			$inCache = false;
                		}
                	}
                
                	// not in the cache, so perform query, then cache the final output
                	$sql = 'SELECT ...';
                	// process and format output and put it in $html
                	$html = ...
                
                	if (is_object($cacheManager) && !$inCache)
                		$cacheManager->set($key, $html, 1800);
                
                	return $html;
                }
                


                Doing your own caching like this means that your site should fly since the complex data is already cached.

                Extensions of this would be to delete the entry in the cache when saving a new blog post to keep the site dynamic and up-to-date while still retaining the benefits of caching.
                  • 15083
                  • 697 Posts
                  Does there exist a step by step guide to enabling memcache as a caching solution in Revolution?
                  I see the Cache Handler Class in system setting, this needs to change?

                    • 36912
                    • 12 Posts
                    Quote from: jusmeig at Oct 05, 2011, 03:56 AM
                    Does there exist a step by step guide to enabling memcache as a caching solution in Revolution?
                    I see the Cache Handler Class in system setting, this needs to change?

                    I'm interested in an answer to this question... since no one seemed to read mine here: https://forums.modx.com/thread/73459/memcached-and-persistent-db-connect-howto#dis-post-408155
                      • 15083
                      • 697 Posts
                      I have still not got anything approaching a guide to doing this yet.
                      Random forum posts are no substitute for a screen cast or a tutorial.

                      To the best of my knowledge, if you set:
                      cache_handler = cache.xpdomemcache
                      memcache_server = localhost:11211

                      This will write all MODx partitions into memcache.
                      The example given in that post is to write just one partition (resources) to memcache.