HomeOracle Page 2 - Generic Architecture for Caching Table Data: Supercharge Your Cache
Switching the cache on and off - Oracle
In the first part of this series we started of by putting the basic structures in place for a cache and wrote some code to manage the cache. In this next part, we will extend the functionality of our cache.
When my code produces truly unexpected results, I always immediately suspect the cache: is it out of sync, does it contain corrupted data, and so on. So what would be an easy way to confirm whether the cache really is to blame? How about switching it off! Thatís right, if you switch the cache off, and your code suddenly starts working, there must be something wrong with your cache; if it still does not work, you are going to have to dig deeper, but at least you ruled out the cache. Letís see how we can implement such a switch.
You will first need to create the switch. This is basically a private global BOOLEAN variable that you declare in your package body and will signal if the cache is in use (TRUE) or not (FALSE).
g_caching BOOLEAN :=TRUE;
Here we default g_caching to TRUE meaning the cache is on by default.
In order for anybody to ďsetĒ the switch, we need a procedure, which we will make public by publishing it in the package specification:
PROCEDURE set_caching (p_on IN BOOLEAN DEFAULTTRUE) AS BEGIN g_caching := NVL(p_on,TRUE); END set_caching;
Some of you are probably wondering why we donít just make our g_caching variable public and forget about the set_caching procedure. It is considered good practice to hide your global variables (all of them, not just this one) in the package body. You should never expose them because then you lose control over its content. Anybody can set your public variable to whatever they want, making it impossible to track what happens.
Using this technique, you have complete control over your global variable and you can add your own business rules. E.g. my business rule is that if NULL is passed in, I set the value to TRUE. If I had exposed the variable, I could not enforce my business rule, and somebody could have easily set it to NULL, probably breaking the caching logic further on. It is true that I could change all the other code to handle NULL values in my g_caching variable, but this is much cleaner (and easier!). I could also extend the set_caching procedure later on, adding some security measures (e.g. only allow certain users to set the switch). You can even log who sets the switch, what it was set to and when.
We also create a public function that can tell us whether the cache is on or off:
FUNCTION caching RETURN BOOLEAN AS BEGIN RETURN g_caching; END caching;
Donít forget to add these functions to the package specification.
Now we are ready to make our modifications to the caching logic. Here is the new code:
FUNCTION dept_data (p_dept_id IN dept.deptno%TYPE) RETURN department_tp AS l_dept_data department_tp; BEGIN l_dept_data := read_from_cache (p_dept_id); IF (l_dept_data.NAME IS NULL) THEN l_dept_data := read_from_db (p_dept_id); IF ((l_dept_data.NAME IS NOT NULL)AND(caching)) THEN write_to_cache (p_dept_id, l_dept_data); END IF; END IF; RETURN l_dept_data; END dept_data;
We do the check before we write to the cache. Notice though that we donít check whether caching is switched on before we read. This isnít really necessary as the cache would be empty anyway, forcing a DB read later on (you could add the check though if you want).
Anybody can now control the behavior of your cache (use it or not), by calling set_caching:
DECLARE l_dept_data dept_cache.department_tp; BEGIN -- cache will be used: the default l_dept_data := dept_cache.dept_data ('10'); -- Cache will not be used dept_cache.set_caching (FALSE); l_dept_data := dept_cache.dept_data ('10'); END;