<!--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: 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 deseralization 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. |
<!--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: 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 deseralization 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.
|
|