Why bootdev — Caching

Cache structure

For an LNMP structure, you probably will have 3 layers of caching to protect your servers from hitting too much.

Page cache: MicroCache / Varnish / Cloudfront Dynamic Content

PHP Cache: APC / OpsCache (PHP 5.5+)

in memory cache: ElastiCache (memcache) / ElastiCache (Redis)

In BootDev, we choose MicroCache + APC + memcache. It is a traditional approach here. We have REVOLUTION plan, that use mostly Cloudfront Dynamic Content service, but that is still experimental. Let’s put that later.

Page cache

Cache invalidation sucks. To advoid this, we use microcache buildin nginx rather than using Varnish. With Varnish, of coz you can speed up per every single request. But, you need to invalidate cache when content update. But with Microcache, what we do is expire the content every 40s, so that when the SECOND+ user hit the same page, he will spend up. We only render pages at the first request, so that we use less memory, but same performance if your site traffic is enough.

If you want to make sure every request HIT cache, we suggest to add cache-warmer, which is build in @bootdev management machine (CHEF Server) too.

Here are some basic config Reference: https://github.com/perusio/drupal-with-nginx

fastcgi_cache_path /dev/shm/microcache levels=1:2 keys_zone=MYAPP:5M max_size=256M inactive=2h;
fastcgi_cache_key “$scheme$request_method$host$request_uri”;
add_header X-Cache $upstream_cache_status;
map $http_cookie $cache_uid {
default nil; # hommage to Lisp🙂
~SESS[[:alnum:]]+=(?<session_id>[[:alnum:]]+) $session_id;
}
map $request_method $no_cache {
default 1;
HEAD 0;
GET 0;
}

#Cache everything by default
set $no_cache 0;
#Don’t cache POST requests
if ($request_method = POST)
{
set $no_cache 1;
}
#Don’t cache if the URL contains a query string
if ($query_string != “”)
{    set $no_cache 1;
}
#Don’t cache the following URLs
if ($request_uri ~* “/(administrator/|login.php)”)
{
set $no_cache 1;
}
#Don’t cache if there is a cookie called PHPSESSID
if ($http_cookie = “PHPSESSID”)
{
set $no_cache 1;
}

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have “cgi.fix_pathinfo = 0;” in php.ini
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php-fpm-www.sock;
fastcgi_read_timeout 40;
fastcgi_cache MYAPP;
fastcgi_cache_valid 200 301 30s;
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

# Set cache key to include identifying components
fastcgi_cache_valid 302     1m;
fastcgi_cache_valid 404     1s;
fastcgi_cache_min_uses 1;
fastcgi_cache_use_stale error timeout invalid_header updating http_500;
fastcgi_ignore_headers Cache-Control Expires;
fastcgi_pass_header Set-Cookie;
fastcgi_pass_header Cookie;

## Add a cache miss/hit status header.
add_header X-Micro-Cache $upstream_cache_status;

## To avoid any interaction with the cache control headers we expire
## everything on this location immediately.
expires epoch;

## Cache locking mechanism for protecting the backend of too many
## simultaneous requests.
fastcgi_cache_lock on;
}

The first few lines of code is to tell where to store cache, and the limit. Also, try to handle auth user. (It need details tunning per site) But, in general , above lines of code will be enough.

Remember to have    #NOTE: You should have “cgi.fix_pathinfo = 0;” in php.ini

PHP Cache

For code cache, we use APC. And later will switch to OpsCache, as APC for PHP 5.4- OpsCache for PHP 5.5+

Share some of the php-fpm.d/www.conf . It really depends on your server resource, calculation and loading test result.

listen = /var/run/php-fpm-www.sock #using .sock over port 9001 will be faster, if your php server and nginx are on the same server, it will be ok.

pm = dynamic

pm.max_children = 50
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 10

request_terminate_timeout = 40

In php.ini we have

memory_limit = 256M

Config above is base on loading test result that we enabled APC and also run under EC2 m3.large with 2000 users from blazemeter.

In memory cache

I see some people will now use Redis over memcache. For bootdev, we use memcache as it is fast and simple enough.

For redis, you can have more advance features like query it with noSQL. But, we don’t need to use all these as in memory cache for Drupal is just use to cache block / views / other things from cache-api. You should install php-memcache library too.

Add below to Drupal with Drupal memcache module, so that you can have memcache backend.

$conf[‘cache_backends’][] = ‘sites/all/modules/contrib/memcache/memcache.inc’;
$conf[‘cache_default_class’] = ‘MemCacheDrupal’;
$conf[‘cache_class_cache_form’] = ‘DrupalDatabaseCache’;
$conf[‘memcache_key_prefix’] = ‘your_prefix’;
$conf[‘memcache_servers’] = array(
‘yourCacheNode:11211’ => ‘default’,
‘yourCacheNode:11211’ => ‘others’,
);

$conf[‘memcache_bins’] = array(
‘cache’ => ‘default’,
‘cache_block’ => ‘others’,
‘cache_content’ => ‘others’,
‘cache_filter’ => ‘others’,
‘cache_form’ => ‘others’,
‘cache_menu’ => ‘others’,
‘cache_page’ => ‘others’,
‘cache_update’ => ‘others’,
‘cache_views’ => ‘others’,
‘cache_views_data’ => ‘others’,
);

You can set different type of cache to different cache node. Of coz, you need to prepare cache node in AWS first. And that is why bootdev, one click for both server architect and relative configurations🙂

Now you have better understanding of caching in bootdev or (Drupal + AWS)

One thought on “Why bootdev — Caching

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s