APC Settings: stat and shm_size

Most of you already know that APC is really important to boost PHP performance. It will probably be replaced in the next months / years by Zend Optimizer+ but meanwhile it is important to know how to properly tweak this amazing PHP extension.

If we have a look at http://php.net/manual/en/apc.configuration.php we will see in the first couple of lines that both stat and shm_size are the most important directives but most people seem to not know about them, and this is why I am writing this post. Yes, most sysadmins don’t even know that these settings exist and the big difference you can make playing with them.

shm_size: This is about how much memory will be reserved for APC. Please take into account that this memory is occupied both by your userland variables and by the opcode cache. So, the more PHP files your application has, the more memory you need. Thanks God, well thanks Rasmus, there is a PHP script to inspect how much memory we are actually using. You can grab it at http://svn.php.net/viewvc/pecl/apc/trunk/apc.php?view=markup. And of course, in modern big PHP applications, with lots of libraries, dependencies, frameworks, etc.. it should be set at least at 64M and probably at 128M. Just inspect it in your production servers and you will find the most appropiate value.

stat: This setting is a really cool one. By default, every time we require a file in PHP, if it is stored in APC, it looks at the filesystem stat to see if the file has changed to load a new version in memory. Of course, this is really inefficient and it is much better to set apc.stat to Off. This way you can literally rm your application folder and all files cached in APC will still work! Of course, this also helps doing deploys even in big traffic moments. The only drawback is that you need to wipe APC (via Apache reload or similar) after every deploy so that the new code actually is installed. But the benefits are far more interesting that this minor drawback! And I honestly hope you are no longer editing remote files with Vim to quick fix Friday afternoon bugs :)

So, quite a short post, but I think it is worth it, provided there are already quite a bunch of companies where I had to fix this!

APC vs Zend Optimizer+ Benchmarks with Symfony2

If you are following some of the biggest names in the PHP scene you may have seen that Zend Optimizer+ is now open-source on Github at https://github.com/zend-dev/ZendOptimizerPlus and it seems that will be included in PHP 5.5 distribution.

This is awesome news as we will finally get a PHP Accelerator bundled with the main language distribution and there should not be (let’s hope) weird issues like the ones happening in early versions of PHP 5.4 with APC that has prevented massive adoption of the new stable version of our favorite language.

There are some really promising performance tests available here but at the time of writing this post there are neither Symfony2 nor Zend Framework 2 benchmarks so I decided to grab the extension and do my own performance tests.

So… let’s see the results

Hardware and software used

What is tested? Basically the simplest application you can develop with Symfony2 (Version 2.2.0).
Composer was run with -o flag which, as you should know, optimizes autoload creating a classmap (except the test which is said that it war ran without it)

Machine used is my laptop, basically a Macbook Air with 1.8 Ghz i5 processor, 8Gb RAM and SSD HD

PHP version is 5.4.12, APC is 3.1.13 and OS is MacOSx Mountain Lion

And the tests are run with Apache Benchmark against a Macports default Apache installation with these settings:

ab -n 5000 -c 10 <symfony2_url>

A Symfony2 Hello world

I’ve seen some “Hello world” performance tests around the Internet using Twig but I guess the fairest test for a Symfony2 minimal footprint is some controller like this one:

<?php
 
namespace RicardClau\Bundle\BenchmarkBundle\Controller;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Response;
 
