I’m trying to cache some results retrieved from database using Yii framework 1.1.12. Here is what I am doing in short:
public static function getCategories()
{
if (self::$_categories !== null)
return self::$_categories;
print "Getting categories...";
self::$_categories = Yii::app()->cache->get("categoriesList");
if (self::$_categories === false)
{
$sql = "SELECT id, parent_id, name FROM {{category}} WHERE id > 0 AND is_deleted = 0";
$categoriesList = Yii::app()->db->createCommand($sql)->queryAll();
// Doing some work with $categoriesList and obtaining self::$_categories as the result
// ...
$dependency = new CDbCacheDependency("SELECT MAX(update_time) FROM {{category}}");
Yii::app()->cache->set("categoriesList", self::$_categories, 3600, $dependency);
}
return self::$_categories;
}
Using the profiling tool I can see that everything works. At the first time both queries are executed (each query once):
SELECT MAX(update_time) FROM arrenda_category
SELECT id, parent_id, name FROM arrenda_category WHERE id > 0 AND is_deleted = 0
On further requests only first one is executed.
The problem is when I increase max value of update_time in arrenda_category table (even not using my own edit script – directly from MySQL command line) and refresh the page a count of SELECT MAX(update_time) FROM arrenda_category queries becomes equal to 2. Further refreshes give only one execution again. The interesting thing is if I clear the cache I have one execution of SELECT MAX(...) ... query too.
So I don’t understand why a query of cache dependency class is executed twice on condition’s change. Is there something wrong with my code or maybe anything else?
P.S. I’m sure that SELECT MAX(update_time) FROM arrenda_category can be executed only in function described above. I also see that the line print "Getting categories..." is reached once per page request.
Yes. It is expected.
EXPLANATION
Suppose the data is already in the cache. And when you call the function
getCategories(), the lineYii::app()->cache->get("categoriesList")will execute the dependancy query to check whether the data is changed. Since it is not changed the query is executed one time only.Now you changed the
update_timevalue externally ( or using some another code in your app ), and you call thegetCategories()again,Yii::app()->cache->get("categoriesList")executes the dependancy query to check whether the data in the cache is valid. It finds that data is invalid and returns falseSELECT id, parent_id, name FROM {{category}} WHERE id > 0 AND is_deleted = 0is executed to fetch the updated data from the databaseYii::app()->cache->set("categoriesList", self::$_categories, 3600, $dependency);AGAIN executes the dependancy querySELECT MAX(update_time) FROM {{category}}to get the latestMAX(update_time)whose result is stored along with the data. Thats why the query is executed twice.So the point is that every time you
set()a value to cache, the dependancy value must be stored along with it since it is needed for the subsequentget()queries for checking whether the dependency is changed.PS:
If you want more clarification check the source code of the
set()function of yourcacheapplication component ,it calls theevaluateDependency()function of theCDbCacheDependancyclass which inturn calls thegenerateDependentData()which causes the execution of the dependancy query