Skip to content

Commit

Permalink
perf(views): Allow serving assets directly from filesystem
Browse files Browse the repository at this point in the history
BREAKING CHANGES:
Assets must have valid file extensions

We now cache assets by `"$viewtype/$view"`, not `md5("$viewtype|$view")`
which can result in conflicts between cacheable views that don't have
file extensions to disambiguate files from directories.

This requirement makes it possibile for us to serve assets
directly from disk for performance, instead of serving them through PHP.
Just create a symlink from /cache/ to /dataroot/views_simplecache/
and if your web server supports following symlinks, you'll get this benefit.

This also makes it much easier to explore the available cached resources
by navigating to dataroot/views_simplecache and browsing around.

Fixes Elgg#8381
  • Loading branch information
ewinslow committed Jun 30, 2015
1 parent 13afe2e commit c4c5734
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 28 deletions.
9 changes: 5 additions & 4 deletions .gitignore
Expand Up @@ -2,12 +2,13 @@
/engine/settings.php
/.htaccess
/mod/*
composer.lock
composer.phar
/composer.lock
/composer.phar
/cache

# Don't check in third party code
node_modules
vendor
/node_modules
/vendor

# don't ignore bundled plugins
!/mod/aalborg_theme/
Expand Down
12 changes: 12 additions & 0 deletions docs/admin/performance.rst
Expand Up @@ -83,6 +83,18 @@ It is recommended that you do this on your development platform if you are writi
This cache is automatically flushed when a plugin is enabled, disabled or reordered,
or when upgrade.php is executed.

For best performance, you can also create a symlink from ``/cache/`` in your www
root dir to the ``/views_simplecache/`` directory in the data directory you
configured when you installed Elgg:

.. code:: shell
cd /path/to/wwwroot/
ln -s /path/to/dataroot/views_simplecache/ cache
If your webserver supports following symlinks, this will serve files straight off
disk without booting up PHP each time.

System cache
------------

Expand Down
17 changes: 17 additions & 0 deletions docs/guides/upgrading.rst
Expand Up @@ -12,6 +12,23 @@ See the administrator guides for :doc:`how to upgrade a live site </admin/upgrad
From 1.x to 2.0
===============

Cacheable views must have a file extension in their names
---------------------------------------------------------

This requirement makes it possibile for us to serve assets directly
from disk for performance, instead of serving them through PHP.

It also makes it much easier to explore the available cached resources
by navigating to dataroot/views_simplecache and browsing around.

* Bad: ``my/cool/template``
* Good: ``my/cool/template.html``

We now cache assets by ``"$viewtype/$view"``, not ``md5("$viewtype|$view")``,
which can result in conflicts between cacheable views that don't have file extensions
to disambiguate files from directories.



Dropped ``jquery-migrate`` and upgraded ``jquery`` to ^2.1.4
------------------------------------------------------------
Expand Down
17 changes: 13 additions & 4 deletions docs/guides/views.rst
Expand Up @@ -73,10 +73,19 @@ Views as cacheable assets
=========================

As mentioned before, views can contain JS, CSS, or even images.
Cacheable views *cannot* take any ``$vars`` parameters. They also must not change
their output based on who is logged in, the time of day, etc. The simplest way
to make sure your view meets the requirements for cacheable views is to just avoid
using any PHP in them.

Asset views must meet certain requirements:

* They *must not* take any ``$vars`` parameters
* They *must not* change their output based on global state like

* who is logged in
* the time of day

* They *must* contain a valid file extension

* Bad: ``my/cool/template``
* Good: ``my/cool/template.html``

For example, suppose you wanted to load some CSS on a page.
You could define a view ``mystyles.css``, which would look like so:
Expand Down
32 changes: 17 additions & 15 deletions engine/classes/Elgg/Application/CacheHandler.php
Expand Up @@ -72,10 +72,12 @@ public function handleRequest($path) {
$viewtype = $request['viewtype'];

$contentType = $this->getContentType($view);
if (!empty($contentType)) {
header("Content-Type: $contentType", true);
if (empty($contentType)) {
$this->send403("Asset must have a valid file extension");
}

header("Content-Type: $contentType", true);

// this may/may not have to connect to the DB
$this->setupSimplecache();

Expand All @@ -85,7 +87,7 @@ public function handleRequest($path) {
$this->application->bootCore();

if (!\_elgg_is_view_cacheable($view)) {
$this->send403();
$this->send403("Requested view is not an asset");
} else {
echo $this->renderView($view, $viewtype);
}
Expand All @@ -100,30 +102,30 @@ public function handleRequest($path) {
exit;
}

$filename = $config->getVolatile('dataroot') . 'views_simplecache/' . md5("$viewtype|$view");
if (file_exists($filename)) {
$this->sendCacheHeaders($etag);
readfile($filename);
exit;
}

$this->application->bootCore();

elgg_set_viewtype($viewtype);
if (!\_elgg_is_view_cacheable($view)) {
$this->send403();
$this->send403("Requested view is not an asset");
}

$cache_timestamp = (int)$config->get('lastcache');
$lastcache = (int)$config->get('lastcache');

$filename = $config->getVolatile('dataroot') . "views_simplecache/$lastcache/$viewtype/$view";
if (file_exists($filename)) {
$this->sendCacheHeaders($etag);
readfile($filename);
exit;
}

if ($cache_timestamp == $ts) {
if ($lastcache == $ts) {
$this->sendCacheHeaders($etag);

$content = $this->getProcessedView($view, $viewtype);

$dir_name = $config->getDataPath() . 'views_simplecache/';
$dir_name = dirname($filename);
if (!is_dir($dir_name)) {
mkdir($dir_name, 0700);
mkdir($dir_name, 0700, true);
}

file_put_contents($filename, $content);
Expand Down
4 changes: 1 addition & 3 deletions engine/classes/Elgg/Cache/SimpleCache.php
Expand Up @@ -146,8 +146,7 @@ function disable() {
_elgg_services()->datalist->set('simplecache_enabled', 0);
_elgg_services()->config->set('simplecache_enabled', 0);

// purge simple cache
_elgg_rmdir(_elgg_services()->config->getDataPath() . "views_simplecache");
$this->invalidate();
}
}

Expand All @@ -158,7 +157,6 @@ function disable() {
* @return bool
*/
function invalidate() {
_elgg_rmdir("{$this->CONFIG->dataroot}views_simplecache");
mkdir("{$this->CONFIG->dataroot}views_simplecache");

$time = time();
Expand Down
2 changes: 1 addition & 1 deletion views/default/elgg/require_config.js.php
Expand Up @@ -8,7 +8,7 @@
// Require is currently an Elgg shim. Convert it to a RequireJS config
?>
// <script>
require = <?php echo json_encode($amdConfig); ?>;
require = <?php echo json_encode($amdConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ?>;

<?php
// Note we don't process the require() queue yet because it may require('elgg')
Expand Down
2 changes: 1 addition & 1 deletion views/default/languages.js.php
Expand Up @@ -33,4 +33,4 @@
}

?>
define(<?php echo json_encode($translations); ?>);
define(<?php echo json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ?>);

0 comments on commit c4c5734

Please sign in to comment.