class DefaultController extends Controller
{
    /**
     * @Route("/benchmark", name="benchmark")
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function benchmarkAction()
    {
        return new Response('APC vs ZO+');
    }
}

Results with Xdebug

PHP 5.4.12 without APC, but with composer optimization

~ 37 req / s (really poor performance)

PHP 5.4.12 with APC 3.1.13 no custom, but without composer autoload optimization

~ 155 req / s

PHP 5.4.12 with APC 3.1.13 no custom

~ 180 req / s (nice boost with composer autoload optimization!)

PHP 5.4.12 with APC 3.1.13 and apc.stat = Off

~ 195 req / s

PHP 5.4.12 with APC 3.1.13, APC Universal ClassLoader and apc.stat = Off

~ 195 req / s (almost same performance if you use composer autoload optimizer and stat is set to off)

PHP 5.4.12 with Zend Optimizer+ no custom

~ 211 req / s

PHP 5.4.12 with Zend Optimizer+ recommended settings

zend_optimizerplus.memory_consumption=128
zend_optimizerplus.interned_strings_buffer=8
zend_optimizerplus.max_accelerated_files=4000
zend_optimizerplus.revalidate_freq=60
zend_optimizerplus.fast_shutdown=1
zend_optimizerplus.enable_cli=1

~ 220 req / s

PHP 5.4.12 with Zend Optimizer+ extreme settings

All of the above plus
zend_optimizerplus.save_comments=0
zend_optimizerplus.enable_file_override=1

~ 202 req / s (which is really weird to be lower actually but I’ve tested it many times and seems to happen in my setup)

Please note that to make this last test work properly, you should have your Symfony2 cache warmed before the test if you are using annotations. Otherwise these settings will make your annotations get ignored!

Results WITHOUT Xdebug

(thx to @pborreli suggestion)

PHP 5.4.12 without APC, but with composer optimization

~ 40 req / s (really poor performance)

PHP 5.4.12 with APC 3.1.13 no custom, but without composer autoload optimization

~ 249 req / s

PHP 5.4.12 with APC 3.1.13 no custom

~ 260 req / s (nice boost with composer autoload optimization but less impressive than before!)

PHP 5.4.12 with APC 3.1.13 and apc.stat = Off

~ 280 req / s (still nice boost)

PHP 5.4.12 with APC 3.1.13, APC Universal ClassLoader and apc.stat = Off

~ 280 req / s (almost same performance again if you use composer autoload optimizer and stat is set to off)

PHP 5.4.12 with Zend Optimizer+ no custom

~ 307 req / s

PHP 5.4.12 with Zend Optimizer+ recommended settings

zend_optimizerplus.memory_consumption=128
zend_optimizerplus.interned_strings_buffer=8
zend_optimizerplus.max_accelerated_files=4000
zend_optimizerplus.revalidate_freq=60
zend_optimizerplus.fast_shutdown=1
zend_optimizerplus.enable_cli=1

~ 337 req / s

PHP 5.4.12 with Zend Optimizer+ extreme settings

All of the above plus
zend_optimizerplus.save_comments=0
zend_optimizerplus.enable_file_override=1

~ 312 req / s (again slower)

Some Excel graphics

APC vs Zend Optimizer Plus in Symfony2

Conclusions

First of all, I would like to say that these results must be taken with a pinch of salt as they were made in my personal laptop which is NOT a production server. Operating System is MacOSx so they might differ a little bit on a Linux machine prepared for production.

Having said that, there are some interesting facts:

- With APC, Symfony2 performance is dramatically boosted!. What would we do without PHP Accelerators? Thus, the importance of having an accelerator bundled with the standard distribution

- With composer autoload optimization we might get a benefit of ~20-25% even with a small test like this. Don’t forget it in your production environments!. However, disabling xDebug made this benefit get reduced to ~10%.

- With apc.stat set to Off, we get an extra boost of ~8-10% even in such a small test. This gets bigger when the amount of files opened increases. Don’t forget to consider it. However, remember that whenever a change is uploaded you should reset APC so that your changes are noticed.

- Xdebug make things SLOW. However, most of the % are similar, except maybe the performance boost with composer autoloader optimizer. Please check that you have not it enabled in your production servers!

- Zend Optimizer+ is faster than APC, even with the default installation. With recommended settings and in the best scenario we get our requests to be 10 times faster than PHP with no Accelerator!

- With recommended settings we get a performance boost of ~11% with xDebug and of ~15% without it, if we compare with APC best performance, but there seems to be something wrong with the extreme settings which were getting worse performance. This was consistent in 5 test I did in different moments, so it seems we can discard that there might have been some background process consuming CPU.

- Is it worth to switch? Well, it depends, at least at the moment of writing these lines. If you are not using APC as a local storage cache, it seems that you will get an extra boost for free, which is always good. Also, if it is bundled with the standard distribution, there is one less PECL package to get into account for automated machine deploys. However, if you are using APC as local storage in your application, I don’t think the performance boost is worth if we’re losing the easy caching that APC provides.

- As Lukas Smith states in his comment, there is some work being done with ApcU (which can be seen at https://github.com/krakjoe/apcu) which will be APC minus the byte choice cache. If they make it, with the launch of PHP 5.5 we will have an optimizer bundled with PHP and we will still be able to install ApcU. This will allow us to benefit of the performance boost in ZO+ while not losing APC storage and upload files hook. It definitely seems PHP 5.5 will be another amazing version of our favorite language with Generators and Zend Optimizer+ being the biggest improvements!

Please, feel free to add any comments to these tests!

Testing Symfony2 Commands – Mocking the DI Container with Mockery

Some months ago I had the pleasure to contribute to phpcassa library, maintained by Tyler Hobbs, adding support for Composer and Travis, and fixing some issues with PHP5.4 and some E_STRICT warnings.

And, as there is no Symfony2 bundle for Cassandra yet, I have decided to create one myself. The bundle is at early stages but I have already developed some basic Administration commands, mainly to create a Keyspace, a ColumnFamily, etc… and I have decided to share here how I have unit-tested those commands.

Let’s be honest, PHPUnit is the de-facto standard for Unit Testing, we owe a lot to this amazing library but its mocking framework is a bit cumbersome and limited. And because of that, I’ve been using Mockery for some time and I am pretty happy about it.

So, let’s see how can unit test a Symfony2 Command with the help of Mockery! Let’s forget about pure TDD, we will start showing the Create Keyspace Command and afterwards we will see how to test it.

<?php
 
namespace ADR\Bundle\CassandraBundle\Command;
 
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use phpcassa\SystemManager;
 
class CassandraCreateKeyspaceCommand extends ContainerAwareCommand
{
    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    private $input;
 
    /**
     *
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    private $output;
 
    /**
     * {@inheritDoc}
     */
    protected function configure()
    {
        parent::configure();
        $this
            ->setName('cassandra:keyspace:create')
            ->setDescription('Creates the configured keyspace in selected cluster')
            ->addArgument('client', InputArgument::REQUIRED, 'The client name in Symfony2 where keyspace will be created')
            ->addArgument('keyspace', InputArgument::REQUIRED, 'The keyspace name')
            ->setHelp(<<<EOT
The <info>cassandra:keyspace:create</info> command creates the configured keyspace in the selected cluster.
 
<info>app/console cassandra:keyspace:create [client] [keyspace]</info>
EOT
            );
    }
 
