Hi everyone
In these days of relative calm, 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="categories" 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();
categoryBean.setId( 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 productBean = new Product()
productBean.setId( id );
var categories = [];
for ( var categoryId in list_categories_from_db ) {
//ok, I don't do exactly that but it's to explain that
// inside the "Product" object there are the categories in cache
categories.add(
getCategory( categoryId )
)
}
productBean.setCategories( categories );
cachePut( "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 “Categories” in the “Product” bean but use a service to get all the categories 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.
- 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…
What I would like to achieve is to have my own “ToolSomethingCache” that deletes all “parents” when deleting a child. Before starting to write code, I would like to understand if there is any literature on the matter, or if products like CacheBox do this job for me
I hope I made myself clear.
I wish a wonderful New Year to all the guys who keep this community alive, and the developers who make Lucee a fantastic product!