[doc] update memcache references to memcached
[lighttpd.git] / doc / outdated / cml.txt
blob32ae1d5d9b9baf1714caedd40076219e96fade30
1 =========================
2 CML (Cache Meta Language)
3 =========================
5 ---------------
6 Module: mod_cml
7 ---------------
9 :Author: Jan Kneschke
10 :Date: $Date: 2004/11/03 22:26:05 $
11 :Revision: $Revision: 1.2 $
13 :abstract:
14   CML is a Meta language to describe the dependencies of a page at one side and building a page from its fragments on the other side using LUA.
16 .. meta::
17   :keywords: lighttpd, cml, lua
19 .. contents:: Table of Contents
21 Description
22 ===========
24 CML (Cache Meta Language) wants to solves several problems:
26  * dynamic content needs caching to perform
27  * checking if the content is dirty inside of the application is usually more expensive than sending out the cached data
28  * a dynamic page is usually fragmented and the fragments have different livetimes
29  * the different fragements can be cached independently
31 Cache Decision
32 --------------
34 A simple example should show how to a content caching the very simple way in PHP.
36 jan.kneschke.de has a very simple design:
38  * the layout is taken from a template in templates/jk.tmpl
39  * the menu is generated from a menu.csv file
40  * the content is coming from files on the local directory named content-1, content-2 and so on
42 The page content is static as long non of the those tree items changes. A change in the layout
43 is affecting all pages, a change of menu.csv too, a change of content-x file only affects the
44 cached page itself.
46 If we model this in PHP we get: ::
48   <?php
50   ## ... fetch all content-* files into $content
51   $cachefile = "/cache/dir/to/cached-content";
53   function is_cachable($content, $cachefile) {
54     if (!file_exists($cachefile)) {
55       return 0;
56     } else {
57       $cachemtime = filemtime($cachefile);
58     }
60     foreach($content as $k => $v) {
61       if (isset($v["file"]) &&
62           filemtime($v["file"]) > $cachemtime) {
63         return 0;
64       }
65     }
67     if (filemtime("/menu/menu.csv") > $cachemtime) {
68       return 0;
69     }
70     if (filemtime("/templates/jk.tmpl") > $cachemtime) {
71       return 0;
72     }
73   }
75   if (is_cachable(...), $cachefile) {
76     readfile($cachefile);
77     exit();
78   } else {
79     # generate content and write it to $cachefile
80   }
81   ?>
83 Quite simple. No magic involved. If the one of the files is new than the cached
84 content, the content is dirty and has to be regenerated.
86 Now let take a look at the numbers:
88  * 150 req/s for a Cache-Hit
89  * 100 req/s for a Cache-Miss
91 As you can see the increase is not as good as it could be. The main reason as the overhead
92 of the PHP interpreter to start up (a byte-code cache has been used here).
94 Moving these decisions out of the PHP script into a server module will remove the need
95 to start PHP for a cache-hit.
97 To transform this example into a CML you need 'index.cml' in the list of indexfiles
98 and the following index.cml file: ::
100   output_contenttype = "text/html"
102   b = request["DOCUMENT_ROOT"]
103   cwd = request["CWD"]
105   output_include = { b .. "_cache.html" }
107   trigger_handler = "index.php"
109   if file_mtime(b .. "../lib/php/menu.csv") > file_mtime(cwd .. "_cache.html") or
110      file_mtime(b .. "templates/jk.tmpl")   > file_mtime(cwd .. "_cache.html") or
111      file_mtime(b .. "content.html")        > file_mtime(cwd .. "_cache.html") then
112      return CACHE_MISS
113   else
114      return CACHE_HIT
115   end
117 Numbers again:
119  * 4900 req/s for Cache-Hit
120  *  100 req/s for Cache-Miss
122 Content Assembling
123 ------------------
125 Sometimes the different fragment are already generated externally. You have to cat them together: ::
127   <?php
128   readfile("head.html");
129   readfile("menu.html");
130   readfile("spacer.html");
131   readfile("db-content.html");
132   readfile("spacer2.html");
133   readfile("news.html");
134   readfile("footer.html");
135   ?>
137 We we can do the same several times faster directly in the webserver.
139 Don't forget: Webserver are built to send out static content, that is what they can do best.
141 The index.cml for this looks like: ::
143   output_contenttype = "text/html"
145   cwd = request["CWD"]
147   output_include = { cwd .. "head.html",
148                      cwd .. "menu.html",
149                      cwd .. "spacer.html",
150                      cwd .. "db-content.html",
151                      cwd .. "spacer2.html",
152                      cwd .. "news.html",
153                      cwd .. "footer.html" }
155   return CACHE_HIT
157 Now we get about 10000 req/s instead of 600 req/s.
159 Power Magnet
160 ------------
162 Next to all the features about Cache Decisions CML can do more. Starting
163 with lighttpd 1.4.9 a power-magnet was added which attracts each request
164 and allows you to manipulate the request for your needs.
166 We want to display a maintainance page by putting a file in a specified
167 place:
169 We enable the power magnet: ::
171   cml.power-magnet  = "/home/www/power-magnet.cml"
173 and create /home/www/power-magnet.cml with: ::
175   dr = request["DOCUMENT_ROOT"]
177   if file_isreg(dr .. 'maintainance.html') then
178     output_include = { 'maintainance.html' }
179     return CACHE_HIT
180   end
182   return CACHE_MISS
184 For each requested file the /home/www/power-magnet.cml is executed which
185 checks if maintainance.html exists in the docroot and displays it
186 instead of handling the usual request.
188 Another example, create thumbnail for requested image and serve it instead
189 of sending the big image: ::
191   ## image-url is /album/baltic_winter_2005.jpg
192   ## no params -> 640x480 is served
193   ## /album/baltic_winter_2005.jpg/orig for full size
194   ## /album/baltic_winter_2005.jpg/thumb for thumbnail
196   dr = request["DOCUMENT_ROOT"]
197   sn = request["SCRIPT_NAME"]
199   ## to be continued :) ...
201   trigger_handler = '/gen_image.php'
203   return CACHE_MISS
206 Installation
207 ============
209 You need `lua <http://www.lua.org/>`_ and should install `memcached <http://www.memcached.org>`_ and have to configure lighttpd with: ::
211   ./configure ... --with-lua --with-memcached
213 To use the plugin you have to load it: ::
215    server.modules = ( ..., "mod_cml", ... )
217 Options
218 =======
220 :cml.extension:
221   the file extension that is bound to the cml-module
222 :cml.memcache-hosts:
223   hosts for the memcache.* functions
224 :cml.memcache-namespace:
225   (not used yet)
226 :cml.power-magnet:
227   a cml file that is executed for each request
229 Language
230 ========
232 The language used for CML is provided by `LUA <http://www.lua.org/>`_.
234 Additionally to the functions provided by lua mod_cml provides: ::
236   tables:
238   request
239     - REQUEST_URI
240     - SCRIPT_NAME
241     - SCRIPT_FILENAME
242     - DOCUMENT_ROOT
243     - PATH_INFO
244     - CWD
245     - BASEURI
247   get
248     - parameters from the query-string
250   functions:
251   string md5(string)
252   number file_mtime(string)
253   string memcache_get_string(string)
254   number memcache_get_long(string)
255   boolean memcache_exists(string)
258 What ever your script does, it has to return either CACHE_HIT or CACHE_MISS.
259 It case a error occures check the error-log, the user will get a error 500. If you don't like
260 the standard error-page use ``server.errorfile-prefix``.