    /**
     * {@inheritDoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;
 
        $keyspace = $input->getArgument('keyspace');
 
        $manager = $this->getContainer()->get('cassandra.' . $input->getArgument('client') . '.manager');
        $manager->create_keyspace($keyspace, array());
 
        $output->writeln('<info>Keyspace ' . $keyspace . ' successfully created at ' . $manager->getServer() . '</info>');
    }
}

In this bundle, via its Dependency Injection Extension file, a ‘cassandra.xxx.manager’ service is created (being xxx how we are naming that cassandra cluster) and this service is basically a wrapper for phpcassa\SystemManager class. Details on how to do it can be found here

But how can we unit test this command? Lots of widely used bundles do not test commands and yes, to be honest, testing this does not add big value but it can still work for a small Container mocking example.

So, to unit test the command, we could do something like this:

<?php
 
namespace ADR\Bundle\CassandraBundle\Tests\Command;
 
use Symfony\Component\Console\Application;
use ADR\Bundle\CassandraBundle\Command\CassandraCreateKeyspaceCommand;
use Symfony\Component\Console\Tester\CommandTester;
use Mockery as m;
 
class CassandraCreateKeyspaceCommandTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider getNonInteractiveData
     */
    public function testCreateKeyspaceCommand($input)
    {
        $application = new Application();
        $application->add(new CassandraCreateKeyspaceCommand());
 
        $command = $application->find('cassandra:keyspace:create');
        $command->setContainer($this->getMockContainer($input));
 
        $tester = new CommandTester($command);
        $tester->execute(
            array_merge(array('command' => $command->getName()), $input)
        );
 
        $this->assertEquals('Keyspace ' . $input['keyspace'] . ' successfully created at #mockServer#' . PHP_EOL, $tester->getDisplay());
    }
 
