<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Perplexed Labs &#187; mysql</title>
	<atom:link href="http://blog.perplexedlabs.com/tag/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Sat, 24 Jul 2010 16:27:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</title>
		<link>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/</link>
		<comments>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/#comments</comments>
		<pubDate>Sun, 15 Nov 2009 20:37:38 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mod_wsgi]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysql-python]]></category>
		<category><![CDATA[mysqldb]]></category>
		<category><![CDATA[setuptools]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=378</guid>
		<description><![CDATA[This is an update to my previous how-to Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel). The biggest reason why I chose to go with Python 2.5 at the time was because the MySQL Python (MySQLdb) package didn't support Python 2.6. The 1.2.3c1 release does so that roadblock is lifted. The instructions [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>This is an update to my previous how-to <a href="http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/">Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a>.</p>
<p>The biggest reason why I chose to go with Python 2.5 at the time was because the MySQL Python (MySQLdb) package didn't support Python 2.6.  The 1.2.3c1 release does so that roadblock is lifted.</p>
<p>The instructions are identical - nothing has really changed in that regard.  Just change the references from Python 2.5 to 2.6.  Here are the links to the versions I'm using successfully:</p>
<blockquote><p>
Python 2.6.4: <a href="http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz">http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz</a></p>
<p>setuptools 0.6c11: <a href="http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086">http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086</a></p>
<p>MySQLdb 1.2.3c1: <a href="http://sourceforge.net/projects/mysql-python/files/mysql-python-test/1.2.3c1/MySQL-python-1.2.3c1.tar.gz/download">http://sourceforge.net/projects/mysql-python/files/mysql-python-test/1.2.3c1/MySQL-python-1.2.3c1.tar.gz/download</a></p>
<p>mod_wsgi 2.6: <a href="http://modwsgi.googlecode.com/files/mod_wsgi-2.6.tar.gz">http://modwsgi.googlecode.com/files/mod_wsgi-2.6.tar.gz</a></p>
<p>Django 1.1.1: <a href="http://www.djangoproject.com/download/1.1.1/tarball/">http://www.djangoproject.com/download/1.1.1/tarball/</a>
</p></blockquote>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>PHP Custom MySQL Session Handler</title>
		<link>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/</link>
		<comments>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 14:00:30 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[session handler]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=302</guid>
		<description><![CDATA[The Problem I'm sure many have used PHP's default session handling capabilities. By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp. This is done for the sake of simplicity. On a single-server, low load website, this particular setup works fine. It's when [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>I'm sure many have used PHP's default session handling capabilities.  By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp.</p>
<p>This is done for the sake of simplicity.  On a single-server, low load website, this particular setup works fine.  It's when you start having multiple simultaneous requests from a single client (identified by a session) that the problems begin to show their ugly heads.  Utilizing AJAX multiple simultaneous requests might be the norm, even for a low load website.</p>
<p>Essentially, in order to prevent <a href="http://en.wikipedia.org/wiki/Race_condition">race conditions</a> PHP internally uses a lock to maintain exclusive access to the file containing the session data for the client connection.  This means that as soon as a single request acquires exclusive access to that session file, no other request can access the file until the original request completes.  What happens when that second request asks to start the session?  It waits.</p>
<h3>The Solution</h3>
<p>Fortunately there's a solution to all this.  Implementing your own custom session handler and moving your session storage backend to another technology (such as a MySQL database or memcache) affords you the ability to handle simultaneous requests in a thread-safe manner.  Remember, it's a good thing that PHP prevents race conditions by locking the session file.  What we're looking to do is increase the granularity of the lock to the level of individual session data key =&gt; value pairs.</p>
<p>For this post we're going to stick to storing sessions in the database with our own custom session save handler.  Perhaps in another post I'll talk about doing the same in memcache.  It's the theory we're concerned about, not necessarily the exact storage mechanism implementation.</p>
<p>Implementing your own custom session handler is simply a matter of calling session_set_save_handler() with the appropriate callback methods for handling the following scenarios:</p>
<ul>
<li><strong>open</strong><br />
Open function, this works like a constructor in classes and is executed when the session is being opened. The open function expects two parameters, where the first is the save path and the second is the session name.</li>
<li><strong>close</strong><br />
Close function, this works like a destructor in classes and is executed when the session operation is done.</li>
<li><strong>read</strong><br />
Read function must return string value always to make save handler work as expected. Return empty string if there is no data to read. Return values from other handlers are converted to boolean expression. TRUE for success, FALSE for failure.</li>
<li><strong>write</strong><br />
The "write" handler is not executed until after the output stream is closed.</li>
<li><strong>destroy</strong><br />
The destroy handler, this is executed when a session is destroyed with session_destroy() and takes the session id as its only parameter.</li>
<li><strong>gc</strong><br />
The garbage collector, this is executed when the session garbage collector is executed and takes the max session lifetime as its only parameter.</li>
</ul>
<p>There's a well known trick for situations like this that allow you to pass a class method (static or instance) for callbacks.  Let's take a look at a simple example.  This correctly implements the required methods but obviously doesn't do much:</p>
<pre class="brush: php;">
class MySession
{
	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		return true;
	}
}
</pre>
<h3>SPL and Storing Session Data In MySQL</h3>
<p>Adding support for MySQL to this class is fairly trivial.  Let's start off by creating a table to store our session data:</p>
<pre class="brush: sql;">
CREATE TABLE `sessions` (
  `sesskey` char(32) NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `varkey` varchar(128) NOT NULL,
  `varval` longtext NOT NULL,
  PRIMARY KEY  (`sesskey`,`varkey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</pre>
<p><a href="http://us3.php.net/spl">PHP's SPL</a> provides the ability to create objects that behave as though they were arrays.  You can make objects that iterate, respond to accessing them via array notation ($object['key']), and lots of other interesting things.</p>
<p>We're going to enhance the MySession class with a couple SPL interfaces that will allow the object, when instantiated, to behave like an array.   We can then override the $_SESSION superglobal with an instance of our new MySession class.  It will provide an identical interface to access session data while internally storing session data to the database.  Also, internally it will use methods for row-level locking via MySQL's advisory locks (GET_LOCK() and RELEASE_LOCK()).</p>
<p>The next step is to implement the required methods for the interfaces we're using.  These methods allow the object to behave as though it's an array.</p>
<pre class="brush: php;">
class MySession implements Countable, ArrayAccess, Iterator
{
	private $index;
	private $curElement;
	private $locks = array();
	private $sessionName = 'sessionId';
	private $serialize = 'serialize';
	private $unserialize = 'unserialize';
	private $session_id = null;

	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public function destroy()
	{
		$sessionName = session_name();
		$cookieInfo = session_get_cookie_params();
		$cookieExpires = time() - 3600;
		if((empty($cookieInfo['domain'])) &amp;&amp; (empty($cookieInfo['secure']))) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path']);
		} elseif(empty($cookieInfo['secure'])) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain']);
		} else {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain'], $cookieInfo['secure']);
		}
		unset($_COOKIE[$sessionName]);

		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$dbo-&gt;query($q);

		session_destroy();
	}

	private function lockName($k)
	{
		return 'sesslock'.$this-&gt;session_id.$k;
	}

	public function locked($k)
	{
		$k = $this-&gt;lockName($k);

		return isset($this-&gt;locks[$k]);
	}

	public function acquire($k, $timeout = 0)
	{
		$k = $this-&gt;lockName($k);

		if(!isset($this-&gt;locks[$k])) {
			$dbo = DBO::getInstance();
			$q = &quot;SELECT GET_LOCK('&quot;.$k.&quot;', &quot;.$timeout.&quot;)&quot;;
			$rs = $dbo-&gt;query($q);
			$this-&gt;locks[$k] = $dbo-&gt;result($rs, 0);
			$dbo-&gt;fr($rs);

			return $this-&gt;locks[$k];
		}

		return false;
	}

	public function release($k)
	{
		$k = $this-&gt;lockName($k);

		unset($this-&gt;locks[$k]);

		$dbo = DBO::getInstance();
		$q = &quot;SELECT RELEASE_LOCK('&quot;.$k.&quot;')&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);

		return true;
	}

	public function count()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT COUNT(*) FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function rewind()
	{
		$this-&gt;index = 0;
		$this-&gt;getCurElement();
	}

	private function getCurElement()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varkey`, `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' LIMIT &quot;.$this-&gt;index.&quot;,1&quot;;
		$rs = $dbo-&gt;query($q);
		$row = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);
		if(is_array($row) &amp;&amp; (count($row) == 2)) {
			$this-&gt;curElement = $row;
		} else {
			$this-&gt;curElement = array(null, null);
		}
	}

	public function key()
	{
		return $this-&gt;curElement[0];
	}

	public function current()
	{
		return call_user_func($this-&gt;unserialize, $this-&gt;curElement[1]);
	}

	public function next()
	{
		$this-&gt;index++;
		$this-&gt;getCurElement();
	}

	public function valid()
	{
		return ($this-&gt;curElement[0] !== null);
	}

	public function offsetSet($k, $v)
	{
		$dbo = DBO::getInstance();
		$q = &quot;REPLACE INTO `sessions` (`sesskey`, `varkey`, `varval`) VALUES ('&quot;.$this-&gt;session_id.&quot;', '&quot;.$k.&quot;', '&quot;.$dbo-&gt;sanitize(call_user_func($this-&gt;serialize, $v)).&quot;')&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetGet($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		if($ret = $dbo-&gt;result($rs, 0)) {
			$ret = call_user_func($this-&gt;unserialize, $ret);
		}
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function offsetUnset($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetExists($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return (bool)$ret;
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `timestamp` &lt; '&quot;.date('Y-m-d H:i:s', time() - $maxlifetime).&quot;'&quot;;
		$dbo-&gt;query($q);

		return $dbo-&gt;query($q);
	}
}
</pre>
<p>The $dbo object is just an example of an interface to the database through the use of a singleton.  Replace the $dbo object with your preferred mysql database interface and you'll be set to go!</p>
<p>Starting your session is now as simple as:</p>
<pre class="brush: php;">
$_SESSION = new MySession;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automated Backups &#8211; A 5 Minute Script To Safer Data</title>
		<link>http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/</link>
		<comments>http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 20:16:24 +0000</pubDate>
		<dc:creator>Eric</dc:creator>
				<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysqldump]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=178</guid>
		<description><![CDATA[Sometimes, taking a few minutes to do a simple thing will save you a headache down the road.  Like changing the oil in your car, or brushing your teeth every night, or automatically backing up your databases. The development server that I mess around on has recently accumulated a lot of data that I really [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/05/mysqldump-usage/' rel='bookmark' title='Permanent Link: mysqldump usage'>mysqldump usage</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/09/11/backing-up-subversion-repositories-using-svnadmin-hotcopy/' rel='bookmark' title='Permanent Link: Backing Up Subversion Repositories Using svnadmin hotcopy'>Backing Up Subversion Repositories Using svnadmin hotcopy</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Sometimes, taking a few minutes to do a simple thing will save you a headache down the road.  Like changing the oil in your car, or brushing your teeth every night, or automatically backing up your databases.</p>
<p>The development server that I mess around on has recently accumulated a lot of data that I really don't want to lose, so I whipped up a small script that will help to ensure this never happens.</p>
<blockquote><p>#!/bin/bash<br />
rm -f /path/to/backups/tmp/*<br />
mysqldump --opt --host=localhost --user=USERNAME --password=PASSWORD --all-databases &gt; /path/to/backups/tmp/dbBackup.sql<br />
tar -czvf /path/to/backups/database/mysql.`date '+%Y%m%d%H%M%S'`.tar.gz /backups/tmp<br />
service httpd restart</p></blockquote>
<p>Line by line, what's happening here is:</p>
<ul>
<li>delete the .sql file generated by the previous backup</li>
<li>dump every MySQL table to a file called dbBackup.sql.  You don't have to go this far; check out <a href="http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html">MySQL's documentation on mysqldump</a> for all the options</li>
<li>tar up the dbBackup.sql file, include a timestamp in the filename</li>
<li>restart Apache</li>
</ul>
<p>I want this script to run every night at 1am, so I added it to the crontab:</p>
<blockquote><p>0 1 * * * /path/to/backup.daily.cron</p></blockquote>
<p>Obviously this is a very simple script that can grow in complexity fairly quickly.  For example, you can clear out old logs (a must for some Rails apps), and do other housekeeping functions.  A good idea would also be to have a syncing program grab that .tar and upload it to your home machine, or even an S3 bucket.</p>
<p>When your MySQL instance gets corrupted or otherwise obliterated, you'll thank yourself for taking 5 minutes to set this up.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/05/mysqldump-usage/' rel='bookmark' title='Permanent Link: mysqldump usage'>mysqldump usage</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/09/11/backing-up-subversion-repositories-using-svnadmin-hotcopy/' rel='bookmark' title='Permanent Link: Backing Up Subversion Repositories Using svnadmin hotcopy'>Backing Up Subversion Repositories Using svnadmin hotcopy</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Mutex with PHP and mySQL</title>
		<link>http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/</link>
		<comments>http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/#comments</comments>
		<pubDate>Wed, 06 Feb 2008 15:34:57 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[mutex]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/</guid>
		<description><![CDATA[There are certain situations where you want a periodically executed script to have one and only one instance running at a time to prevent against poisoning of data due to simultaneous execution. There's actually an extremely simple way to accomplish this in a PHP environment by taking advantage of mySQL's built in "lock" functionality. The [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/' rel='bookmark' title='Permanent Link: PHP fast, large (megabyte), data transfer between sessions'>PHP fast, large (megabyte), data transfer between sessions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>There are certain situations where you want a periodically executed script to have one and only one instance running at a time to prevent against poisoning of data due to simultaneous execution.</p>
<p>There's actually an extremely simple way to accomplish this in a PHP environment by taking advantage of mySQL's built in "lock" functionality.</p>
<p>The following simple example illustrates the usage of the class that is listed below:</p>
<pre class="brush: php;">
&lt;?php
$lock = new cLock(&quot;updateEarnings&quot;);
if($lock-&gt;isFree()) {
	$lock-&gt;lock();
	// execute
	$lock-&gt;release();
} else {
	trigger_error(&quot;ALREADY LOCKED... ABORTING!&quot;, E_USER_NOTICE);
}
?&gt;
</pre>
<p>Class listing cLock.php: (keep in mind this class uses some helper functions of mine, particularly 'qdb()' and 'result()' which do the obvious)</p>
<pre class="brush: php;">
&lt;?php

class cLock
{
	var $lockname;
	var $timeout;
	var $locked;

	function cLock($name, $timeout = 0)
	{
		$this-&gt;lockname = $name;
		$this-&gt;timeout = $timeout;
		$this-&gt;locked = -1;
	}

	function lock()
	{
		$rs = qdb(&quot;SELECT GET_LOCK('&quot;.$this-&gt;lockname.&quot;', &quot;.$this-&gt;timeout.&quot;)&quot;);
		$this-&gt;locked = result($rs, 0);
		mysqli_free_result($rs);
	}

	function release()
	{
		$rs = qdb(&quot;SELECT RELEASE_LOCK('&quot;.$this-&gt;lockname.&quot;')&quot;);
		$this-&gt;locked = !result($rs, 0);
		mysqli_free_result($rs);

	}

	function isFree()
	{
		$rs = qdb(&quot;SELECT IS_FREE_LOCK('&quot;.$this-&gt;lockname.&quot;')&quot;);
		$lock = (bool)result($rs, 0);
		mysqli_free_result($rs);

		return $lock;
	}
}

?&gt;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/' rel='bookmark' title='Permanent Link: PHP fast, large (megabyte), data transfer between sessions'>PHP fast, large (megabyte), data transfer between sessions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>mysqldump usage</title>
		<link>http://blog.perplexedlabs.com/2008/02/05/mysqldump-usage/</link>
		<comments>http://blog.perplexedlabs.com/2008/02/05/mysqldump-usage/#comments</comments>
		<pubDate>Tue, 05 Feb 2008 19:23:26 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[export]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysqldump]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://perplexedlabs.com/2008/02/05/mysqldump-usage/</guid>
		<description><![CDATA[The info is available everywhere but for those who want it spoon fed into their eager mouths here goes: To dump a database to an optimized sql file and then tar/gz compress the file: > mysqldump --opt database > database.sql > tar -czvf database.tar.gz database.sql To export a database from the local mysql server to [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/' rel='bookmark' title='Permanent Link: Automated Backups &#8211; A 5 Minute Script To Safer Data'>Automated Backups &#8211; A 5 Minute Script To Safer Data</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>The info is available everywhere but for those who want it spoon fed into their eager mouths here goes:</p>
<p>To dump a database to an optimized sql file and then tar/gz compress the file:</p>
<blockquote><p>> mysqldump --opt database > database.sql<br />
> tar -czvf database.tar.gz database.sql</p></blockquote>
<p>To export a database from the local mysql server to a remote mysql server:</p>
<blockquote><p>> mysqldump --opt -A | mysql --user=remote_user --password=remote_pw --host=xxx.xxx.xxx.xxx</p></blockquote>
<p>You're welcome!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/' rel='bookmark' title='Permanent Link: Automated Backups &#8211; A 5 Minute Script To Safer Data'>Automated Backups &#8211; A 5 Minute Script To Safer Data</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/' rel='bookmark' title='Permanent Link: Ruby On Rails and SliceHost Part 1: Initial Setup'>Ruby On Rails and SliceHost Part 1: Initial Setup</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2008/02/05/mysqldump-usage/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ruby On Rails and SliceHost Part 1: Initial Setup</title>
		<link>http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/</link>
		<comments>http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 20:42:54 +0000</pubDate>
		<dc:creator>Eric</dc:creator>
				<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[slicehost]]></category>

		<guid isPermaLink="false">http://perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/</guid>
		<description><![CDATA[I recently purchased a VPS from SliceHost.com. After ruling out a shared host and deciding on a VPS setup, SliceHost seemed to offer the most for my money. My initial setup is the 256slice, which offers 256mb RAM, 10GB of storage, and 100GB of bandwidth for $20 a month. Being able to build my own [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I recently purchased a VPS from <a href="http://www.slicehost.com">SliceHost.com</a>. After ruling out a shared host and deciding on a VPS setup, SliceHost seemed to offer the most for my money.   My initial setup is the 256slice, which offers 256mb RAM, 10GB of storage, and 100GB of bandwidth for $20 a month.  Being able to build my own stack from scratch is very appealing, especially as most of my side projects are written in <a href="http://www.rubyonrails.org/">Rails</a>.  My goal was to have <a href="http://httpd.apache.org/">Apache 2.2</a> as a front-end server, proxying requests to <a href="http://mongrel.rubyforge.org/">mongrel</a> or a <a href="http://mongrel.rubyforge.org/docs/mongrel_cluster.html">cluster of mongrels</a>, with <a href="http://dev.mysql.com/downloads/mysql/5.0.html">MySQL 5.0</a> as the database.  Of course I also wanted to have <a href="http://www.ruby-lang.org/en/downloads/">Ruby 1.8.6</a> and Rails 2.0.2.  Another useful, though optional, tool is <a href="http://www.phpmyadmin.net/home_page/index.php">phpMyAdmin</a>.  I am also using <a href="http://www.centos.org/">CentOS 5.0</a>, first because of my familiarity with it and second because it's well documented and supported.  The following is the first of a multipart tutorial about how I set this all up.  In future installments I will detail how to secure and optimize each installation as well as enable Apache proxying and mongrel clustering.</p>
<p><strong>Change the default root password</strong></p>
<blockquote><p>&gt;passwd</p></blockquote>
<p><strong>Update all yum packages</strong></p>
<blockquote><p>&gt;yum update</p></blockquote>
<p><strong>Bring in some standard packages</strong></p>
<blockquote><p>&gt;yum install wget openssl-devel lynx zlib zlib-devel vixie-cron curl lynx subversion make gcc automake</p></blockquote>
<p><strong>Install Apache</strong></p>
<blockquote><p>&gt;yum install httpd</p></blockquote>
<p><strong>Setup your domain records</strong><br />
<a href="http://articles.slicehost.com/2007/10/24/creating-dns-records"> http://articles.slicehost.com/2007/10/24/creating-dns-records</a></p>
<p><strong>Create your VirtualHosts in Apache's httpd.conf</strong><br />
<a href="http://httpd.apache.org/docs/2.2/vhosts"> http://httpd.apache.org/docs/2.2/vhosts</a><br />
Note: Apache's conf directory will be /etc/httpd/conf.  Don't forget to restart Apache after setting up each new vhost:</p>
<blockquote><p>&gt;service httpd restart</p></blockquote>
<p>See <a href="http://mongrel.rubyforge.org/docs/apache.html">http://mongrel.rubyforge.org/docs/apache.html</a> for details on how to get Apache and mongrel to play with each other.</p>
<p><strong>Start Apache</strong></p>
<blockquote><p>&gt;httpd service start</p></blockquote>
<p><strong>Install MySQL</strong></p>
<blockquote><p>&gt;yum install mysql mysql-server</p></blockquote>
<p>See <a href="http://dev.mysql.com/doc/">http://dev.mysql.com/doc/</a> for post-installation instructions (setting up initial accounts, etc.)</p>
<p><strong>Install Ruby (1.8.6)</strong></p>
<blockquote><p>&gt;wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz<br />
&gt;gzip -d ruby-1.8.6-p111.tar.gz<br />
&gt;tar xvf ruby-1.8.6-p111.tar.gz<br />
&gt;cd ruby-1.8.6-p111.tar.gz<br />
&gt;./configure<br />
&gt;make<br />
&gt;make install</p></blockquote>
<p><strong>Install Ruby Gems (1.0.1)</strong></p>
<blockquote><p>&gt;wget http://rubyforge.org/frs/download.php/29548/rubygems-1.0.1.tgz<br />
&gt;tar xvf rubygems-1.0.1.tgz<br />
&gt;cd rubygems-1.0.1.tgz<br />
&gt;ruby setup.rb</p></blockquote>
<p><strong>Install Rails (2.0.2)</strong></p>
<blockquote><p>&gt;gem install rails --include-dependencies</p></blockquote>
<p><strong>Install Mongrel</strong></p>
<blockquote><p>&gt;gem install mongrel</p></blockquote>
<p><strong>Optional for phpMyAdmin<br />
Install php</strong></p>
<blockquote><p>&gt;yum install php</p></blockquote>
<p><strong>Install php extensions necessary for phpMyAdmin</strong></p>
<blockquote><p>&gt;yum install php-mcrypt<br />
&gt;yum install php-mbstring</p></blockquote>
<p><strong>I</strong><strong>nstall phpmyadmin</strong><br />
<a href="http://www.phpmyadmin.net/documentation/#setup">http://www.phpmyadmin.net/documentation/#setup</a></p>
<p>Any comments or feedback is appreciated.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/15/setup-python-2-6-4-mod_wsgi-2-6-and-django-1-1-1-on-centos-5-3-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)'>Setup Python 2.6.4, mod_wsgi 2.6, and Django 1.1.1 on CentOS 5.3 (cPanel)</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2008/02/04/building-a-rails-capable-slice-from-scratch/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
