FASTER Unit Test

January 19th, 2012

I’m developing with linux(Vmware guest) on Windows7(host OS).
My laptop has 5400rpm HDD and 8G mem.
My project has many test cases and fixtures, all test cases finished in about 15 minutes :(

I wanted to finish group test faster. I guessed large amount of fixture data is bottleneck, since it deletes and restores data many times. Disk I/O of Virtual Machine is slower than Host OS’s one.
At first I changed from HDD to SSD, all test cases finished in under 10 minutes, Wow.

Unfortunately, SSD has a limit on the number of write(update) operation. When group test ran, I worried that it might shorten the life of SSD.
So I moved MySQL data files to Memory(tmpfs) to reduce SSD I/O.
After doing it, group test finished in about 5 minutes :)

Redhat clone Linux like CentOS has tmpfs(on memory filesystem) and mounts it /dev/shm by default.

I made the following scripts,
1.  onMemoryDeploy.sh: To move files to memory and make softlink to it
2. onMemoryMySQL: init script to run onMemoryDeploy.sh start/stop

Before executing the scripts, I recommend to backup mysql data files.

/etc/rc.d/init.d/mysql stop
cp -rp /var/lib/mysql /var/lib/mysql-orig

Here is the script to move data files to memory and move them back.
/root/onMemoryDeploy.sh script on gist


#!/bin/sh

MYSQL=/etc/rc.d/init.d/mysql

MEMDIR=/dev/shm
MEM_MYSQL=$MEMDIR/mysql

LIBDIR=/var/lib
ORIGDIR=$LIBDIR/mysql

start()
{
  if [ ! -L $MEM_MYSQL ] && [ -d $ORIGDIR ]; then
    $MYSQL stop
    mv $ORIGDIR $MEMDIR/
    ln -s $MEM_MYSQL $ORIGDIR
    $MYSQL start
  fi
}

stop()
{
  if [ -L $ORIGDIR ] && [ -d $MEM_MYSQL ]; then
    $MYSQL stop
    rm -f $ORIGDIR
    mv $MEM_MYSQL $LIBDIR/
  fi
}

case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  *)
    echo "Usage: onMemoryDeply.sh start or stop"
    exit 1
  ;;
esac

exit 0

Here is init script(for CentOS) that runs onMemoryDeploy.sh (start/stop) at the OS boot and shutdown.
/etc/rc.d/init.d/onMemoryMySQL script on gist

#!/bin/bash
#
# Startup script for onMemoryMySQL
# chkconfig: 345 85 15
#

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:/usr/bin:/bin

test -f /root/onMemoryDeploy.sh || exit 0

start()
{
  /root/onMemoryDeploy.sh start
  touch /var/lock/subsys/onMemoryMySQL
}

stop()
{
  /root/onMemoryDeploy.sh stop
  rm -f /var/lock/subsys/onMemoryMySQL
}

case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  *)
    echo "Usage: onMemoryMySQL start or stop"
    exit 1
  ;;
esac

exit 0

Then register init script with chkconfig tool.

/sbin/chkconfig --add onMemoryMySQL
/sbin/chkconfig onMemoryMySQL on

Have a good testing time!

Performance check of CakePHP1.3.11 and CakePHP2beta

July 27th, 2011

CakePHP1.3.11 and CakePHP2beta  released today. I appreciated great work of the CakeTeam.
CakePHP 1.3.11 and 2.0.0-beta released

CakePHP2 introduces lazy loading and some great futures. I’m interested in how CakePHP2 faster than CakePHP1.3, so I compared both performance.
I know CakePHP2 still beta release, maybe it will be faster more and more until the final release.

Measurement Tool
I use the Siege(measurement tool) to measure throughput and the XHPref to measure the number of functaion calls.
Before this test, I tried apache ab tool to measure throughput, but it doesn’t work well. Because it sends the request as HTTP/1.0, Cake2 sends the response as HTTP/1.1.  The ab waits a few seconds for  connection close request.

Server Spec
Server: Dell SC440 Pentium Dual  CPU  E2180  @ 2.00GHz / 2G Memory / SATA HDD
OS: Ubuntu11 64bit
PHP 5.3.5 with APC
Apache 2.2.17

Step
I ran the Siege on same server. Siege option is “siege -b -c 10 -t 3S”, it means concurrency 10, access to the server in 3 seconds.
I know it’s better to run the Siege on another host, but it slightly difficult for me to prepare 2 hosts. This test only compare CakePHP1.3 and Cake2beta on same environment.

SampleApps
I prepare the sample apps of Cake1.3 and Cake2. It’s very simple code made by bake.
Here is the code.
https://github.com/ichikaway/CakePHP-PerformanceCheckSample


Database

I prepare the posts table which has 25,000 records.


Result

Target action is PostController/index which is using pagination.

Cake2beta & Cakephp2.0.2

 Siege:  40 trans/sec
xhpref: 50msec, 11790calls


CakePHP1.3.11

Siege: 29 trans/sec
xhpref: 66msec, 16,931calls

