Fishtrap

php and other stuff I know

7 February 2013
by nat
0 comments

Ch-Cha-Changes

While we’d all like to be as prodigious as a 1971 David Bowie it is not always possible. Last year things changed for me in a whole load of ways with the birth of my twin daughters. Ever since the first big shock in the ultrasound room things changed in the way I view the world and the people in it. Obviously for me now my home life is the priority and I spend as much time as I can with my three young children. I have also found myself to be less patient. Both with myself and also with other people and their agendas. This may sound like a negative development but to put it bluntly I’m getting on a bit and I think it’s about time I made the world resemble the way I’d like it a bit more.

Thea

At work this led to me becoming increasingly frustrated with the work I was doing. The technical challenges weren’t the sort that I could get enthusiastic over and my personal learning had pretty much ground to a halt. I guess I was still learning some things but working with technologies I didn’t like meant it was not the kind of knowledge I valued. The rut I found myself in was almost certainly the product of my own attitude. But changing your job is much easier than changing your own attitude.

emily

To cut a very long story short I have decided to leave the employ of Inviqa and go across town and work for the good folks of Plusnet. I’ll admit there have been some raised eyebrows among my colleges at inviqa most of whom have worked at Plusnet in the past and left for various reasons. But for me the important thing is it is a change and hopefully a challenge.

8 October 2012
by nat
0 comments

Fork It! Parallel Processing in PHP. PHPNW 2012

So this weekend I gave my first ever conference talk on parallel PHP at PHPNW. I have been to PHPNW as a delegate every year since it started in 2007. It was a really great experience to go as a speaker and thanks to everyone who came to my talk.

The conference was as always really good. Highlights would include the key note from Ade Oshineye. Add John Cleveley talking about his work at BBC news.

20 April 2012
by nat
0 comments

Multiple PHP versions from git

A while back I read Derick Rethans post on how he compiles multiple versions of PHP which he posted back in November 2011. Of the many possible solutions out there this one seemed the most flexible, however, the php project has now moved to git as it its source code management tool so it requires a few changes. Git means it is now faster and a bit simpler to pull down different branches and build from them. Git also has the added benefit that it is possible to do so from your own fork of the PHP repo.

Here is my version of Derick’s bash script. It is tuned for building on a mac or BSD. You many need to fiddle around with the compile flags to get it work on your system. There are also some hard coded paths which will need altering.

#!/bin/sh

VERSION=$1
DEBUG=$2
ZTS=$3
THIRTYTWO=$4
POSTFIX=
EXTRA_FLAGS=

if (test "${DEBUG}" = "nodebug"); then
	POSTFIX="$POSTFIX-nodebug"
else
	EXTRA_FLAGS="$EXTRA_FLAGS --enable-debug"
fi

if (test "${ZTS}" = "zts"); then
	EXTRA_FLAGS="$EXTRA_FLAGS --enable-maintainer-zts"
	POSTFIX="$POSTFIX-zts"
fi

if (test "${THIRTYTWO}" = "32bit"); then
	export CFLAGS="-m32"
	POSTFIX="$POSTFIX-32bit"
fi

PHP_DIR=/Users/nathanielmchugh/nmdev/php/php-src/
cd $PHP_DIR

SCRUBBED=`echo $VERSION | sed 's/[0-9.]*//'`
if (test "${SCRUBBED}" = "dev"); then
	BRANCH=`echo PHP-${VERSION} | sed 's/dev//'`
	git checkout $BRANCH
else
	TAG=`echo php-${VERSION} `
	git checkout $TAG
fi

echo "Building ${VERSION}${POSTFIX} with ($EXTRA_FLAGS)"

make clean
rm -rf configure
./vcsclean
./buildconf --force

if (test "${THIRTYTWO}" = "32bit"); then
	OPTIONS="--disable-all"
else
	OPTIONS="--with-gd --with-jpeg-dir=/opt/local --with-png-dir=/opt/local --with-xpm-dir=/opt/local --with-freetype-dir=/opt/local \
		--with-mysql=mysqlnd --enable-bcmath --with-gmp=/opt/local --with-readline=/opt/local \
		--with-openssl --with-curl --without-esmtp \
		--with-mysqli=mysqlnd --enable-pcntl \
		--enable-memory-limit --with-mcrypt=/opt/local \
		--with-iconv=/opt/local --enable-wddx  \
		--enable-spl --enable-pdo --with-pdo-mysql --with-pdo-sqlite \
		--with-ctype --with-bz2 --enable-mbstring --with-mime-magic \
		--with-xmlrpc --with-zlib --disable-zend-memory-manager --with-esmtp \
		--with-xsl --enable-exif --enable-soap --enable-ftp"
fi

./configure \
--prefix=/usr/local/php/${VERSION}${POSTFIX} ${EXTRA_FLAGS} ${OPTIONS}

make -j 5
make install

Heres an example of using it to build the stable version of php5.4

$sudo ./php-build.sh 5.4.0
$pe 5.4.0
$php - v
PHP 5.4.0 (cli) (built: Apr 20 2012 12:37:52) (DEBUG)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies

22 November 2011
by nat
0 comments

Mysqlnd_qc Query caching in the client

One of the uses for mysqlnd’s plugin architecture that I found most interesting was query caching on the client.

