Caching Indefinitly And Flushing Cache When Needed


I’m thinking of the best way to implements caching for my website and I have an Idea but I don’t know if it’s viable.

I have some pages displaying a lot of data from severals models. So if I want the content displayed to the end user to be everytime up to date I need to put several caches with several cache dependencies (checking for each model type the max update time).

The problem is that if I have 10 different models then I’ll have to execute 10 requests and that’s a lot!

This is why I came up with the idea to cache all my content with a big caching period (like 1 week) and whitout any dependencies. Then in the aftersave() methods of my models I’ll flush all the concerned caches (for example flush the cache of the model page, flush the cache listing all the models, …)

That way if a page is visited again the content won’t be cached so the cache will be generated again.

What do you think about this strategy ?

Hi darkheir,

I believe that we should let the caches flush themselves by peeking their own dependencies. Flushing caches manually doesn’t sound a good idea to me.

Imagine you have several caches depending on the same condition. You must flush them all when the underlying data get changed. But it’s error-prone and tedious. You will sure to forget doing it when you have added another new cache.

Probably you may consider nested caching. :)

And if you are conscious about the db access overhead of CDbCacheDependency, well, I think there has been a good post by @redguy.

Hi softark and thanks for you answer!

I agree with you that with what I was suggesting you have to think to flush all the cache, If you forget one then it’s gonna be disastrous!

But I was thinking about implementing a module or a widget registering for each model the id of the caches that have been set so I could then know what are all the items I have to flush.

I wasn’t thinking about doing it manually.

For the overhead of CDbCacheDependency I’m very interested by the post but I didn’t find it!

I don’t think the overhead will be my main problem, it’s more that requesting Select MAX(update_time) FROM model on a big table will take time. I can see that in general my caching requests are the one taking the more time (but it’s still way much better than no caching at all!).

Several things to note:

  1. Generally "SELECT MAX(updatetime)" is not appropriate.

It doesn’t update the dependency when a record has been deleted.

One workaround may be "SELECT AVG(updatetime)" … but it looks damned slower.

So, in fact I ended up avoiding CDbCacheDependency.

I’m now using CGlobalStateCacheDependency.

I update the global states with the current time in afterSave and afterDelete.

But probably the best solution is the one that has been suggested by redguy (I couldn’t find the post either).

He uses a dedicated table to record the modified times of all other tables. And every record representing other table is updated automatically by the db triggers of the corresponding table.

  1. I have to repeat. "Let the cache manage itself."

Every cache entry has these data in it: 1) cache id, 2) expiration time, 3) dependency method, 4) dependency result and 5) cache content. It is self-content and needs no external mechanism to hold these data. What we have to do is just maintaining the data that supply the proper dependencies.

And there’s no need to flush a cache until it is accessed. Flushing all the dependent caches can be another overhead. I believe that a lazy evaluation approach is better suited for caching.

Yeah I didn’t think about it and it could be a problem! I’ve been fouled by the yii guide who is showing this technique.

Really nice way to do it!

This way I would be avoiding useless db requests, and I’m not flushing the cache myself!

The yii guide should talk more about this cachedependency feature!

Yeah I know that it’s better to let the cache manage itself, this is why I created this post to see some alternatives!

I think I’ll go with your solutions, it seems simple to implements!

Just a question, in the global state you store one entry for each model object and one for all the models?

Because if I store only the global state for a table in general then when a new entry is added all the caches for the models belonging to this table will be flushed

Thanks again for your advices!


The topic where redguy is explaining his solution:

I use one global state per each table.

But in fact I’m not satisfied with my solution at all. It’s tedious and error-prone. When you have to use DAO instead of AR, afterSave and afterDelete won’t be called and you have to manually update the global state.

Yeah, that’s it. Thanks. Very good thread, isn’t it?

Yeah really interesting, the redguy solution is really good and since everything is triggered from the db you don’t have to set anything on the php side to update the table storing the cache times!

It’s true, it’s one of the drawback of your solution, but you don’t use the db and since the DB is often the bottleneck in apps it can help to ease the load on it!