    private function getMockContainer($input)
    {
        $systemManager = m::mock('phpcassa\SystemManager');
        $systemManager
            ->shouldReceive('create_keyspace')
            ->once()
            ->with($input['keyspace'], array())
        ;
 
        $systemManager
            ->shouldReceive('getServer')
            ->once()
            ->withNoArgs()
            ->andReturn('#mockServer#')
        ;
 
        $container = m::mock('Symfony\Component\DependencyInjection\Container');
        $container
            ->shouldReceive('get')
            ->once()
            ->with('cassandra.' . $input['client'] . '.manager')
            ->andReturn($systemManager)
        ;
 
        return $container;
    }
 
    public function getNonInteractiveData()
    {
        return array(
            array(array('keyspace' => 'fooKeySpace', 'client' => 'barClient')),
        );
    }
}

The most interesting part is that we create a mocked phpcassa\SystemManager and a mocked Symfony\Component\DependencyInjection\Container with the calls and responses we expect from them.

As you can see, Mockery’s fluent interface seems to be more clear and intuitive than PHPUnit and makes it easier to test Symfony2 commands!

I hope you’ve liked this example and I promise to blog more often than these latest months!

Resumen 2012 y Objetivos 2013

Si 2011 fue movido, 2012 ha sido una auténtica locura.

Resumen Laboral

Empecé el año en Ulabox, posiblemente el proyecto web del cual me siento más orgulloso, terminando una larga migración del infame Prestashop a un backend 100% Symfony2. Todo parecía ir bien, pero la crisis no perdona ni a los mejores y el proyecto tuvo una época muy dura con fuertes recortes de personal. Fueron momentos complicados ya que como sabréis todos los que habéis estado en una startup, acabas sintiendo que el proyecto es como un pequeño hijito y creedme que fue muy duro tenerlo que dejar.

A pesar de todo esto, me alegra mucho ver que el proyecto no solo ha conseguido aguantar sino que goza de una salud inmejorable, con un stock creciente cada mes, volumen de pedidos aumentando y en definitiva, parece que tenemos Ulabox para rato. Bien por ellos, que lo suyo les ha costado! :)

Terminada la etapa de Ulabox, y tras una ronda bastante interesante de entrevistas, el resumen de la cual podéis leer aquí decidí aceptar la oferta de Emagister, donde me volvería a reunir con mi viejo compañero de fatigas en Privalia theUnic y por fin trabajaría con el gran Carlos Buenosvinos.

No puedo pasar por alto la increíble recibida que tuve por parte de todo el equipo y sobretodo el nivel técnico y humano de todos los profesionales que trabajan en Emagister. Fueron unos meses muy felices, donde aprendí muchísimo de testing, de Scrum y de buenas prácticas en general.

Y cuando todo parecía que iba a durar mucho tiempo un viejo compañero de los tiempos de consultoría en Ricoh contactó conmigo ya que en SocialPoint buscaban gente con experiencia en Symfony2. Todo empezó siendo una visita de cortesía por aquello de quedar bien con mi viejo compañero pero la verdad es que se me presentó un proyecto extremadamente atractivo en todos los sentidos.

Tras un par de entrevistas, tuve que tomar la que posiblemente haya sido la decisión profesional más difícil de mi vida, dejar un entorno inmejorable para un desarrollador como Emagister, donde además llevaba poco más de 3 meses, para asumir un rol que nunca había tenido, en una empresa aún en construcción, en plena locura de crecimiento, teniendo que liderar un equipo al cual no conocía y con un reto inmenso por delante.

