View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template}

{zone-data:component-name}
Zend_Cache_Backend_Database
{zone-data}

{zone-data:proposer-list}
[Pádraic Brady|mailto:padraic.brady@yahoo.com]
{zone-data}

{zone-data:liaison}
TBD
{zone-data}

{zone-data:revision}
0.95 - 25 January 2009
{zone-data}

{zone-data:overview}
The purpose of this proposal is to offer a database backed caching mechanism to allow for scenarios where cached data requires persistence but without the volatility of using memory, or being limited to a filesystem. This can be useful at times when the cost of slower cache access is offset/avoided by the efficiency of the end caching result.

In a sense this is nothing new, it's largely a Model masquerading as a Cache. However the simpler access pattern and integration into caching adds the benefits of a database to some caching strategies. This is relevant for caches which utilise a multi-level caching strategy (i.e. entries are cached to database, but once loaded are cached into memory for faster access without any concern over data loss in the event of hardware failure - the database entries are not moved, only copied). It's also relevant, without the added complexity of an intermediate memory cache (distributed or local), simply to insure cached items are maintained with a greater degree of security and redundancy simply because they are intended to have a long term time to live.
{zone-data}

{zone-data:references}
* [Building A Better Page Cache|http://blog.astrumfutura.com/archives/380-Zend-Framework-Page-Caching-Part-1-Building-A-Better-Page-Cache.html]
* [Controller Based Cache Management|http://blog.astrumfutura.com/archives/381-Zend-Framework-Page-Caching-Part-2-Controller-Based-Cache-Management.html]
* [Tagging For Static File Caches|http://blog.astrumfutura.com/archives/383-Zend-Framework-Page-Caching-Part-3-Tagging-For-Static-File-Caches.html]

*Source Code (In Development)*
* [git repository|http://github.com/padraic/zfcache]
{zone-data}

{zone-data:requirements}
* *MUST* implement a fully funtional and configurable database cache
* *MUST* provide sufficient features to be easily adapted at a high level by a Cache Manager (see related proposal)
* *WILL* require some refactoring of Zend_Cache_Core to remove restrictions on ID validity and proxy non-Interface methods back to the Backend objects. By removal, it means allowing Backends to set their own validity rules to replace any defaults set by Zend_Cache_Core.
{zone-data}

{zone-data:dependencies}
*Zend Framework Classes*
* Zend_Cache_Core
* Zend_Exception
* Zend_Cache_Backend_Interface

*Additionally Proposed Classes*
* Zend_Cache_Backend_Database_Interface
{zone-data}

{zone-data:operation}
It's not intended to be prescriptive and enforce any particular database schema or connection method, so Zend_Cache_Backend_Database is a default choice for a database supported cache which, in combination with Zend_Cache_Backend_Database_Interface, enforces an API any Model can implement.

One possible schema solution supporting tags, which can be documented as the suggested approach (and perhaps included within Zend_Tool at some point when/if database migrations are implemented), could be as follows:

{{noformat}}
CREATE TABLE `cache` (
`id` varchar(255) collate utf8_unicode_ci NOT NULL,
`value` text collate utf8_unicode_ci NOT NULL,
`expires` datetime NOT NULL,
`tags` text collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `expires` (`expires`),
FULLTEXT KEY `tags` (`tags`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
{{noformat}}

The table follows a denormalised approach to tagging, so there is somewhat of an assumption (as if it being a database was not enough!) that the database cache entries would be copied to memory when called upon (leaving the database as a more secure persistent store). That said, it's not as slow as going the full three table route with foreign keys, and I doubt that level of normalisation is needed for a caching backend. In any case, a single table using fulltext search for tags should be sufficient in most scenarios.

With the table in place, the simplest method using the cache would be pass the backend a reference to the table name, and a Zend_Db adapter. This, by default, minimises the amount of setup required but will not prevent passing the backend a complete Model (of any description) implementing the Zend_Cache_Backend_Database_Interface interface through an alternative option. This would allow for more exotic database designs and/or Model messaging to be implemented at will.

Note that similar to any other cache, every entry is stored to a new single row including details of it's expiry date (creation time + user TTL) and tags. The internal mechanics of this backend are almost identical as for any other backend, with minor tweaks given the nature of the backend as a database. It will therefore support the full range of caching methods available to other backends.

Note: As noted with the Zend_Cache_Backend_Static, it's more useful than not to allow for an expanded set of ID possibilities. As this proposal suggests by the schema, IDs should accept a wide range of characters and lengths with little restriction unless it solves a real problem. A proposal for some minor refactoring of Zend_Cache_Core (the main suspect) will follow.
{zone-data}

{zone-data:milestones}
* Milestone 1: Complete any relevant Zend_Cache refactoring to support more dynamic self-validating backends
* Milestone 2: Complete final feature list incorporating feedback
* Milestone 3: Verify operation using Unit Tests
* Milestone 4: Documentation
{zone-data}

{zone-data:class-list}
* Zend_Cache_Frontend_Capture
* Zend_Cache_Backend_Static
{zone-data}

{zone-data:use-cases}
*Note:* All use cases have the same problem - the cache ID is actually the REQUEST URI of the current request and this can easily contain characters forbidden by Zend_Cache_Core's private static validation methods. I have omitted any workaround on the assumption this restriction can be lifted.

||UC-01: Initialising Cache||

{code:php}
$params = array(
'host' => '127.0.0.1',
'username' => 'xxxxxxxx',
'password' => 'xxxxxxxx',
'dbname' => 'test'
);
$db = Zend_Db::factory('Pdo_Mysql', $params);
$frontendOptions = array(
'lifetime' => 43200,
'automatic_serialization' => true,

);
$backendOptions = array(
'adapter' => $db
);
$cache = Zend_Cache::factory('Core', 'Database', $frontendOptions, $backendOptions);
{code}

||UC-02: Full example (remaining behaviour is identical to existing cache mixes)||

{code:php}
$params = array(
'host' => '127.0.0.1',
'username' => 'xxxxxxxx',
'password' => 'xxxxxxxx',
'dbname' => 'test'
);
$db = Zend_Db::factory('Pdo_Mysql', $params);
$frontendOptions = array(
'lifetime' => 43200,
'automatic_serialization' => true,

);
$backendOptions = array(
'adapter' => $db
);
$cache = Zend_Cache::factory('Core', 'Database', $frontendOptions, $backendOptions);

// cache an object

$obj = new stdClass;
$obj->foo = bar;
$cache->save($obj, 'myobject', array('tag1','tag2'));
$retrievedObj = $cache->load('myobject');
{code}

{zone-data}

{zone-data:skeletons}
Class skeletons will follow the current Zend_Cache_Backend_Interface_Extended and offer identical functionality to other backends. Class skeletons will be published here as development progresses.
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>