Main  /  Edit version 8  /  Edit version 9  /   /  Users Area

Difference "Advanced Caching" ver. 8 versus ver. 9

Titles:

Version 8Version 9
.Advanced Caching

Content:

.
<!--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.
When we update an 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 clear cache for all objects that depend on it
including `my_page`.
If you use the 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 we call `fetch_data()` or `store_data()`
the "settings" trigger is added as a dependency to "foo".
More than 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 recorded 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 exists 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: the [`<% cache ... %>`](/wikipp/en/page/cppcms_1x_templates_flow#Caching.Elements) tag.
For example
<% template foo() %>
<html>
...
<body>
<% cache "sidebar" %>
<div id="sidebar">
...
</div>
<% end cache %>
...
</body>
</html>
<% end template>
However frequently it is not enough to "just cache"
some HTML frame, we usually need to prepare some data
that would be rendered, so we would like to be
able to do something, like updating the rendering
content with some data from database in case of
cache miss.
We can add a callback to the content that would
be called in case of cache miss.
<% cache "sidebar" on miss update_content() %>
<div id="sidebar">
...
</div>
<% end cache %>
So we add the the content that is rendered a callback
`update_content()` that may look like:
my_content : public cppcms::base_content {
booster::function<void()> update_content;
};
And in the application we would define it like:
my_content c;
// bind a member function `update_content` to the
// callback
c.update_content = std::bind(&my_app::update_content,this,&c);
And our update function would look like:
void update_content(my_content *c)
{
c->some_data = fetch_some_bid_data_from_db();
}
So on cache miss `update_content` would be called
and setup all the content for the rendering of the frame.
## Notes about Page level caching
The page level caching is the best cache that can be
done. If you can always cache entire page rather
then for example database objects that are rendered
in the view.
There are two reasons for this:
1. Serialization and de-serialization of objects takes time. Rendering even simple takes time. So caching entire page
would give the best performance.
2. CppCMS pages are cached already gzip compressed. So you do not only save the time required to compress the page you
also reduce the response time as the data that is sent is
much smaller then original HTML.
Thanks to the fact the _all_ modern browsers support
gzip compression makes the page level caching fast
and efficient.
## Triggers or Timeouts
CppCMS provides you two ways to manage cached objects
life time: by limiting their life time by a timeout or
by explicitly removing them from the cache using triggers.
If certain page is updated very frequently and visited
very frequently then it may be wise to setup a small timeout and never invalidate it manually.
However if you can define a sources of page invalidation,
design a triggers set carefully and define dependencies clearly then you will be able to almost never use
timeouts and keep the web site up-to-date.
For example CppCMS blog's cache system is entirely
triggers based and never uses timeouts. On the other
side this wiki uses triggers for some pages and uses
timeouts for some others like for example index that
any page update would trigger its invalidation.
Finally the safest thing is to use both, thus even
if you have a bug and missed some dependency the page
would eventually be updated.
---
← [Internationalization and Localization][prev]
| [Top](#maincontent)
| [Asynchronous I/O][next] →
[toc]: /wikipp/en/page/cppcms_1x
[prev]: /wikipp/en/page/cppcms_1x_i18n_and_l10n
[next]: /wikipp/en/page/cppcms_1x_aio

Sidebar:

## Related
- [cache\_interface](/cppcms_ref/1.0.2/classcppcms_1_1cache__interface.html)
## Browse
[CppCMS 1.x.x - Stable][toc]
← [Internationalization and Localization][prev]
[Asynchronous I/O][next] →
[toc]: /wikipp/en/page/cppcms_1x
[prev]: /wikipp/en/page/cppcms_1x_i18n_and_l10n
[next]: /wikipp/en/page/cppcms_1x_aio

About

CppCMS is a web development framework for performance demanding applications.

Support This Project

SourceForge.net Logo

Поддержать проект

CppCMS needs You


Navigation

Main Page


Valid CSS | Valid XHTML 1.0