Conclusion
Cake2beta and 2.0.2  are  30% faster than CakePHP1.3.10 and reduces 5000 function calls. Awesome!
I guess performance gap is wider on the apps using some DB relations and models.


Extra
The result of XHpref on CakePHP2 beta. It shows CakeRoute cost is high.  I guess the sample app are using paginator helper, it calls Router class a lot to make the URLs( sort, page number, etc)
The call-graph can be clicked to see original size.

xhpref-list of Cake2 beta

callgraph-cake2beta

checking update of pecl mongo

July 22nd, 2011

Recently, pecl mongo driver has been updated a lot.  Today it’s released version 1.2.2, fixed some segmentation fault problems.
Here is the change log
http://pecl.php.net/package-changelog.php?package=mongo&release=1.2.2

It’s a little bit difficult for me to catch up the update info, so I made the twitter bot which tweets the update info when pecl mongo driver is updated.
https://twitter.com/#!/pecl_mongo_bot

Misunderstanding of CakePHP beginners or not cake users

May 19th, 2011

I and @hiromi2424 picked up some misunderstandings of CakePHP, especially cake beginners/not cake users.

Need no SQL knowledge, because Cake model makes SQL statements.
SQL knowledge is so important. We have to understand SQL statements which CakePHP made. It’s good for performance improvement, debug and avoiding security problems.

Everybody can learn and develop fast, good for PHP noobs
It’s better to understand OOP and pure PHP before using Cake.

Need a patch to use external libraries
We can use any libraries. Set library files in vendors directory and call App::import(’Vendor’,'LibraryName’)

Slower  so much than pure php
It’s true only to compare with “Hello world” scripts, but production code is more complex. Production code are using many functions and objects, fetching DB records.
In production code, cake applications are slower than pure php one, but not slower so much.
APC(OP code caching) is great solution of performance imporvement.

Can make similar code whoever coding.
It depends on skill and knowledge of MVC.

Don’t have to care security problems when use SecurityComponent
SecurityComponent detects and protects from CSRF and alteration of post fields.
http://book.cakephp.org/#!/view/1296/Security-Component
We have to care more security problems, XSS, SQL Injection, ObjectInjection and so on.

DB is mandatory
Set “false” to Model::useTable property.
CakePHP FromHelper decides html input type from DB schema, so it’s better to set input type option of FormHelper::input() without DB.

FormHelper/HtmlHelper is mandatory
You can write view files with pure html tags :)
It’s easy to make form screen using helpers, but sometimes the html tags CakePHP made can’t fit your html/css.
If helpers is annoying you so much, make new helper extending cake helper or set tags.php or get rid of cake helpers(SecurityComponent needs FromHelper, make CSRF protection by yourself).

Can’t change bake templates
http://book.cakephp.org/#!/view/1526/Modify-default-HTML-produced-by-baked-templates

must follow singular/plural convention
There are some ways to avoid convention. Ex. can use any table name with Model::table property.
We can define our inflection rules using Inflector::rules()
http://book.cakephp.org/view/953/Inflections
http://api.cakephp.org/class/inflector#method-Inflectorrules

CakePHP-MongoDB-DboSource update

April 22nd, 2011

I launched new web service using CakePHP and MongoDB. I had to add some functions to CakePHP-MongoDB DboSource, such as using MongoDB update operators, Groupby, Map/Reduce.

I’ve already pushed bunch of adding code to github. Please git pull and try it :)
https://github.com/ichikaway/cakephp-mongodb/

I also write some documents on Wiki of Github.
https://github.com/ichikaway/cakephp-mongodb/wiki/_pages

There are API documents in the repository.
https://github.com/ichikaway/cakephp-mongodb/tree/master/docs

Now I’m testing this DboSource with MongoDB1.6.5 and 1.8.1, pecl mongo1.1.4, CakePHP1.3

I start to work applying it to CakePHP2.0, it’s in the cake2.0 branch.
https://github.com/ichikaway/cakephp-mongodb/tree/cake2.0

Check your spelling mistake and save your life time.

February 3rd, 2011

Hi, I’m back the blog :)

Sometimes I see messages about “WTF, Could not load behavior by spelling mistake as Model::actAs. waste much time…” on the Twitter. Model::actsAs is correct.
I also did such spelling mistake, and wanted to die…

I want save time and make awesome plugin “cakephp-MissingDetect-Plugin“.

This plugin detects spelling mistake of property name as follow.

  • Controller::helper ( helpers is correct)
  • Controller::component (components is correct)
  • Controller::use (uses is correct)
  • Model::actAs (actsAs is correct)

If you write wrong property name as Controller::component,  you can see alert messages on the top of screen as follow.

missingdetect-screenshot

This plugin works only on the debug mode.

It’s easy to setup, please read the Readme.
https://github.com/ichikaway/cakephp-MissingDetect-Plugin

New repository name of MongoDB driver for CakePHP

September 17th, 2010

I changed repository name of MongoDB driver for CakePHP.

