April 4, 2010
We’ve been working on a huge, soon to announced project, that involves Wordpress MU. I’ve been working for months on this, and I’ve really got stuck into the deepest and darkest parts of Wordpress MU. I’ve got to say, I’m a fan of Wordpress. The code is nice to work with, its well documented (for the most part) and it just works. As opposed to some of the other open source CMS and blog engines out there.
But, I just kept hitting performance bottle necks with it. Which, is to be expected when you’re serving heavy php pages with lots of MySQL reads. I went down the well traveled road of installing various caching plugins (WP Super Cache was my favorite), and they made a huge difference – but still, performance not quite where I felt it should be. This project is for a very high traffic site, so its really worth squeezing every last percentage of performance out of your code.
I was about to roll out some kind of reverse caching proxy, such as Squid or a server cluster when I started to debate changing out Apache for something lighter and faster. I’d heard good things about lighthttp and Nginx. I was a little nervous, as this is a live production site, but the performance gains could be worth it. When I learned that wordpress.com uses Nginx, that sealed it for me. Nginx it is then!
So, after much trepidation, I pulled the trigger and started to rebuild the app on a new server running Nginx (a LEMP stack, as opposed to LAMP). It took me a long and arduous 2 days to get everything back up and running (on a dev server first, customers didn’t experience any downtime), but man, first impressions blew my mind. It is way faster, especially for serving images. I intend to write a detailed post about the setup, but for now here is a graph that shows a quick comparison. The site is monitored by Site24×7, and the following graph shows the ping times for a given page. This page had very simple content, so I wasn’t expecting much. But, see for yourself. Its a big difference, note that the site was cached before and after the change it was not cached. Once I roll out caching on the new nginx server then it will be fair comparison, i.e. this graph makes it looks like nginx is slower then it really could be.

Some quick lessons learned, the hard way.
PHP sessions did not work, I spent hours trying to get them to work under nginx but then just decided it was time to roll out a MySQL based session system, its more secure and portable and only took about 2 hours to get up and running on the live site. I’ll post a how-to article soon.
If images aren’t being served correctly it could be because FastCGI php cache’s directory needs to be owned by the same user that Nginx is running as. Obvious I know, but still, not so obvious at the time. Well, lots of things aren’t obvious at 2 in the morning!
I promise I’ll post more details shortly.
by Mike Pritchard
Categories: Products, php
Tags: nginx, php, wordpress
Comments (0)

We’ve worked on a few projects over the years where we’ve adopted agile project planning practices. As a result, we’ve used a few of the tools that are out there, such as Rally, and just haven’t been that impressed (not to mention the cost!). It seems to me that the beauty of Agile is that its simple, and I feel like the tools that go along with it should be too. I’m a big fan of 7 Signal’s Basecamp, and use it a lot. Its good for what it is, but its not a project planing tool (which is fair, as its not supposed to be).
When we run agile, most of the actual day-to-day planning activity focuses around a white board, with basically a bunch of post-it notes that we physically move around. So, why is it that none of these project planning tools for Agile seem to be as simple? In my experience, they seemed to get bogged down by trying to give us too many features.
So, at Ad Astra we developed our own tool, trying to keep it as simple as possible. And, finally, we’ve decided to release it into the wild as a free service with the hope that other people will find it useful. We’re going to open source the code, if we have any interest from the community to do so. So, if you’re interested, send us an email and we’ll get you hooked up with the code repo. Its Beta software, and a work in progress – when we have free time!
Check it out at etasking.com.
by Mike Pritchard
Categories: Products
Tags: agile, ajax, css, etasking, html, php
Comments (0)
December 4, 2009

Its been out for a few months now, but we just wanted to announce the official release of Electronic Arts’ latest game in the Spore series, Spore Islands for Facebook.

