Long-running PHP processes

Giorgio Sironi

If you are looking at these slides on your pc, there are speaker notes in the HTML

Giorgio Sironi (@giorgiosironi)

I work for eLife Sciences
Italy European Union
  • Software Engineer in Test (automates stuff for a living)
  • What do I do
    • Distributed systems
    • Automated complex tests, integrating many different projects
    • Continuous Delivery
    • Pasta and risotto

(Main) PHP SAPIs

The type of PHP SAPIs and how the CLI one is used by all long-running processes

Differentiation

CLI category Running time STDIN STDOUT
Script seconds, minutes sometimes always
Cron seconds (best), minutes no no
Long-running process minutes, hours, days no no

Quick examples

  • RabbitMQ/ZeroMQ/SQS consumer
  • Gearman worker
  • Database projection generator

Essential characteristics

  • runs for hours, and it's supposed to
  • instantiated multiple times for parallelism
  • child of a daemon instead of a web server

PHP?!

Not exactly built for this (non-exhaustive list)

  • multiprocess is not memory efficient
  • no standard supervision system or framework
  • not much fault tolerance (Fatal Error)

PHP!

  • your application is already written with it
  • you use libraries written in PHP (open source and private)
  • deployment tooling is already in place
  • you know the language
  • ability to move existing functionality into the background

Minimal structure


#!/usr/bin/env php
require_once 'bootstrap.php';
while (!$shouldStop()) {
    $item = $blockingCall($timeout = 20);
    $doWork($item);
    $commit($item);
}
                      

Problems: supervising (1)

  • Is the process still alive?
  • If it's crashed, we should restart it
  • There's a Class 'X' not found, we should stop restarting it

Problems: supervising (2)

  • Supervisord (everywhere)
  • Upstart (e.g. Ubuntu 14.04)
  • Systemd (e.g. Ubuntu 16.04)

Problems: stopping


$flag = false;
pcntl_signal('SIGTERM', function () use (&$flag) {
    $flag = true;
});
$shouldStop = function() use (&$flag) {
    pcntl_signal_dispatch();
    return $flag;
}
                      

Problems: memory


# php.ini memory_limit = -1
$shouldStop = function() use ($maximum) {
    return memory_get_usage(true) > $maximum;
}
                      

Problems: global state


                      global $db;
                      CustomerSingleton::instance();
                      $entityManager->close();
                      

Problems: bottlenecks

(in order of appearance)

  • Single threading ...create more than one instance
  • Memory ...horizontal scaling with more servers
  • CPU ...horizontal scaling too
  • Database you're reading/writing ...the usual profiling
  • External resources (e.g. rate limiting) ...congratulations

Tools

  • ps afuxww
  • New Relic APM and Server
  • Symfony Console and your own libraries

Resources

Conclusions

  • PHP long-running processes work well if you carefully work on the operations, not just development
  • Know your limits: performance and efficiency
  • Cohesion and simplicity of your application win over technological terror

Thanks!

I work for eLife Sciences

@giorgiosironi @eLife

g.sironi@elifesciences.org

https://joind.in/talk/b89dd