New repository name is “cakephp-mongodb”. It is easy to understand the purpose  from repository name :)

New repository is here.
http://github.com/ichikaway/cakephp-mongodb

Also, I keep  the old repository for keeping forking list and watcher list.
http://github.com/ichikaway/mongoDB-Datasource

I will often sync repository data  from new to old repository with “git push –mirror ” command.
If you are using the old git repository, please change git remote site to the new repository.

How to control a cakephp cache duration

October 7th, 2009

I’m using CakePHP1.2.5.

Cache is very powerful function for web pages using heavy SQL queries. If you use the CakePHP default Cache, you can see cache files in the app/tmp/cache directory.

I want to use the Cake Cache function at many points of my system and set different cache duration.

Firstly, check the core.php for using Cache.

//in core.php
Cache::config('default', array('engine' => 'File'));

This is a sample script using cache function, duration is 1 minute.

class PostController extends AppController {

var $uses = array('Post');

function index() {

$return_data = null;
Cache::set(array('duration' => '+60 seconds'));
if( ($return_data = Cache::read('PostController-index') ) === false ){

$return_data = $this->Post->find('all');
Cache::set(array('duration' => '+60 seconds'));
Cache::write('PostController-index' , $return_data);
}

$this->set('data',$return_data);
}
}

If there is no cache file(PostController-index),get DB data with the Post model and create cache file with its result. Array data is serialized.
You access same page within 1 minute, cake read cache file(PostController-index)
and return it insted of getting data from the Post model.

Key point is to use Cache::set() before each Cache::read and Cache::write(), we can change cache duration in each point.
http://book.cakephp.org/view/773/Cache-set

Be careful setting cache file name without overlap at each point. I recommend the rule, Class name + function name + parameter.

BTW, we can set Cache::config parameter in Cache::read() and Cache::write().
http://book.cakephp.org/view/766/Cache-read
http://book.cakephp.org/view/767/Cache-write
So, we can define multiple cache config as follow and use it each cache point.

//in core.php
Cache::config('default', array('engine' => 'File'));
Cache::config('onemin', array('engine' =>'File','duration'=> 60,));
Cache::config('onehour', array('engine' => 'File','duration'=> 3600,));
Cache::read('PostController-index', 'onemin')
Cache::write('PostController-index' , $return_data,'onemin');

How to extend a plugin indirectly

September 16th, 2009

I’m using CakePHP1.2.4
At times, I want to modify/extend a  plugin indirectly.

How do I do that?
It’s very simple, only import and inherit.

Here is the plugin(cakeplus) I made on the github.
http://github.com/ichikaway/cakeplus/tree

In this example ,   I use the behavior(add_validation_rule.php) of the cakeplus plugin and modify some method.

1. download the cakeplus plugin, and set in the “app/plugins” directory.
2. make new directory(cakeplusplus) in the “app/plugins”.

Now you can see directories as follow.


plugins/cakeplus/models/behaviors/add_validation_rule.php

plugins/cakeplusplus/models/behaviors/

3. create “ext_add_validation_rule.php”  file in “cakeplusplus/models/behaviors/” directory .


App::import('Model', 'cakeplus.AddValidationRule');
class ExtAddValidationRuleBehavior extends AddValidationRuleBehavior {

}

4. Now you can use the “ExtAddValidationRuleBehavior” class which has same functions as the “AddValidationRuleBehavior” class.

class Post extends AppModel {

var $name = 'Post';

//var $actsAs = array('Cakeplus.AddValidationRule');
var $actsAs = array('Cakeplusplus.ExtAddValidationRule');

}

5.If you want to modify or extend, you just override a method of the “AddValidationRuleBehavior” class in the “ExtAddValidationRuleBehavior” class as follow.
In this example, override the maxLengthJP method and change that function.

App::import('Model', 'cakeplus.AddValidationRule');
class ExtAddValidationRuleBehavior extends AddValidationRuleBehavior {

function maxLengthJP( &$model, $wordvalue, $length ) {
$word = array_shift($wordvalue);

//extend
$length = $length * 2;

return( mb_strlen( $word ) <= $length );
}
}

Use placeholder in Model::query() of CakePHP

August 10th, 2009

I’m using CakePHP1.2.3

Model::query() is very useful for writing SQL statements as follow.

<?php

$this->Model->query("SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = 100", $cachequeries = false);

To avoid SQL Injection, we want to use placeholders instead of embedding user input value in the statement.
We use “?” character in the SQL statement, set array data in 2nd parameter of the query method.
If 2nd parameter is array data in query method, it executes DboMysql::value() for escape value, using the “mysql_real_escape_string” function.

<?php

$sql = "SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = ? LIMIT ?";
$this->Model->query($sql, array(100,1), $cachequeries = false);

Cake constructs a SQL statement and executes as follow.

SELECT `Post`.`id` FROM `posts` AS `Post` WHERE `Post`.`id` = 100 LIMIT 1

References
http://book.cakephp.org/view/456/query
http://en.wikipedia.org/wiki/SQL_injection