Site icon GetPageSpeed

Deployment and cache purge on Citrus powered servers

Citrus is what we internally call the web software stack of Varnish, Nginx, PHP-FPM and Percona MySQL.

To invalidate PHP OPcache, without restarting PHP-FPM, you want to use cachetool.
The latest branch of cachetool only supports the most recent PHP versions.

Branch 3.x of cachetool supports many PHP versions (5.5 onward, including PHP 7).
To install it:

sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm
sudo yum -y install cachetool

Often times, due to a number of caching layers, the changes to website files do not reflect on the site immediately or do not reflect at all. This is a good thing, aimed to ease the load to PHP backend and increase performance and total traffic throughput.

The caching layers may include:

A simple GitHub hook for deployment and cache clearing may look like this:

<?php

// Github will post secret code to your hook script and we have to check for it
if (!isset($_GET['code']) && $_GET['code'] != 'replace-me-with-random-password') {
        exit('404');
}

$result = exec('git pull') 
        . exec('../artisan migrate') 
        . exec('cd ../ && composer install') 
        . exec('curl -X PURGE -H "User-Agent: W3 Total Cache/.*" http://www.example.com/.*')   
        . opcache_reset()
        . clearstatcache(true);

Script logic

git pull fetches files from remote git origin.

../artisan migrate runs database migrations for Laravel website.

cd ../ && composer install for a site using Composer and not tracking track vendor directory in git, install PHP libraries

curl -X PURGE -H "User-Agent: W3 Total Cache/.*" http://www.example.com/.*' This will purge complete cache for domain in Varnish

clearstatcache(true) clears real path PHP cache

Finally, opcache_reset() clears PHP opcode cache

Branch-based development

You may also have set up staging and master branches on respective domains. Your deployment hook can be able to act only when a specific branch is being merged.
For this, you can use branch named passed in the web hook, e.g. provided by BitBucket:

if ($payload && $branch == $payload->push->changes[0]->new->name) {

A further improvement may be putting up all the clear and deployment commands in a single deploy.sh file which is run by the webhook in the background.
So you can set up a $branch variable that is obtained either from shell environment or included from a gitignored file. Its value may be staging or master to match up with the git branch having been pushed to:

if ($payload && $branch == $payload->push->changes[0]->new->name) {
    $deployCmd = 'deploy.sh';
    exec('bash -c "exec nohup setsid ./' . $deployCmd . ' > ./deploy.log 2>&1 &"');
}

This truly detaches from any SSH sessions and runs $deployCmd in the background.

The deploy.sh may look like the following for a Magento 1.x website:

#!/bin/bash

/usr/bin/git pull
/usr/local/bin/cachetool stat:clear
/usr/local/bin/cachetool opcache:reset
n98-magerun cache:flush

Some things are important to remember:

The URL in Varnish cache purge should be plain HTTP, not HTTPS since communication to Varnish always happens over HTTP.
The call to opcache_reset() should only be done in a webhook and not directly on the command line. Calling it directly on the command line will not affect opcache data of PHP-FPM pools and thus the cache will stay intact.

If you want to clear PHP opcache and realpath caches on the command line, use the following:

/usr/local/bin/cachetool opcache:reset
/usr/local/bin/cachetool stat:clear

For a complete integration, take a look at GitHook.

Purge cache in Prestashop

<?php

chdir(dirname(__FILE__));

echo 'Clearing File Cache' . '<br />';
shell_exec('rm -rf ./cache/cachefs');

echo 'Clearing Smarty Caches' . '<br />';
shell_exec('rm -rf ./cache/smarty/cache');
shell_exec('rm -rf ./cache/smarty/compile');
shell_exec('rm -rf ./cache/class_index.php');

echo 'Clearing filestat cache' . '<br />';
clearstatcache();
clearstatcache(true);

echo 'Clearing PHP files cache (opcache)' . '<br />';
opcache_reset(); 

require_once 'config/settings.inc.php';

echo 'Clearing smarty cache database tables' . '<br />';

/* Connect to a MySQL database using driver invocation */
$dsn = 'mysql:dbname=' . _DB_NAME_ . ';host=' . _DB_SERVER_;

try {
    $conn = new PDO($dsn, _DB_USER_, _DB_PASSWD_);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

$conn->query(sprintf("TRUNCATE TABLE %ssmarty_cache", _DB_PREFIX_));
$conn->query(sprintf("TRUNCATE TABLE %ssmarty_last_flush", _DB_PREFIX_));
$conn->query(sprintf("TRUNCATE TABLE %ssmarty_lazy_cache", _DB_PREFIX_));

echo 'Done';
Exit mobile version