As we know:
Hi everyone
I’d like to talk to you about a topic that I’ve been carrying around since I’ve been a developer without ever finding the perfect solution: cache invalidation with nested objects.
This is the scenario:
Product.cfc:
property name="id" type="String"
property name="category" type="Category"
Category.cfc:
property name="id" type="String";
When I read a “Category”:
function getCategory( id ) {
// check that the object is not already in cache
// if yes, return it
if ( cacheIdExists("category_" & id) ) {
return cacheGet( "category_" & id )
} else {
// Otherwise I create it
var categoryBean = new Category();
var categoryFromDB = getDAO().select( id = id );
categoryBean.setId( categoryFromDB.category_id );
cachePut( "category_#id#", categoryBean );
return categoryBean;
}
}
When I read “Product”:
function getProduct( id ) {
// check that the object is not already in cache
// if yes, return it
if ( cacheIdExists( "product_" & id) ) {
return cacheGet( "product_" & id )
} else {
// Otherwise I create it
var productFromDB = getDAO().select( id = id );
var productBean = new Product()
productBean.setId( productFromDB.product_id );
// get the category from model
var categoryBean = getCategory( productFromDB.category_id );
productBean.setCategory( categoryBean );
cachePut( "product_#productFromDB.product_id#", productBean );
return productBean;
}
}
As you can see, I cached the entire “Product” object, including its “Categoy” children.
But now I have the problem that when I edit a category, I should invalidate the cache of all linked “Product” objects.
It’s quite simple when there’s a parent and a few children, but it gets really complex if there are many, very nested children, children, children…
The solutions that I have found and that I apply depending on the complexity are these:
- Clear the parent cache by hand (in our example all the “Products” that have the category changed. It works, but it’s complicated with many parents/children).
- Don’t put the nested properties like “Category” in the “Product” bean but use a service to get all the category of a product. It’s not as solid as I would like.
- Use an event system that removes as in point 1. It is more complex but it also works with complex objects. In this case i could use the CacheBox events.
- Don’t cache all the “Product” object, but create it every time. It’s a good solution because it generally decreases the load on the db a lot anyway, but I would prefer that my code doesn’t do useless things like always creating the same object when it hasn’t been modified yet. And then I don’t really like caching the data that comes from the DB.
I was trying to figure out if CacheBox could take care of these “cascading deletions” for me.
I could use both CacheBox events (afterCacheElementRemoved, afterCacheElementUpdated methods) and listeners.
I hope I made myself clear.
Before I start coding, I’d like to hear your opinion.
I wish a wonderful New Year to all the guys who keep this community alive.
As usual, many many many thanks