Yii Framework Forum: flushable - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

flushable A simple dependency to flush specific records from cache Rate Topic: -----

#1 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 24 May 2012 - 02:08 AM

Here's a very simply dependency which helps you to remove specific items from the cache. It does not require another query to find out if your record has changed. Instead it just uses another cache key to inform the dependency about the change.

http://www.yiiframew...sion/flushable/

I've kept it very simple, so you use it like this:

<?php
// At the query:

$dependency = new FlushableDependency($id,'Post');
$post = Post::model()->cache(3600,$dependency)->findByPk($id);

// In the model:
FlushableDependency::flushItem($this->id,'Post');



Maybe i'll add a behavior to the extension which you can attach to your ActiveRecords to automate this. But i'm hesitating because it may not catch all updates (e.g. calls to saveAttributes()).

Let me know, what you think about it.


EDIT:
Updated the examples for 1.1.0.

This post has been edited by Mike: 24 May 2012 - 09:50 AM

2

#2 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 24 May 2012 - 03:36 AM

I think this is a really great idea, especially in combination with afterSave() etc. and will take a lot of load off the db.

Regarding your idea of making this a behavior: You think newcomers would easily get "tricked" by thinking saveAttributes() calls afterSave() which it doesn't? I agree that this is somewhat weird but wouldn't they have the same problem implementing this dependency by themselves?
0

#3 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 24 May 2012 - 05:03 AM

View PostHaensel, on 24 May 2012 - 03:36 AM, said:

Regarding your idea of making this a behavior: You think newcomers would easily get "tricked" by thinking saveAttributes() calls afterSave() which it doesn't?


Exactly. We should have events for this. I hope we get them in Yii 2.0.

View PostHaensel, on 24 May 2012 - 03:36 AM, said:

I agree that this is somewhat weird but wouldn't they have the same problem implementing this dependency by themselves?


Not sure what you mean. But if we don't automate too much, there's a better chance that the developer understands what this dependency does. So he will know, that whenever he updates (either by save() or saveAttributes()) or deletes a record, he also has to flush the respective ids from the cache through this dependency.
0

#4 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 24 May 2012 - 05:22 AM

View PostMike, on 24 May 2012 - 05:03 AM, said:

Not sure what you mean. But if we don't automate too much, there's a better chance that the developer understands what this dependency does. So he will know, that whenever he updates (either by save() or saveAttributes()) or deletes a record, he also has to flush the respective ids from the cache through this dependency.


That's what I tried to say although I can't even get that form my own words now ;)
Less automation will make it easier to maintain I think
0

#5 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 24 May 2012 - 09:52 AM

Bethrenzen raised the valid argument, that this dependency could also be used for page/fragment caching. In this case the $model does not make sense. So i changed the interface slightly and realeased version 1.1.0.

I hope such an interface change is still acceptable on the first day :)
0

#6 User is offline   Bethrezen 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 76
  • Joined: 24-December 08
  • Location:Tambov, Russia

Posted 25 July 2012 - 08:58 AM

Mike, can you please upload this extension to github? Thanks.
My site and Blog

dotPlant CMS based on Yii Framework
0

#7 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 26 July 2012 - 09:34 AM

Ok, i'll move it to github over the next days.
0

#8 User is offline   Fábio Felicidade 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 16
  • Joined: 14-March 12

Posted 21 March 2013 - 01:08 PM

I'm working in a semi-static website project based in Yii framework.
At this moment i want to implement a cache to improve performance and page on load.

I'm using COutputCache filter on Controller (that extends CController) to cache all pages (static and dynamic).
public function filters()
	{
		return array(
			array(
				'COutputCache',
				'duration'=>20,
			)
		);
	}
	


As I have several zones with dynamic data updated regularly by members,
Flushable extension seems to be an excellent solution but some problems came up.

On all models, i flush data on afterSave method.
public function afterSave()
	{
		FlushableDependency::flushItem($this->id,'Post');
		return parent::afterSave();
	}


Now, when i want to get the fresh data, cOutputCache override any results (parent cache duration bigger)
$data = Post::model()->findByPk($id);

Is it necessary to use renderDynamic?

