PHP Sessions on MS Azure Redis service

I sure hope you’ll never end up facing this, but in case you do indeed have a PHP application on MS Azure, and you want to use MS Azure Redis service for the session backend, you’ll have to set your session.save_path to something like:

session.save_path='tcp://UNIQUENAME.redis.cache.windows.net:6379?auth=MSHATESYOU4FUNandPROFIT=&timeout=1&prefix=OHNOMS'

Easy enough, unless your auth key happens to contain a + symbol. In that case, your PHP session creation will fail with this error:

# php count.php
PHP Fatal error:  Uncaught exception 'RedisException' with message 'Failed to AUTH connection' in count.php:3
Stack trace:
#0 count.php(3): session_start()
#1 {main}
  thrown in count.php on line 3
PHP Fatal error:  Uncaught exception 'RedisException' with message 'Failed to AUTH connection' in [no active file]:0
Stack trace:
#0 {main}
  thrown in [no active file] on line 0

From redis-cli the authentication was working fine, so it took us a while to debug. It ended up being a problem with the + symbol, and the the quickest solution in our case was to just regen the auth key so it didn’t have a + in it, but I suspect (but I didn’t test this solution) that URLencoding the + as %2B might work as well.

How to keep your XHgui (mongo) database in check

We got XHgui installed on a client’s VM. It’s profiling 1/100 requests, but the Mongo Database size increased rapidly and we started to look into how to keep it in check.

First thing is to gather some data about our database (and collection):

# mongo
MongoDB shell version: 2.6.5
connecting to: test
> show dbs
local   0.078GB
xhprof  3.952GB

> use xhprof
switched to db xhprof

> show collections
results
system.indexes

> db.results.find().count()
48417

We’re dealing with about 48k records, but many of those are useless, profiling of scripts that are already very fast and unworthy of the devs attention, so the first thing we wanted was to remove most of the “noise” from our collection.

The document has this format:

> db.results.findOne()
{
	"_id" : ObjectId("547ef32ec2f6cd9253ec1053"),
	"profile" : {
		[...]
		"main()==>{closure}" : {
			"ct" : NumberLong(1),
			"wt" : NumberLong(27),
			"cpu" : NumberLong(0),
			"mu" : NumberLong(2952),
			"pmu" : NumberLong(0)
		},
		"main()" : {
			"ct" : NumberLong(1),
			"wt" : NumberLong(247),
			"cpu" : NumberLong(0),
			"mu" : NumberLong(8232),
			"pmu" : NumberLong(4880)
		}
	},
	[...]
}

So we want to filter by main() wt (wall time):

> db.results.find().count()
48417
> db.results.find({"profile.main().wt" : {$lt: 1000000}}).count()
48129
> db.results.find({"profile.main().wt" : {$lt: 500000}}).count()
47185
> db.results.find({"profile.main().wt" : {$lt: 100000}}).count()
41694

As you see, the vast majority of the results are below 100 milliseconds of wall time. Since we’re not interested in that many results and we want to trim the database a bit, we made a backup and then performed a remove:

> db.results.remove({"profile.main().wt" : {$lt: 500000}})
WriteResult({ "nRemoved" : 47234 })

At this point, the database stats did look a lot more interesting:

> db.stats()
{
	"db" : "xhprof",
	"collections" : 3,
	"objects" : 1846,
	"avgObjSize" : 65844.85373781149,
	"dataSize" : 121549600,
	"storageSize" : 3583025152,
	"numExtents" : 24,
	"indexes" : 6,
	"indexSize" : 703136,
	"fileSize" : 4226809856,
	"nsSizeMB" : 16,
	"dataFileVersion" : {
		"major" : 4,
		"minor" : 5
	},
	"extentFreeList" : {
		"num" : 0,
		"totalSize" : 0
	},
	"ok" : 1
}

The dataSize parameter is the size of the actual data. The storageSize parameter is the space MongoDB allocated for the data, while the fileSize is the on-disk size of the database (including indexes, etc).

As you can learn from the MongoDB Storage FAQ, Mongo allocates space in chunks: the first chunk is 64Mb, the second 128Mb and so on up to 2Gb. Each file after that is 2Gb in size, as you can see in this output:

