Jorge Niedbalski R : Another boring unix programmer.

Jorge Niedbalski R.

Patrones : Cache Manager (PHP5)

leave a comment »

Cuando :

Es un elemento crucial para cualquier aplicación que obtenga informacion de un centro de computo que tome tiempo en procesar una solicitud , lo anterior debido a que que se deben inicializar estructuras para mantener las copias de datos/buffers/ , generalmente inicializar un descriptor , sobre eso realizar la lógica del protocolo , solicitar el recurso, inicializar la transferencia y finalmente copiar en las estructuras previamente allocadas.

La solución mas simple es realizar una sola vez este proceso durante un periodo T , manteniendo esta información en algun backend accesible por el proceso sin necesidad de realizar nuevamente el costoso proceso descrito anteriormente de forma transparente para la aplicación.

Como :

Si los datos solicitados se encuentran en el cache ( hit ) , la solicitud será atendida leyendolo desde el caché, lo que acelerará el tiempo de respuesta. En el caso de que el dato solicitado no se encuentre, será solicitado a la fuente de datos en su ubicación original ( independiente del protocolo ) y almacenada en el caché intermedio. Del mismo modo, la siguiente solicitud para el mismo recurso será atendida desde el caché previamente almacenado.

Cache Manager Design Pattern

Código:

La implementación utiliza adicionalmente un patrón Factory para obtener una instancia dependiendo del tipo de backend seleccionado , tambien utiliza un patrón singleton en vista de asegurar la existencia de una sola instancia en un ciclo de ejecucción.

<?php

/*
* Partial implementation of a Cache Manager Pattern for general use.
*
* @author Jorge Niedbalski R.
* @(#)Data::Cache::CacheManager
*
*/

class   CacheObjectException extends Exception {}    
class   CacheObject {

    private    $is_url;
    private $keyname;
    private $lifetime;
    private $value;
    private $backend;

    public  function    __construct($backend = NULL, $keyname, $value, $lifetime, $is_url = FALSE) 
    {
                $this->backend  = $backend;
                $this->keyname  = $keyname;
                $this->value    = $value;
                $this->lifetime = $lifetime;
                $this->is_url   = TRUE;
        }

        public  function    __destruct()  {}

        public  function getKeyName() 
        {
            return ( $this->keyname );
        }   
    
        public  function    getLifeTime()
        {
            return ( $this->lifetime );
        }

        public  function    getValue()
        {
            if($this->is_url) { 
                $this->value = file_get_contents($this->keyname);
                if($http_response_header[0] != 'HTTP/1.1 200 OK') {
                    throw new CacheObjectException(sprintf("Invalid response from resource %s", $this->keyname));
                }
            }
            return ( $this->value );
        }

        public  function    getBackend()
        {
            return ( $this->backend );
        }
    
        public    function    isURL()
        {
            return    ( $this->is_url );
        }

        public  function    setBackend($backend)
        {
            $this->backend = $backend;
        }

        public  function    setKeyName($name, $url = FALSE)
        {
            $this->keyname = $name;
        }

        public    function    setIsURL($is_url = FALSE) 
        {
            $this->is_url = $is_url;
        }

        public  function    setLifeTime($lifetime)
        {
            $this->lifetime = $lifetime;
        }

        public  function    setValue($value)
        {
            $this->value    = $value;
        }

        public  function    fetch()
        {
            try {
                return ( CacheManager::fetchObject($this) ); 
            } catch(CacheManagerException $e) {
                throw new CacheObjectException($e->getMessage()); 
            }
        }
    }
 
    class   CacheBackendFactoryException extends Exception {}
    class   CacheBackendFactory
    {
       protected static $_instance; //single instance for each backend on the same execution time

       public   static  function    getInstance($backend)
       {
            if(self::$_instance == NULL) {
                $backend_class = sprintf("%sBackend", $backend);
                $backend_file  = sprintf("%s/Backends/%s.php", dirname(__FILE__), $backend_class);

                if(!file_exists($backend_file)) {
                    throw new CacheBackendFactoryException(sprintf("File %s not found on available backend classes\n", $backend_file));
                }
        
                require_once($backend_file);
                self::$_instance = new $backend_class();
            }

            return ( self::$_instance );
        } 
    }
    
    class   CacheException extends Exception {}
    class   Cache {
        
         public static  function    fetchObject(CacheObject $object)
         {
            try {
                    return ( CacheBackendFactory::getInstance($object->getBackend())->fetchObject($object) );
            } catch(CacheBackendFactoryException $e) {
                throw new CacheException($e->getMessage());                
            } catch(CacheBackendException $e) {
                throw new CacheException($e->getMessage());
            }
         }
    
         public static  function    addObject(CacheObject $object)
         {
            try  {
                    return ( CacheBackendFactory::getInstance($object->getBackend())->saveObject($object) );
            } catch(CacheBackendFactoryException $e) {
                throw new CacheException($e->getMessage());
            } catch(CacheBackendException $e) {
                throw new CacheException($e->getMessage());
            }
         }
    }
    
    class   CacheManagerException extends Exception {}
    class   CacheManager {
    
        public static   function    fetchObject(CacheObject $object) 
        {   
            try {   
                return ( Cache::fetchObject($object) );
            } catch(CacheException $e) {
                throw new CacheManagerException($e->getMessage());
            } catch(ObjectKeyNotFoundException $e) {
                try {
                    return ( Cache::addObject($object) );
                } catch(ObjectCreaterException $e) {
                    throw new CacheManagerException($e->getMessage());    
                } catch(CacheException $e) {
                    throw new CacheManagerExeption($e->getMessage());
                }
            }
        }
    }

    interface   Interface_CacheBackend {
        public  function    fetchObject(CacheObject $object);
        public  function    saveObject(CacheObject $object);
    }
?>

Una implementación de Backend , ha sido realizada utilizando Memcached como centro de almacenamiento en un esquema llave-valor (debes tener instalada la extensión Pecl Memcache ) :


<?php

define(MEMCACHE_HOST, '127.0.0.1');
define(MEMCACHE_PORT, 11211);

class	ObjectKeyNotFoundException extends Exception {}
class	CacheBackendException	extends Exception {}

class	MemcacheBackend implements Interface_CacheBackend {

		private	$conn;

		public	function	__construct() 
		{
			$this->conn = new Memcache();
			$this->conn->connect(MEMCACHE_HOST, MEMCACHE_PORT);
		}

		public	function	fetchObject(CacheObject $object) 
		{
			if( ($data = $this->conn->get($object->getKeyName()) ) == FALSE ) {
				throw new ObjectKeyNotFoundException();
			} else {
				return ( $data );
			}
		}

		public	function	saveObject(CacheObject $object) 
		{
			if ( $this->conn->set($object->getKeyName(), $object->getValue(), 0, $object->getLifeTime()) ) {				
				return ( $object->getValue() );
			}
			
			throw new CacheBackendException("Cannot save cache key %s", $object->getKeyName());
		}
	
}

?>

Otros

Es un patrón de uso cotidiano para cualquier programador que se interese en el rendimiento de su aplicación. Es importante que sea usado con un backend que asegure un buen tiempo de respuesta.

Referencias

Caché
Pecl Memcache

Advertisement

Written by niedbalski

24/05/2010 at 2:10 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.