From f4dad25af0e43899b82591b6f18c6e7e0c594c1b Mon Sep 17 00:00:00 2001 From: Will Rossiter Date: Fri, 17 Oct 2014 21:16:50 +1300 Subject: [PATCH] Rewrite and tidy up of performance section --- .../08_Performance/00_Partial_Caching.md | 122 ++++++++---------- .../08_Performance/02_Cache_Control.md | 25 ---- .../08_Performance/02_HTTP_Cache_Headers.md | 33 +++++ .../08_Performance/03_Profiling.md | 13 ++ .../08_Performance/04_Static_Publishing.md | 20 +++ .../08_Performance/Profiling.md | 8 -- .../08_Performance/index.md | 12 +- docs/en/02_Developer_Guides/17_CLI/index.md | 89 +++++++------ 8 files changed, 179 insertions(+), 143 deletions(-) delete mode 100644 docs/en/02_Developer_Guides/08_Performance/02_Cache_Control.md create mode 100644 docs/en/02_Developer_Guides/08_Performance/02_HTTP_Cache_Headers.md create mode 100644 docs/en/02_Developer_Guides/08_Performance/03_Profiling.md create mode 100644 docs/en/02_Developer_Guides/08_Performance/04_Static_Publishing.md delete mode 100644 docs/en/02_Developer_Guides/08_Performance/Profiling.md diff --git a/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md b/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md index 64e5c8366..c2836e24e 100644 --- a/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md +++ b/docs/en/02_Developer_Guides/08_Performance/00_Partial_Caching.md @@ -1,64 +1,49 @@ +title: Partial Caching +summary: Cache SilverStripe templates to reduce database queries. + # Partial Caching -## Introduction - -Partial caching is a feature that allows the caching of just a portion of a page. - -As opposed to static publishing, which avoids the SilverStripe controller layer on cached pages, partial caching allows -caching for pages that contain a mix of moderately static & user specific data, and still provide full access control -and permission enforcement. - -The trade-off is that it does not provide as much performance improvement as static publishing, although for data heavy -pages the speed increases can be significant. - -## Basics - -The way you mark a section of the template as being cached is to wrap that section in a cached tag, like so: +Partial caching is a feature that allows the caching of just a portion of a page. :::ss - <% cached %> + <% cached 'CacheKey' %> $DataTable ... <% end_cached %> -Each cache block has a cache key - an unlimited number of comma separated variables (in the same form as `if` and -`loop`/`with` tag variables) and quoted strings. - -Every time the cache key returns a different result, the contents of the block are recalculated. If the cache key is the -same as a previous render, the cached value stored last time is used. +Each cache block has a cache key. A cache key is an unlimited number of comma separated variables and quoted strings. +Every time the cache key returns a different result, the contents of the block are recalculated. If the cache key is +the same as a previous render, the cached value stored last time is used. Since the above example contains just one argument as the cache key, a string (which will be the same every render) it -will invalidate the cache after the TTL has expired (default 10 minutes) +will invalidate the cache after a given amount of time has expired (default 10 minutes). Here are some more complex examples: -From a block that updates every time the Page subclass it's the template for updates - - :::ss - <% cached 'database', LastEdited %> - - -From a block that shows a login block if not logged in, or a homepage link if logged in, depending on the current member - :::ss + <% cached 'database', LastEdited %> + + <% end_cached %> + <% cached 'loginblock', CurrentMember.ID %> + + <% end_cached %> - -From a block that shows a summary of the page edits if administrator, nothing if not - - :::ss <% cached 'loginblock', LastEdited, CurrentMember.isAdmin %> + + <% end_cached %> - -An additional global key is incorporated in the cache lookup. The default value for this is -`$CurrentReadingMode, $CurrentUser.ID`, which ensures that the current `[api:Versioned]` state and user ID are -used. This may be configured by changing the config value of `SSViewer.global_key`. It is also necessary -to flush the template caching when modifying this config, as this key is cached within the template itself. +An additional global key is incorporated in the cache lookup. The default value for this is +`$CurrentReadingMode, $CurrentUser.ID`. This ensures that the current `[api:Versioned]` state and user ID are used. +This may be configured by changing the config value of `SSViewer.global_key`. It is also necessary to flush the +template caching when modifying this config, as this key is cached within the template itself. For example, to ensure that the cache is configured to respect another variable, and if the current logged in user does not influence your template content, you can update this key as below; +**mysite/_config/app.yml** + :::yaml SSViewer: global_key: '$CurrentReadingMode, $Locale' @@ -66,39 +51,45 @@ user does not influence your template content, you can update this key as below; ## Aggregates -Often you want to invalidate a cache when any in a set of objects change, or when the objects in a relationship change. -To help do this, SilverStripe introduces the concept of Aggregates. These calculate and return SQL aggregates -on sets of `[api:DataObject]`s - the most useful for us being the Max aggregate. +Often you want to invalidate a cache when any object in a set of objects change, or when the objects in a relationship +change. To do this, SilverStripe introduces the concept of Aggregates. These calculate and return SQL aggregates +on sets of [api:DataObject]s - the most useful for us being the `Max` aggregate. For example, if we have a menu, we want that menu to update whenever _any_ page is edited, but would like to cache it -otherwise. By using aggregates, we can do that like this: +otherwise. By using aggregates, we do that like this: :::ss <% cached 'navigation', List(SiteTree).max(LastEdited), List(SiteTree).count() %> -If we have a block that shows a list of categories, we can make sure the cache updates every time a category is added or -edited +The cache for this will update whenever a page is added, removed or edited. + +If we have a block that shows a list of categories, we can make sure the cache updates every time a category is added +or edited :::ss <% cached 'categorylist', List(Category).max(LastEdited), List(Category).count() %> -Note the use of both .max(LastEdited) and .count() - this takes care of both the case where an object has been edited -since the cache was last built, and also when an object has been deleted/un-linked since the cache was last built. +
+Note the use of both `.max(LastEdited)` and `.count()` - this takes care of both the case where an object has been +edited since the cache was last built, and also when an object has been deleted since the cache was last built. +
-We can also calculate aggregates on relationships. A block that shows the current member's favourites needs to update -whenever the relationship Member::$has_many = array('Favourites' => Favourite') changes. +We can also calculate aggregates on relationships. A block that shows the current member's favorites needs to update +whenever the relationship `Member::$has_many = array('Favourites' => Favourite')` changes. :::ss <% cached 'favourites', CurrentMember.ID, CurrentMember.Favourites.max(LastEdited) %> ## Cache key calculated in controller -That last example is a bit large, and is complicating our template up with icky logic. Better would be to extract that -logic into the controller +In the previous example the cache key is getting a bit large, and is complicating our template up. Better would be to +extract that logic into the controller. :::php + public function FavouriteCacheKey() { $member = Member::currentUser(); + return implode('_', array( 'favourites', $member->ID, @@ -106,8 +97,7 @@ logic into the controller )); } - -and then using that function in the cache key +Then using that function in the cache key: :::ss <% cached FavouriteCacheKey %> @@ -159,29 +149,28 @@ heavy load: <% cached 'blogstatistics', Blog.ID if HighLoad %> -By adding a HighLoad function to your page controller, you could enable or disable caching dynamically. +By adding a `HighLoad` function to your `Page_Controller`, you could enable or disable caching dynamically. To cache the contents of a page for all anonymous users, but dynamically calculate the contents for logged in members, -you could use something like: + use something like: :::ss <% cached unless CurrentUser %> +## Uncached -As a shortcut, the template tag 'uncached' can be used - it is the exact equivilent of a cached block with an if -condition that always returns false. The key and conditionals in an uncached tag are ignored, so you can easily -temporarily disable a particular cache block by changing just the tag, leaving the key and conditional intact. +Yhe template tag 'uncached' can be used - it is the exact equivalent of a cached block with an if condition that always +returns false. The key and conditionals in an uncached tag are ignored, so you can easily temporarily disable a +particular cache block by changing just the tag, leaving the key and conditional intact. :::ss <% uncached %> -## Nested cacheblocks +## Nested cache blocks -You can also nest independent cache blocks (with one important rule, discussed later). - -Any nested cache blocks are calculated independently from their containing block, regardless of the cached state of that -container. +You can also nest independent cache blocks Any nested cache blocks are calculated independently from their containing +block, regardless of the cached state of that container. This allows you to wrap an entire page in a cache block on the page's LastEdited value, but still keep a member-specific portion dynamic, without having to include any member info in the page's cache key. @@ -217,11 +206,10 @@ could also write the last example as: $ASlowCalculation <% end_cached %> - -## The important rule - +
Currently cached blocks can not be contained within if or loop blocks. The template engine will throw an error letting you know if you've done this. You can often get around this using aggregates. +
Failing example: @@ -236,8 +224,6 @@ Failing example: <% end_cached %> - - Can be re-written as: :::ss @@ -249,4 +235,4 @@ Can be re-written as: <% end_loop %> <% end_cached %> - <% end_cached %> + <% end_cached %> \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/08_Performance/02_Cache_Control.md b/docs/en/02_Developer_Guides/08_Performance/02_Cache_Control.md deleted file mode 100644 index e8a6184f9..000000000 --- a/docs/en/02_Developer_Guides/08_Performance/02_Cache_Control.md +++ /dev/null @@ -1,25 +0,0 @@ -# Cache control - -By default, PHP add caching headers that make the page appear "purely dynamic". -This isn't usually appropriate for most sites, even ones that are updated reasonably frequently. -In particular, the default PHP cache-control settings prevent sites from appearing in the internet archive. -SilverStripe overrides the default settings with the following: - -Default setting: - - * The `Last-Modified` date is set to be most recent modification date of any database record queried in the generation of the page. - * The `Expiry` date is set by taking the age of the page and adding that to the current time. - * `Cache-Control` is set to `max-age=86400, must-revalidate` - * Since a visitor cookie is set, the site won't be cached by proxies - * Ajax requests are never cached. - -Overriding these defaults - - * `[api:HTTP::$cache_age]` can be used to set the max-age component of the cache-control line, in seconds. -Set it to 0 to disable caching; the "no-cache" clause in `Cache-Control` and `Pragma` will be included. It works only for live sites, if `SS_ENVIRONMENT_TYPE` is set to "dev" `[api:HTTP::$cache_age]` will be always overridden with 0. - * `[api:HTTP::register_modification_date()]` can be used to set the modification date to something more recent than the default. - -How it works: - - * `[api:DataObject::__construct()]` calls `[api:HTTP::register_modification_date()]` whenever a record comes from the database - * `Controller::run()` calls `[api:HTTP::add_cache_headers()]` before outputting the page diff --git a/docs/en/02_Developer_Guides/08_Performance/02_HTTP_Cache_Headers.md b/docs/en/02_Developer_Guides/08_Performance/02_HTTP_Cache_Headers.md new file mode 100644 index 000000000..455a3c4e9 --- /dev/null +++ b/docs/en/02_Developer_Guides/08_Performance/02_HTTP_Cache_Headers.md @@ -0,0 +1,33 @@ +title: HTTP Cache Headers +summary: Set the correct HTTP cache headers for your responses. + +# Caching Headers + +By default, PHP adds caching headers that make the page appear purely dynamic. This isn't usually appropriate for most +sites, even ones that are updated reasonably frequently. SilverStripe overrides the default settings with the following +headers: + + * The `Last-Modified` date is set to be most recent modification date of any database record queried in the generation + of the page. + * The `Expiry` date is set by taking the age of the page and adding that to the current time. + * `Cache-Control` is set to `max-age=86400, must-revalidate` + * Since a visitor cookie is set, the site won't be cached by proxies. + * Ajax requests are never cached. + +## Customizing Cache Headers + +### HTTP::set_cache_age + + :::php + HTTP::set_cache_age(0); + +Used to set the max-age component of the cache-control line, in seconds. Set it to 0 to disable caching; the "no-cache" +clause in `Cache-Control` and `Pragma` will be included. + +### HTTP::register_modification_date + + :::php + HTTP::register_modification_date('2014-10-10'); + +Used to set the modification date to something more recent than the default. [api:DataObject::__construct] calls +[api:HTTP::register_modification_date(] whenever a record comes from the database ensuring the newest date is present. diff --git a/docs/en/02_Developer_Guides/08_Performance/03_Profiling.md b/docs/en/02_Developer_Guides/08_Performance/03_Profiling.md new file mode 100644 index 000000000..be25f1298 --- /dev/null +++ b/docs/en/02_Developer_Guides/08_Performance/03_Profiling.md @@ -0,0 +1,13 @@ +title: Profiling +summary: Identify bottlenecks within your application. + +# Profiling + +Profiling is the best way to identify bottle necks and other slow moving parts of your application prime for +optimization. + +SilverStripe does not include any profiling tools out of the box, but we recommend the use of existing tools such as +[XHProf](https://github.com/facebook/xhprof/) and [XDebug](http://xdebug.org/). + +* [Profiling with XHProf](http://techportal.inviqa.com/2009/12/01/profiling-with-xhprof/) +* [Profiling PHP Applications With xdebug](http://devzone.zend.com/1139/profiling-php-applications-with-xdebug/) \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/08_Performance/04_Static_Publishing.md b/docs/en/02_Developer_Guides/08_Performance/04_Static_Publishing.md new file mode 100644 index 000000000..a477334a0 --- /dev/null +++ b/docs/en/02_Developer_Guides/08_Performance/04_Static_Publishing.md @@ -0,0 +1,20 @@ +title: Static Publishing +summary: Export your web pages as static HTML and serve the web like it's 1999. + +# Static Publishing + +One of the best ways to get the top performance out of SilverStripe is to bypass it completely. This saves on any loading +time, connecting to the database and formatting your templates. This is only appropriate approach on web pages that +have completely static content. + +
+If you want to cache part of a page, or your site has interactive elements such as forms, then +[Partial Caching](partial_caching) is more suitable. +
+ +By publishing the page as HTML it's possible to run SilverStripe from behind a corporate firewall, on a low performance +server or serve millions of hits an hour without expensive hardware. + +This functionality is available through the [StaticPublisher](https://github.com/silverstripe-labs/silverstripe-staticpublisher) +module. The module provides hooks for developers to generate static HTML files for the whole application or publish key +pages (e.g a web applications home page) as HTML to reduce load on the server. \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/08_Performance/Profiling.md b/docs/en/02_Developer_Guides/08_Performance/Profiling.md deleted file mode 100644 index 5deae0b16..000000000 --- a/docs/en/02_Developer_Guides/08_Performance/Profiling.md +++ /dev/null @@ -1,8 +0,0 @@ -#### Profiling - -Profiling is the best way to identify bottle necks and other slow moving parts of your application prime for optimization. SilverStripe -does not include any profiling tools out of the box, but we recommend the use of existing tools such as [XHProf](https://github.com/facebook/xhprof/) -and [XDebug](http://xdebug.org/). - -* [Profiling with XHProf](http://techportal.inviqa.com/2009/12/01/profiling-with-xhprof/) -* [Profiling PHP Applications With xdebug](http://devzone.zend.com/1139/profiling-php-applications-with-xdebug/) \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/08_Performance/index.md b/docs/en/02_Developer_Guides/08_Performance/index.md index 3ec99cb2c..162475d2c 100644 --- a/docs/en/02_Developer_Guides/08_Performance/index.md +++ b/docs/en/02_Developer_Guides/08_Performance/index.md @@ -1,8 +1,14 @@ title: Performance summary: Make your applications faster by learning how to write more scalable code and ways to cache your important information. +introduction: Make your applications faster by learning how to write more scalable code and ways to cache your important information. -[CHILDREN] +The following guide describes the common ways to speed your SilverStripe website up. The general rules for getting +the best performance out of SilverStripe include running the latest versions of PHP alongside a +[opcode](http://en.wikipedia.org/wiki/Opcode) cache such as [XCache](http://xcache.lighttpd.net/) or +[APC](http://nz2.php.net/manual/en/intro.apc.php). -## How-to +If you're running shared hosting, make sure your host meets the minimum system requirements and has activated one of the +PHP opcode caches to achieve the best results for your application. Once your hardware is performing it's best, dig +into the guides below to see what you can do. -[CHILDREN How_To] \ No newline at end of file +[CHILDREN Exclude=How_Tos] \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/17_CLI/index.md b/docs/en/02_Developer_Guides/17_CLI/index.md index 985816560..56d0709c8 100644 --- a/docs/en/02_Developer_Guides/17_CLI/index.md +++ b/docs/en/02_Developer_Guides/17_CLI/index.md @@ -1,13 +1,13 @@ +title: Command Line Interface summary: Automate SilverStripe, run Cron Jobs or sync with other platforms through the Command Line Interface. +introduction: Automate SilverStripe, run Cron Jobs or sync with other platforms through the Command Line Interface. -# Command Line +SilverStripe can call [Controllers](../controllers) through a command line interface (CLI) just as easily as through a +web browser. This functionality can be used to automate tasks with cron jobs, run unit tests, or anything else that +needs to interface over the command line. -SilverStripe can call [controllers](../controllers) through a command line interface (CLI) just as easily as through a -web browser. This can be used to automate tasks with cron jobs, run unit tests, or anything else that needs to interface -over the command line. - -The main entry point for any command line execution is `cli-script.php`. For example, to run a database rebuild -from the command line, use this command: +The main entry point for any command line execution is `framework/cli-script.php`. For example, to run a database +rebuild from the command line, use this command: :::bash cd your-webroot/ @@ -19,76 +19,86 @@ more). This can be a good thing, your CLI can be configured to use higher memory to have. -## Sake: SilverStripe Make +## Sake - SilverStripe Make Sake is a simple wrapper around `cli-script.php`. It also tries to detect which `php` executable to use if more than one are available.
-If you are using a debian server: Check you have the php-cli package installed for sake to work. If you get an error +If you are using a Debian server: Check you have the php-cli package installed for sake to work. If you get an error when running the command php -v, then you may not have php-cli installed so sake won't work.
### Installation -You can copy the `sake` file into `/usr/bin/sake` for easier access (this is optional): +`sake` can be invoked using `./framework/sake`. For easier access, copy the `sake` file into `/usr/bin/sake`. cd your-webroot/ sudo ./framework/sake installsake
-This currently only works on UNIX-like systems, not on Windows. +This currently only works on UNIX like systems, not on Windows.
### Configuration -Sometimes SilverStripe needs to know the URL of your site, for example, when sending an email or generating static -files. When you're visiting your site in a web browser this is easy to work out, but if you're executing scripts on the -command line, it has no way of knowing. - -To work this out, you should add lines of this form to your [_ss_environment.php](/getting_started/environment_management) -file. +Sometimes SilverStripe needs to know the URL of your site. For example, when sending an email or generating static +files. When you're visiting the site in a web browser this is easy to work out, but when executing scripts on the +command line, it has no way of knowing. To work this out, add lines to your +[_ss_environment.php](/getting_started/environment_management) file. :::php global $_FILE_TO_URL_MAPPING; $_FILE_TO_URL_MAPPING['/Users/sminnee/Sites'] = 'http://localhost'; -The above statement tells SilverStripe that anything executed under the `/Users/sminnee/Sites` directly will have the -base URL `http://localhost`. +The above statement tells SilverStripe that anything executed under the `/Users/sminnee/Sites` directory will have the +base URL `http://localhost`. The site `/Users/sminnee/Sites/my_silverstripe_project` will translate to the URL +`http://localhost/my_silverstripe_project`. -You can add multiple file to url mapping definitions. The most specific mapping will be used. For example: +You can add multiple file to url mapping definitions. The most specific mapping will be used. :::php global $_FILE_TO_URL_MAPPING; $_FILE_TO_URL_MAPPING['/Users/sminnee/Sites'] = 'http://localhost'; - $_FILE_TO_URL_MAPPING['/Users/sminnee/Sites/mysite'] = 'http://mysite.localhost'; + $_FILE_TO_URL_MAPPING['/Users/sminnee/Sites/my_silverstripe_project'] = 'http://project.localhost'; ### Usage -Sake is particularly useful for running build tasks +Sake can run any controller by passing the relative URL to that controller. + + :::bash + sake / + # returns the homepage + + sake dev/ + # shows a list of development operations + +Sake is particularly useful for running build tasks. :::bash - cd /your/site/folder sake dev/build "flush=1" + +Or running unit tests.. + + :::bash sake dev/tests/all -It can also be handy if you have a long running script. +It can also be handy if you have a long running script.. :::bash - cd /your/site/folder sake dev/tasks/MyReallyLongTask ### Running processes -You can use sake to make daemon processes for your application. +`sake` can be used to make daemon processes for your application. -Step 1: Make a task or controller class that runs a loop. To avoid memory leaks, you should make the PHP process exit -when it hits some reasonable memory limit. Sake will automatically restart your process whenever it exits. +Make a task or controller class that runs a loop. To avoid memory leaks, you should make the PHP process exit when it +hits some reasonable memory limit. Sake will automatically restart your process whenever it exits. -Step 2: Include some appropriate sleep()s so that your process doesn't hog the system. The best thing to do is to have -a short sleep when the process is in the middle of doing things, and a long sleep when doesn't have anything to do. +Include some appropriate sleep()s so that your process doesn't hog the system. The best thing to do is to have a short +sleep when the process is in the middle of doing things, and a long sleep when doesn't have anything to do. This code provides a good template: @@ -115,22 +125,21 @@ This code provides a good template: } } -Step 3: Install the "daemon" command-line tool on your server. - -Step 4: Use sake to start and stop your process +Then the process can be managed through `sake` :::bash sake -start MyProcess sake -stop MyProcess + +
-Sake stores Pid and log files in the site root directory. +`sake` stores `pid` and log files in the site root directory.
-## GET parameters as arguments +## Arguments -You can add parameters to the command by using normal form encoding. All parameters will be available in `$_GET` within -SilverStripe. Using the `cli-script.php` directly: +Parameters can be added to the command. All parameters will be available in `$_GET` array on the server. :::bash cd your-webroot/ @@ -143,8 +152,10 @@ Or if you're using `sake` ## Running Regular Tasks With Cron -On a UNIX machine, you can typically run a scheduled task with a [cron job](http://en.wikipedia.org/wiki/Cron). You can -execute any `BuildTask` in SilverStripe as a cron job using `Sake`. +On a UNIX machine, you can typically run a scheduled task with a [cron job](http://en.wikipedia.org/wiki/Cron). Run +`BuildTask` in SilverStripe as a cron job using `sake`. + +The following will run `MyTask` every minute. :::bash * * * * * /your/site/folder/sake dev/tasks/MyTask