The traditional MySQL Query Cache

The MySQL query cache is a really nice simple feature which helps speed up repeated queries of the same data. A certain portion of memory can be set aside to hold results sets in a look table against the query string. When a SELECT query comes in which the MySQL server has seen before it looks in the query cache and if it has seen the query before it doesn’t touch the table data or indexes instead it simply returns the result set it has in memory which is much faster.  This is particularly useful in web applications where repeated SELECT queries are very common.

Here’s a little script to demo the effect query cache can have

<?php
$mysqli = new mysqli('127.0.0.1', 'root', '', 'war_and_peace');
$mysqli->query('RESET QUERY CACHE');
$querySQL = "SELECT * FROM words WHERE word = 'sausage' ";
$time_start = microtime(true);
if ($results = $mysqli->query($querySQL)) {
 echo "Cold got {$results->num_rows} results in ";
 echo microtime(true) - $time_start,"s",PHP_EOL;
 $results->close();
}
$time_start = microtime(true);
if ($results = $mysqli->query($querySQL)) {
 echo "Server cache got {$results->num_rows} results in ";
 echo microtime(true) - $time_start,"s",PHP_EOL;
 $results->close();
}

Running this script against a completely un-normalised table with the 600 thousand odd words of war and peace (thanks Project Gutenberg) in it gives you

$ php MySQL_QueryCache.php
Cold got 5 results in 0.13019204139709s
Server cache got 5 results in 0.00035595893859863s

Why not put this in the client?

What would make it even faster is if the client did not need to connect to the server at all. If the client had a cache of all queries it performed it could save the time taken for the network round trip and help take load off the server by not even asking the question.

mysqlnd_qc attempts to do this.

There is, however, a major problem though, cache invalidation. In the traditional server based cache a very simple table based cache invalidation algorithm is used. If a query which alters the data in a table is detected all caches of data with that table in the result set are invalidated. This will even work for servers which are part of a replication cluster since the update sql will propagate from the master where the data was updated to the slaves and the slaves’ caches will then be invalidated.

But what if there are multiple clients? One client could update a table while another client still thinks its cache is still valid and so continues to serve it. The situation of two clients is actually relevant even if you don’t have multiple web servers talking to the same database server. It could occur by using something other than PHP and mysqlnd to connect the database. For instance using the command line mysql client (which uses libmysql) any updates made through this would not invalidate other clients query caches.

This problem is seemingly insoluble without some complex mechanism of feeding back to every client which caches are still valid. So the simple answer is to only cache for a few seconds and then only use the caching where slightly stale data is not hugely important.

Therefore by default after installing the extension with a quick

phpize
./configure
make 
sudo make install

(you will need to be using mysqlnd obviously to be able to use it)

You will need to prefix all queries you want to cache with

“/*qc=on*/ SELECT * from some_table”

which for portability can be written as “/*” . MYSQLND_QC_ENABLE_SWITCH . “*/”;

e.g.

<?php
$mysqli = new mysqli('127.0.0.1', 'root', '', 'war_and_peace');
$mysqli->query('RESET QUERY CACHE');
$querySQL = "SELECT * FROM words WHERE word = 'sausage' ";
$time_start = microtime(true);
if ($results = $mysqli->query($querySQL)) {
	echo "Cold got {$results->num_rows} results in ";
 	echo microtime(true) - $time_start,"s",PHP_EOL;
    $results->close();
}
$querySQL = "SELECT * FROM words WHERE word = 'sausage' ";
$time_start = microtime(true);
if ($results = $mysqli->query($querySQL)) {
        echo "Server cache got {$results->num_rows} results in ";
        echo microtime(true) - $time_start,"s",PHP_EOL;
    $results->close();
}
$querySQL = "/*" . MYSQLND_QC_ENABLE_SWITCH . "*/";
$querySQL .= "SELECT * FROM words WHERE word = 'sausage' ";
$mysqli->query($querySQL);
$time_start = microtime(true);
if ($results = $mysqli->query($querySQL)) {
        echo "Client cache got {$results->num_rows} results in ";
        echo microtime(true) - $time_start,"s",PHP_EOL;
    $results->close();
}

Running this script will show an even better speed improvement.

$ php MySQL_QueryCache_mysqlnd_qc.php 
Cold got 5 results in 0.1253068447113s
Server cache got 5 results in 0.00036311149597168s
Client cache got 5 results in 9.8943710327148E-5s

Win Win Win!

This is pretty impressive! This is using the default settings for the length of cache or time to live. The default setting is 30 seconds and it is set with the ini setting mysqlnd_qc.ttl. Of course after our 30 seconds are up the server cache is still there so we will still be able to make use of that. So in a way we can think of mysqlnd_qc as a way of protecting the db server from excessive repeated queries.

More Options

We are using the default handler which behaves in a similar way to the query cache in the mysql server. There are also other backends available such as APC, memcache and sqlite. Sqlite may well be an excellent choice even though using another db may seem a little weird, the reason is that the results sets are in the form of table data which fits well with sqlite. There is also the possibility of creating you own user handler which can use a custom invalidation algorithm there is an example at http://uk3.php.net/manual/en/mysqlnd-qc.set_user_handlers.php