[root@ws ~]# ls -lh /var/lib/mongo/
totale 4,1G
drwxr-xr-x 2 mongod mongod 4,0K  3 dic 12:19 journal
-rw------- 1 mongod mongod  64M 23 nov 22:44 local.0
-rw------- 1 mongod mongod  16M 23 nov 22:44 local.ns
-rwxr-xr-x 1 mongod mongod    6 23 nov 22:44 mongod.lock
drwxr-xr-x 2 mongod mongod 4,0K 29 nov 23:12 _tmp
-rw------- 1 mongod mongod  64M  3 dic 12:38 xhprof.0
-rw------- 1 mongod mongod 128M  3 dic 12:38 xhprof.1
-rw------- 1 mongod mongod 256M  3 dic 12:38 xhprof.2
-rw------- 1 mongod mongod 512M  3 dic 12:38 xhprof.3
-rw------- 1 mongod mongod 1,0G  3 dic 12:38 xhprof.4
-rw------- 1 mongod mongod 2,0G  3 dic 12:38 xhprof.5
-rw------- 1 mongod mongod  16M  3 dic 12:37 xhprof.ns

To retrieve the space used by empty chunks, you’ll need to perform a db repair or drop the database alltogether and rebuild it from a backup.

WARNING: the db repair operation is BLOCKING and it requires double the amount of space taken by the files on disk (fileSize: in my case, about 4G).

After removing the records and performing the repair, we went down from 4G to about 200Mb:

> db.repairDatabase()
{ "ok" : 1 }
> db.stats()
{
	"db" : "xhprof",
	"collections" : 3,
	"objects" : 2079,
	"avgObjSize" : 65585.81625781626,
	"dataSize" : 136352912,
	"storageSize" : 167763968,
	"numExtents" : 13,
	"indexes" : 6,
	"indexSize" : 490560,
	"fileSize" : 201326592,
	"nsSizeMB" : 16,
	"dataFileVersion" : {
		"major" : 4,
		"minor" : 5
	},
	"extentFreeList" : {
		"num" : 0,
		"totalSize" : 0
	},
	"ok" : 1
}

A different approach would be to backup the database, drop it alltogether and restore it from the backup. WARNING: this one requires downtime, too!

# echo "db.results.remove({\"profile.main().wt\" : {\$lt: 500000}})" | mongo xhprof
# mongodump -d xhprof
# echo 'db.dropDatabase()' | mongo xhprof
# sync
# mongorestore dump/xhprof

Note: MongoDB has a compact function for collections that is used to remove data fragmentation (doc). This function does NOT free up disk space (the fileSize will stay the same):

> db.runCommand({ compact : 'results' })
{ "ok" : 1 }
> db.stats()
{
	"db" : "xhprof",
	"collections" : 3,
	"objects" : 2079,
	"avgObjSize" : 65585.81625781626,
	"dataSize" : 136352912,
	"storageSize" : 167759872,
	"numExtents" : 13,
	"indexes" : 6,
	"indexSize" : 490560,
	"fileSize" : 4226809856,
	"nsSizeMB" : 16,
	"dataFileVersion" : {
		"major" : 4,
		"minor" : 5
	},
	"extentFreeList" : {
		"num" : 24,
		"totalSize" : 3438862336
	},
	"ok" : 1
}

PHP memcached Fatal error: Class ‘Memcache’ not found

I had this problem on an older Debian system we are supporting. After installing memcached and php5-memcached packages, the PHP interpreter didn’t find the required functions (take a look at this comment for a simple test script).

Turns out, Debian has different configuration for different environments (cli, apache2, php-fpm, etc) and apparently the guy providing the php5-memcached package wasn’t aware of that, so I fixed this error by creating a link to the memcache php-ini config for each environment:

# cd /etc/php5/apache2/conf.d
# ln -s ../../conf.d/memcached.ini ./
# cd /etc/php5/cli/conf.d
# ln -s ../../conf.d/memcached.ini ./
# # ls -l /etc/php5/*/conf.d/
/etc/php5/apache2/conf.d/:
total 32K
lrwxrwxrwx 1 root root   26 Jul 18 14:48 memcached.ini -> ../../conf.d/memcached.ini
/etc/php5/cli/conf.d/:
total 32K
lrwxrwxrwx 1 root root   26 Jul 18 14:46 memcached.ini -> ../../conf.d/memcached.ini
# php -i | grep -i memcache
/etc/php5/cli/conf.d/memcached.ini,
memcached
memcached support => enabled
libmemcached version => 0.40
Registered save handlers => files user memcached