We had the privilege of assisting Area/Code in its development. We were the lead php and AI developer for this game, it was a lot of fun and a technically challenging project. We have a lot of experience building websites that support high traffic, but supporting a game as advanced as this with as much back-end logic as this required, was always going to be a challenge. But, we rose to the challenge and we’re extremely proud to have been a part of this great game series!
Try it out on Facebook:
http://apps.facebook.com/sporeislands/
by Mike Pritchard
Categories: AI, Games, News, php
Tags: facebook, php
Comments (0)
November 17, 2009
Logging in php isn’t fun, lets face it. There are quite a few php loggers out there, but we’ve built our own over the years and we felt why not share this with the community? So, why did we build our own? Well, firstly I personally like log files that are actually readable and secondly I really need a stack trace, I need to know not only what part of the code fell over, but what called that piece of code? And, then what called that piece of code. Sometimes I like to use the system log file, and just have a terminal open (yeah, we’re a Mac shop here!) and just watch the log using tail -f php_errors.log. But, sometimes I like to view the log in html for small test scripts. And, another thing is that sometimes I want to setup the logger to email me any fatal errors, this is very handy as sometimes these can go under the radar especially on really busy sites.
Our Logger class is static, which is great for speed and means there’s only ever one instantiation floating around. It also initializes itself, so you don’t need to worry.
require_once("Logger.class.php");
// This tells the logger to echo html
Logger::echoLog();
// Tells the Logger to catch and log system errors
Logger::catchSysErrors();
// This tells the logger to email you fatal errors. The email will contain the error
// message as well as a dump of the POST, GET and SESSION variables
Logger::setupEmail($email, $projectName, $logLevel = self::$FATAL)
// You can set the logging level (the default is debug)
// Level can be (in order);
// Logger::$DEBUG, Logger::$INFO, Logger::$ERROR, Logger::FATAL
// e.g., if you set the level to ERROR, only ERROR and FATAL messages would be outputted
// e.g., if you set the level to INFO, only INFO, ERROR & FATAL messages would be outputted
Logger::setLevel(Logger::$DEBUG);
// To use, call any of these
Logger::fatal("Some error message");
Logger::error("Some error message");
Logger::debug("Some error message");
Logger::info("Some error message");
// You can also dump a variable, array or object's contents with
Logger::dump($variable);
Here is the Logger class (apologies that the indenting is a little screwy);
/**
* Logging class, use for all trace commands
*
*/
class Logger {
public static $DEBUG = 0;
public static $INFO = 1;
public static $WARNING = 2;
public static $ERROR = 3;
public static $FATAL = 4;
private static $debugLevel = 0;
private static $echoLog = false;
private static $email = "dev@adastrasystems.com";
private static $project = "Adastra Project";
private static $sendEmail = true;
private static $emailLevel = 4;
// //////////////////////////////////////////////////////////////////////////////////////
/**
* Class constructor
*/
public function __construct(){
self::init();
}
// //////////////////////////////////////////////////////////////////////////////////////
public static function echoLog(){
self::$echoLog = true;
}
// //////////////////////////////////////////////////////////////////////////////////////
/**
* Let the Logger catch any system errors
*/
public static function catchSysErrors(){
set_error_handler("Logger::sysErrorHandler");
}
// //////////////////////////////////////////////////////////////////////////////////////
/**
*
*/
private static function trace($level, $msg){
if ($level < self::$debugLevel){
return;
}
$bt = debug_backtrace();
$class = "";
$function = "";
// get class, function called by caller of caller of caller
if (isset($bt[2]['class'])){
$class = $bt[2]['class'];
$function = "." . $bt[2]['function'];
}
// get file, line where call to caller of caller was made
$file = $bt[1]['file'];
$line = $bt[1]['line'];
$file_name = basename($file);
if (self::$echoLog){
switch($level){
case self::$DEBUG: $levMsg = "debug"; break;
case self::$INFO: $levMsg = "info"; break;
case self::$WARNING:$levMsg = "warning"; break;
case self::$ERROR: $levMsg = "error"; break;
case self::$FATAL: $levMsg = "fatal"; break;
}
$msg = "[$levMsg $class$function] $msg";
$msg .= " on line $line of $file_name";
if (isset($bt[2]['file'])){
$fname = basename($bt[2]['file']);
$msg .= ", called from line ".$bt[2]['line']." of $fname";
}
$msg .= "\n";
echo $msg;
flush();
}
else {
// Get IP address....
// Translate level into text...
$levMsg = "????";
switch($level){
case self::$DEBUG: $levMsg = "DEBUG"; break;
case self::$INFO: $levMsg = "INFO"; break;
case self::$WARNING:$levMsg = "WARNING"; break;
case self::$ERROR: $levMsg = "ERROR"; break;
case self::$FATAL: $levMsg = "FATAL"; break;
}
$msg = "[$levMsg] $class$function $msg";
$msg .= " {on line $line of $file_name";
if (isset($bt[2]['file'])){
$fname = basename($bt[2]['file']);
$msg .= ", called from line ".$bt[2]['line']." of $fname";
}
if (isset($bt[3]['file'])){
$fname = basename($bt[3]['file']);
$msg .= ", called from line ".$bt[3]['line']." of $fname";
}
$msg .= "}";
error_log($msg);
}
if (self::$sendEmail){
self::sendEmail($msg, $level);
}
}
// //////////////////////////////////////////////////////////////////////////////////////
private static function sendEmail($msg, $level){
if ($level < self::$emailLevel){
return;
}
$headers = 'From: ' . self::$email;
$message = $msg . "\n\nPOST VARIABLES \n\n" . print_r($_POST, true) . "\nGET VARIABLES\n\n" . print_r($_GET, true) . "\nSESSION VARIABLES\n\n" . print_r($_SESSION, true);
mail(self::$email, self::$project, $message, $headers);
}
// //////////////////////////////////////////////////////////////////////////////////////
/**
* Use to replace system error handler, if desired. Use Logger::catchSysErrors() to
* activate
*/
public static function sysErrorHandler($errno, $errstr, $errfile, $errline){
switch ($errno) {
case E_USER_ERROR:
$levMsg = "SYS_ERROR";
break;
case E_USER_WARNING:
$levMsg = "SYS_WARNING";
break;
case E_USER_NOTICE:
$levMsg = "SYS_NOTICE";
break;
default:
$levMsg = "SYS_UNKNOWN";
break;
}
if (self::$echoLog){
switch($errno){
case E_USER_NOTICE: $levMsg = "info"; break;
case E_USER_WARNING:$levMsg = "warning"; break;
case E_USER_ERROR: $levMsg = "error"; break;
default: $levMsg = "debug"; break;
}
$msg = "[$levMsg #$errno] $errstr";
$msg .= " on line $errline of ".basename($errfile)."";
$msg .= "\n";
echo $msg;
flush();
}
else {
$msg = "[$levMsg] No: $errno Msg: $errstr";
$msg .= " {on line $errline of ".basename($errfile)."}";
error_log($msg);
}
/* Don't execute PHP internal error handler */
return true;
}
// //////////////////////////////////////////////////////////////////////////////////////
public static function setupEmail($email, $projectName, $logLevel = self::$FATAL) {
self::$sendEmail = true;
self::$emailLevel = $logLevel;
self::$project = $projectName;
}
// //////////////////////////////////////////////////////////////////////////////////////
public static function setLevel($newLevel) { $debugLevel = $newLevel; }
public static function setLevelDebug() { self::$debugLevel = self::$DEBUG; }
public static function setLevelInfo() { self::$debugLevel = self::$INFO; }
public static function setLevelWarning() { self::$debugLevel = self::$WARNING; }
public static function setLevelError() { self::$debugLevel = self::$ERROR; }
public static function setLevelFatal() { self::$debugLevel = self::$FATAL; }
// //////////////////////////////////////////////////////////////////////////////////////
public static function dump($var) { self::debug(print_r($var, true)); }
public static function debug($msg){ self::trace(self::$DEBUG, $msg); }
public static function warning($msg){ self::trace(self::$WARNING, $msg); }
public static function warn($msg){ self::trace(self::$WARNING, $msg); }
public static function error($msg){ self::trace(self::$ERROR, $msg); }
public static function info($msg){ self::trace(self::$INFO, $msg); }
public static function fatal($msg){ self::trace(self::$FATAL, $msg); die(); }
// //////////////////////////////////////////////////////////////////////////////////////
}
by Mike Pritchard
Categories: php
Tags: logging, php
Comments (0)
September 1, 2009
Ad Astra has just completed a major 7-month project developing a game for the facebook platform. Ad Astra was the lead developer for the server-side code. The game, well, more of a simulation-game, was written in php and MySQL and was pretty computationally intensive. We were selected because of our expertise in both Artificial Intelligence and also our expertise in developing large scale websites. The game itself hasn’t officially launched yet, so we’re not able to publicize any details of the game yet – but when it is we’ll post an update here. But, what I can say is that the publisher is one of the largest game publishers in the world, so it was very exciting to work on a project for them. And, as always, working on games is just fun – though there are some long hours to get it just right. And, it was also fun to work on a team of talented professionals, artists, producers and developers. Awesome, can’t wait to get another game contract!
by Mike Pritchard
Categories: AI, Games, php
Tags: AI, facebook, Games, php
Comments (0)
|
|