<!--toc--> |
|
## Introduction |
|
Unlike many caching systems available today in different |
web frameworks that are based on "key-value-timeout" |
concepts, CppCMS's cache provides "key-triggers-value-timeout" |
model that provides an easy and efficient way to |
invalidate cache - keep it consistent. |
|
Each cached object has following properties: |
|
1. The unique key that identifies it. |
2. The life time of the entry that can be infinite |
3. The set of triggers that can invalidate the cache. |
|
For example we have following triggers set: |
|
![Triggers](/pics/triggers-scheme.png) |
|
So we can invalidate multiple keys by rising different |
triggers. |
|
For example. when we update article about `drugs` we |
rise a trigger `new_drugs` and invalidate both `health` |
news and `news_all` that depend on this trigger. |
|
On the other hand the "technology" news remain untouched. |
|
This method allows us to keep the web site both |
up-to-date and fully cached. |
|
|
## Managing Triggers |
|
### Direct Handling |
|
When you cache a page you can easily add a trigger |
to it using `cppcms::cache_interface::add_trigger()` function. |
|
For example: |
|
if(cache().fetch_page("my_page")) |
return; |
|
// Create some content |
|
cache().add_trigger("my_trigger"); |
cache().store_page("my_page"); |
|
Now the `my_page` object would depend on two triggers |
`my_trigger` and `my_page` itself. |
|
You can clear the cache by rising a trigger: |
|
cache().rise("my_trigger"); |
|
Would create cache for all objects that depend on it |
including `my_page`. |
|
If you use cache interface to store some objects |
or text frames in it you can pass a set of triggers |
directly as a parameter. |
|
std::set<std::string> my_triggers; |
my_triggers.insert("my_trigger"); |
|
cache().store_data("my_object",my_object,my_triggers); |
|
Note: `my_object` trigger would be automatically added |
to the `my_object` key. |
|
### Automatic Dependencies Tracking |
|
CppCMS cache allows to track object dependencies |
automatically. |
|
Let's assume we have following code: |
|
if(cache().fetch_page("foo")) |
return; |
|
if(!cache().fetch_data("settings",settings)) { |
load_settings(settings); |
cache().store_data("settings",settings); |
} |
|
// do something |
|
cache().store_page("foo"); |
|
Now we use a "setting" object in order to create |
"foo" page. When cal call `fetch_data()` or `store_data()` |
the "settings" trigger is added as a dependency to "foo". |
|
More then that, if "settings" has its own triggers: |
|
if(!cache().fetch_data("settings",settings)) { |
load_settings(settings); |
std::set<std::string> settings_triggers; |
settings_triggers.insert("bar"); |
cache().store_data("settings",settings); |
} |
|
|
Then our "foo" page would automatically depend |
on "settings" and "bar" triggers, so for example |
rising "bar" would invalidate both "foo" and "settings". |
|
The automatic recording can be disabled by |
setting `no_triggers` parameter to "true" for `store_data()` |
and "fetch_data()" for example: |
|
|
if(!cache().fetch_data("settings",settings,true)) { |
load_settings(settings); |
cache().store_data("settings",settings,-1,true); |
// -1 for infinite timeout |
} |
|
### Nested Tacking |
|
The triggers are recorder for the frame, but sometimes |
we want to record the triggers on lower level. For example: |
|
- "page" |
- "settings" |
- "local_settings" |
- "global_settings" |
|
Like: |
|
if(cache().fetch_page("foo")) |
return; |
|
if(!cache().fetch_data("settings",settings)) { |
if(!cache().fetch_data("local_settings",ls) { |
... |
} |
if(!cache().fetch_data("global_settings",gs) { |
... |
} |
load_settings(settings,ls,gs) |
cache().store_data("settings",settings); |
} |
|
cache().store_page("foo"); |
|
But the `local_settings` and `global_settings` triggers |
would not be recorder for "settings" because "store\_data()" |
function does not know about them. |
|
So in order to handle such situation we have a |
[triggers\_recorder](/cppcms_ref_v0_99/classcppcms_1_1triggers__recorder.html) object: |
|
We change our code in following way: |
|
if(!cache().fetch_data("settings",settings)) { |
cppcms::triggers_recorder rec(cache()); |
if(!cache().fetch_data("local_settings",ls) { |
... |
} |
if(!cache().fetch_data("global_settings",gs) { |
... |
} |
load_settings(settings,ls,gs); |
cache().store_data("settings",settings,rec.detach()); |
} |
|
We start an instance of a `triggers_recorder` and |
and the end of the section we recorder the triggers |
on we detach it by calling `detach()` member function |
that returns a set of triggers, in our case |
the `local_settings` and `global_settings` |
|
We got all we needed |
|
## Recording Text Frames |
|
### From The Application |
|
Consider we have a situation where some part |
of the HTML frame is shared for many pages |
and it requires some non-trivial methods |
to create it (for example make some complex query |
to the DB in order to fetch all the data we need). |
|
CppCMS provides us a simple [copy_filter](/cppcms_ref_v0_99/classcppcms_1_1copy__filter.html). |
|
We can use it as following: |
|
std::string frame; |
if(cache().fetch_frame("key",frame)) { |
response().out() << frame; |
} |
else { |
cppcms::copy_filter tee(response().out()); |
... |
// generate something heavy |
... |
cache().store_frame("key",tee.detach()); |
} |
|
Note: If the frame exist in the cache, we |
write it to the output stream directly, but if |
it does not, we generate it in a usual way, |
but we attach a small filter `tee` to the |
output stream that would copy all the data |
to a temporary buffer and would write it |
to the stream as well. |
|
When we call `tee.detach()` we remove the filter |
and return the entire frame we had recorder. |
|
### From The View |
|
It is much more natural to cache some frames |
from the views - the template engine. CppCMS |
provides such a tool. |