Y de momento, ahí seguimos. Actualmente tengo el cargo de Tech Lead de un equipo de 12 personas, algunos de ellos de sobra conocidos en la comunidad PHP-Symfony2 y tenemos proyectos tan bestias como DragonCity, donde hemos superado los 4,5 millones de jugadores al día y tenemos problemas de escalabilidad con los que hasta ahora solo había podido soñar. No todo ha sido un camino de rosas y han habido momentos complicados pero la verdad es que hay muy pocos proyectos donde trabajar con gente tan potente y con retos tan grandes.

Y en cuanto a proyectos personales, decidí dar carpetazo a los proyectos OnlyHouseMusic y PixelAvengers. La verdad es que los foros ya no son lo que fueron, el mundo de la música está como está y el mundillo freelance… para qué contaros :)

Dando charlas

En pleno cambio entre Emagister y SocialPoint, tuve el privilegio de dar una charla en un evento tan importante como deSymfony. No todos los días se tiene la oportunidad de dar una charla delante de 160 personas, compartiendo cartel con gente como Álvaro Videla, Javier Eguiluz, Jordi Llonch… y el mismísimo Fabien Potencier. Fue una experiencia increíble, parece ser que mi charla gustó bastante y la verdad es que espero poder repetir.

El único pequeño lunar fue suspender el examen de certificación de Symfony. La verdad es que con la charla tuve poco tiempo de prepararlo pero no tengo perdón con algunas de las preguntas que fallé. Espero volverlo a intentar y tener algo más de éxito.

Y en octubre, junto con Marc Canaleta, el CTO de SocialPoint, presentamos el proyecto SocialPoint en Betabeers. Para mí fue un gran honor salir a la palestra en un evento tan importante en la comunidad barcelonesa como este y de nuevo lo volví a disfrutar muchísimo.

Total que espero poder seguir dando charlas de vez en cuando en los próximos años.

Revisando objetivos de 2012

- Seguir mejorando en Symfony2 -> DONE!
- Presentar en eventos como deSymfony o la PHP Conference -> DONE!
- Lanzar de una vez la comunidad Symfony-Barcelona -> DONE! (aunque está muy parado)
- Sacarme las certificaciones de MySQL -> PENDING! (aunque el voucher está comprado)
- Intentar que Pixelavengers crezca -> FAIL y CARPETAZO!
- Practicar HTML5, CSS3 y JavaScript que la verdad es que cada vez lo llevo peor -> FAIL TOTAL!

Objetivos 2013

- Seguir mejorando en Symfony2
- Presentar en algún evento fuera de España
- Sacarme las certificaciones de MySQL (o se me caducará el voucher)
- Sacarme la certificación de Symfony
- Aprender algo de Python y Erlang
- Lo de Front-End… lo doy por imposible… bueno va aprender Twitter Bootstrap :)

How to log messages to different files with Monolog in Symfony2.0

If you are logging lots of activity in your Symfony2.0 application you might have seen that it is not that obvious to split them into different files.

This has been solved in Symfony2.1, thanks to MonologBundle channels (Cookbook here) but if you are in Symfony2.0 and need to handle different log files this is the way to do so:

You’ll have to define a new Logger service and a Handler service for that logger. And this can be done in any of your configuration files under the services key like this:

services:
    special_logger:
        class: Symfony\Bridge\Monolog\Logger
        arguments: [special]
        calls:
            - [pushHandler, [@special_handler]]
    special_handler:
        class: Monolog\Handler\StreamHandler
        arguments: [%kernel.logs_dir%/%kernel.environment%.special.log]

And to use it, inside any ContainerAware class

   $this->container->get('special_logger')->info('your message here');

And of course you can inject this new logger service into any of your application services and use it exactly the same way!

And that’s it, a new app/logs/prod.special.log (if you are using default settings) will be created and your special logs will end there!

Hope you liked this small trick!