second with this extension will it be necessary to replace the above code by the following code in every controllers/actions?
// Cache a ActiveRecord
$dependency = new FlushableDependency($id,'Post');
$post = Post::model()->cache(10,$dependency)->findByPk($id);


I need help here.
Thank you in advance.
0

#9 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 21 March 2013 - 01:18 PM

1. You would have to add another flushable dependency to your COutputCache. But this only makes sense if Post is the only cached AR on that page and if you know the id.
2. You only should add the dependency if you use a cache() query. So in your example, you don't have to replace

$data = Post::model()->findByPk($id);

0

#10 User is offline   Fábio Felicidade 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 16
  • Joined: 14-March 12

Posted 21 March 2013 - 01:45 PM

Thank you for your quick answer.

If i replace Controller filters to the following code, does it work?

public function filters()
        {
                return array(
                        array(
                                'COutputCache',
                                'duration'=>20,
                                'varyByParam' => array('id'),
                                'dependency' => array(
					'class'=>'CDbCacheDependency',
    				        'sql'=>'SELECT MAX(modified) FROM tb_prov',
                                 )
                        ),
                        array(
                                'COutputCache',
                                'duration'=>20,
                                'varyByParam' => array('id'),
                                'dependency' => array(
					'class'=>'CDbCacheDependency',
    				        'sql'=>'SELECT MAX(modified) FROM tb_prop',
                                 )
                        ),
                        array(
                                'COutputCache',
                                'duration'=>20,
                                'varyByParam' => array('id'),
                                'dependency' => array(
					'class'=>'CDbCacheDependency',
    				        'sql'=>'SELECT MAX(modified) FROM tb_mem',
                                 )
                        )
                );
        }



That is one dependency for each model (i have more or less 50 models for now)?

Is a cOutputCache filter in general controller (Controller that extends CController) the best approach? Or the best approach is define cOutputCache filter to each controller?

Bottom line, i want to cache all pages with cOutputCache and always that a model is updated, cache should be updated too.

Thank you a lot for your help.
0

#11 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 22 March 2013 - 03:16 AM

You can not have more than one COutputCache for the same page. So either configure your filter to be active for specific pages only or take another approach. Apart from that i can't see, where you use the flushable behavior at all.
0

#12 User is offline   Fábio Felicidade 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 16
  • Joined: 14-March 12

Posted 28 March 2013 - 02:17 PM

Thank you for your help.

To solve my problem I followed your advice and tried a new approach to develop my caching system and improve page loading.

I used COutputCache filter on specific pages with a dependency to renew cache as the following example.
class ProviderController extends Controller
{
...
        public function filters()
	{
		return array(
			'accessControl', // perform access control for CRUD operations
			
			array(
				'COutputCache +navigation',
				'duration'=>7200,
				'dependency'=>array(
					'class'=>'system.caching.dependencies.CDbCacheDependency',
					'sql'=>'SELECT MAX(update_date) FROM tb_provider'
				)
			)
			
		);
	}
...
}


Beyond the pages, I also needed to cache fragments of some pages. These fragments depended on some ActiveRecords. For that propose, used the flushable extension that facilitate this process.
For each ActiveRecord used on a fragment caching I override afterSave method to flush the content from cache as following code.
Provider ActiveRecord example:
class Provider extends CActiveRecord
{
...
        public function afterSave()
	{
		FlushableDependency::flushItem('Provider');
		return parent::afterSave();
	}
...
}



And on the fragment page which depends on Property ActiveRecord I used the following code:
<?php
$dependency = new FlushableDependency('Provider');
if($this->beginCache('memberbenefit_widget', array('duration'=>7200, 'dependency'=>$dependency)))
{ 
?>
...HTML code...
<?php 
	$this->endCache();
}
?>


I don't know if this is the best solution but it seems to work.
0

#13 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,013
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 29 March 2013 - 06:19 AM

Much better :). But you should include the provider id in the calls to flushItem($this->id,'Provider') and FlushableDependency($id, 'Provider'). Otherwhise whenever you update one provider you flush all from the cache. You'll probably use something like $id = isset($_GET['id']) ? (int) $_GET['id'] : -1; or something to get the $id in the controller.
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users