<?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>Journal de Jacques &#187; Technical Notes</title>
	<atom:link href="http://chester.id.au/category/technical-notes/feed/" rel="self" type="application/rss+xml" />
	<link>http://chester.id.au</link>
	<description>Things I write. Stuff that happens to me.</description>
	<lastBuildDate>Mon, 14 May 2012 07:48:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Installing the pg gem on OS X with MacPorts</title>
		<link>http://chester.id.au/2011/06/03/installing-the-pg-gem-on-os-x-with-macports/</link>
		<comments>http://chester.id.au/2011/06/03/installing-the-pg-gem-on-os-x-with-macports/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 03:01:05 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=471</guid>
		<description><![CDATA[The pg gem, used to connect ruby programs to the PostgreSQL server, is notoriously difficult to install on OS X. I&#8217;m using MacPorts and have PostgreSQL 9 installed. Here&#8217;s what I did: jacques$ sudo bash root$ export PATH=/opt/local/lib/postgresql90/bin:${PATH} root$ env &#8230; <a href="http://chester.id.au/2011/06/03/installing-the-pg-gem-on-os-x-with-macports/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The pg gem, used to connect ruby programs to the PostgreSQL server, is notoriously difficult to install on OS X.</p>
<p>I&#8217;m using MacPorts and have PostgreSQL 9 installed. Here&#8217;s what I did:<br />
<code><br />
jacques$ sudo bash<br />
root$ export PATH=/opt/local/lib/postgresql90/bin:${PATH}<br />
root$ env ARCHFLAGS="-arch x86_64" gem install pg<br />
root$ env ARCHFLAGS="-arch x86_64" gem install rdbi-driver-postgresql<br />
</code></p>
<p>First, the code switches us to the root user to avoid various hair-pulling problems with using &#8216;sudo&#8217; on commands starting with &#8216;env&#8217;.</p>
<p>Next we export the path where PostgreSQL lives in a MacPorts setup. You may need to adjust this path depending on the version you have installed.</p>
<p>Next, using &#8216;env&#8217;, we tell gem to install and compile a 64-bit version of the pg gem. Then we do the same again for the RDBI driver I&#8217;m using for a project.</p>
<p>And that&#8217;s it.</p>
<p>Leaving out any part, trust me, is a path to eternal madness.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2011/06/03/installing-the-pg-gem-on-os-x-with-macports/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fixing blogger imports into Wordpress</title>
		<link>http://chester.id.au/2011/02/17/fixing-blogger-imports-into-wordpress/</link>
		<comments>http://chester.id.au/2011/02/17/fixing-blogger-imports-into-wordpress/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 02:15:24 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=464</guid>
		<description><![CDATA[I&#8217;ve been expanding my Ozblogistan network and have run into an interesting bug. When importing from Google&#8217;s blogspot, all post titles and post content wind up with the &#8216;&#62;&#8217; symbol prepended. This is annoying. If you have database access, the &#8230; <a href="http://chester.id.au/2011/02/17/fixing-blogger-imports-into-wordpress/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been <a href="http://ozblogistan.com.au/2011/02/11/ozblogistan-will-grow-larger/">expanding my Ozblogistan</a> network and have run into an interesting bug.</p>
<p>When importing from Google&#8217;s blogspot, all post titles and post content wind up with the &#8216;&gt;&#8217; symbol prepended. This is annoying.</p>
<p>If you have database access, the fix is fairly straightforward:<br />
<code><br />
 update wp_posts set post_title = trim( leading '&gt;' from post_title );<br />
update wp_posts set post_content = trim( leading '&gt;' from post_content );<br />
</code></p>
<p>If you have multisite enabled, like me, you need to run this query for each affected blog, with the correct table number. So for example, if you have blogs 1-10, and blog 7 is affected, the table name might be &#8216;wp_7_posts&#8217; instead of &#8216;wp_posts&#8217;.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2011/02/17/fixing-blogger-imports-into-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More Mongrel2 / OS X notes</title>
		<link>http://chester.id.au/2011/02/13/more-mongrel2-os-x-notes/</link>
		<comments>http://chester.id.au/2011/02/13/more-mongrel2-os-x-notes/#comments</comments>
		<pubDate>Sun, 13 Feb 2011 05:57:09 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=460</guid>
		<description><![CDATA[Having a bit of spare time this Sunday, I&#8217;m continuing to work through the Mongrel2 manual (it&#8217;s good, as such things go). There are still places where Mac OS X does not behave according to the expectations of the manual &#8230; <a href="http://chester.id.au/2011/02/13/more-mongrel2-os-x-notes/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Having a bit of spare time this Sunday, I&#8217;m continuing to work through the <a href="http://mongrel2.org/doc/tip/docs/manual/book.wiki">Mongrel2 manual</a> (it&#8217;s good, as such things go).</p>
<p>There are still places where Mac OS X does not behave according to the expectations of the manual (written for a GNU/Linux environment).</p>
<ul>
<li>Most of the shortcut code in S4.5 doesn&#8217;t work on OS X.</li>
<li>When chowning files, remember that OS X does not have a root group &#8212; it&#8217;s called staff. So &#8220;chown root:staff&#8221; instead of &#8220;chown root:root&#8221;.</li>
<li>When you&#8217;ve configured procer to launch mongrel2, running ps aux | grep procer won&#8217;t show procer right away. It takes a second or two. Mongrel2 itself doesn&#8217;t seem to launch properly &#8230; until you issue the -murder command to m2sh. Then procer steps in to resurrect the undead &#8230;</li>
<li>To run the chat demo, you need to have installed the mongrel2 library and dependencies. Go to (mongrel_dir)/examples/python and run python setup.py install. You also need to install the python-zmq library for your system &#8212; I used macports.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2011/02/13/more-mongrel2-os-x-notes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building Mongrel2 on OS X with MacPorts</title>
		<link>http://chester.id.au/2011/02/08/building-mongrel2-on-os-x-with-macport/</link>
		<comments>http://chester.id.au/2011/02/08/building-mongrel2-on-os-x-with-macport/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 09:14:05 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=457</guid>
		<description><![CDATA[The documentation says &#8220;make all install&#8221; &#8212; use &#8220;make macports install&#8221; instead. That&#8217;s what I get for not reading the damn makefile first. Update: To build the procer utility mentioned in the Mongrel2 manual, a quick-and-dirty hack is to copy &#8230; <a href="http://chester.id.au/2011/02/08/building-mongrel2-on-os-x-with-macport/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://mongrel2.org/doc/tip/docs/manual/book.wiki#x1-110002">The documentation</a> says &#8220;make all install&#8221; &#8212; use &#8220;make macports install&#8221; instead.</p>
<p>That&#8217;s what I get for not reading the damn makefile first.</p>
<p><strong>Update: </strong> To build the procer utility mentioned in the Mongrel2 manual, a quick-and-dirty hack is to copy and paste the macport target lines from the main makefile into the procer makefile, then run &#8220;make clean macports&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2011/02/08/building-mongrel2-on-os-x-with-macport/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An XCache &#8216;gotcha&#8217;</title>
		<link>http://chester.id.au/2010/08/11/an-xcache-gotcha/</link>
		<comments>http://chester.id.au/2010/08/11/an-xcache-gotcha/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 03:15:26 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=414</guid>
		<description><![CDATA[XCache is a PHP opcode cacher. This is good and well as it speeds up pageloads by removing PHP parsing overhead. On the downside, you must remember to restart your PHP processes after changing PHP files, as XCache doesn&#8217;t seem &#8230; <a href="http://chester.id.au/2010/08/11/an-xcache-gotcha/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://xcache.lighttpd.net/">XCache</a> is a PHP opcode cacher. This is good and well as it speeds up pageloads by removing PHP parsing overhead. On the downside, you must remember to restart your PHP processes after changing PHP files, as XCache doesn&#8217;t seem to perform stat()s on the files to check if they&#8217;ve been updated.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2010/08/11/an-xcache-gotcha/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brian&#8217;s Latest Comments doesn&#8217;t scale.</title>
		<link>http://chester.id.au/2010/08/06/brians-latest-comments-doesnt-scale/</link>
		<comments>http://chester.id.au/2010/08/06/brians-latest-comments-doesnt-scale/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 06:10:37 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=399</guid>
		<description><![CDATA[Or, to be fair, MySQL 3.x is rubbish. But you knew that already. Most of the blogs on the Ozblogistan network use BLC. Recently, Larvatus Prodeo came on board and expected it to work for them too. But it simply &#8230; <a href="http://chester.id.au/2010/08/06/brians-latest-comments-doesnt-scale/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Or, to be fair, MySQL 3.x is rubbish. But you knew that already.</p>
<p>Most of the blogs on the <a href="http://ozblogistan.com.au/">Ozblogistan</a> network use <a href="http://code.google.com/p/brianslatestcomments/">BLC</a>. Recently, <a href="http://larvatusprodeo.net/">Larvatus Prodeo</a> came on board and expected it to work for them too. But it simply hung without giving much in the way of error message. I pottered around with it at the time without much success.</p>
<p>Today it occurred to me that the problem is the way BLC works. BLC aims at being compatible with MySQL 3.x. Among other things, this means giving up subqueries and having piss-poor SELECT DISTINCT.</p>
<p>So BLC issues this beauty of a query:<br />
<code><br />
SELECT comment_post_ID, post_title<br />
FROM (wp_comments LEFT JOIN wp_posts ON (comment_post_ID = ID))<br />
WHERE comment_approved = '1'<br />
AND wp_posts.post_status='publish'<br />
AND comment_type'pingback'<br />
AND comment_type'trackback'<br />
ORDER BY comment_date DESC;<br />
</code></p>
<p>This query pulls out the post ID and post_title for every approved, published comment in the database. On a site like Larvatus Prodeo, the results run to ~165k lines. BLC pulls these results into PHP as objects, one for each line; then it merges down the results to determine which posts have comments.</p>
<p>As you can imagine, this is memory intensive. Very memory intensive. And it&#8217;s a bit brutal on the CPU too, especially when garbage collection occurs. The upshot is that on Larvatus Prodeo, BLC exceeds the memory limits for PHP, and that for other sites, it adds about between 600 and 1200 milliseconds of processing time. Not cool.</p>
<p>Naturally, I&#8217;m running MySQL 5.x on my server, so SELECT DISTINCT works &#8216;as advertised&#8217;. So I&#8217;ve implemented a super high tech optimisation on BLC:</p>
<p><code><br />
SELECT <strong>DISTINCT</strong> comment_post_ID, post_title<br />
FROM (wp_comments LEFT JOIN wp_posts ON (comment_post_ID = ID))<br />
WHERE comment_approved = '1'<br />
AND wp_posts.post_status='publish'<br />
AND comment_type'pingback'<br />
AND comment_type'trackback'<br />
ORDER BY comment_date DESC;<br />
</code></p>
<p>While this query is slower than the original, it basically returns only the list of posts with comments on them. On Larvatus Prodeo, this is a much more manageable ~2.5k lines of results. The reduction in PHP runtime completely overshadows the increase in query time; and besides, the smaller result sets don&#8217;t clog up the MySQL query cache.</p>
<p>So there you have it: a one-word way to dramatically improve the performance of Brian&#8217;s Latest Comments on MySQL 5.x-backed Wordpress.</p>
<p><strong>Update:</strong> Two additional optimisations are worthy. First, add a LIMIT clause to your copy of BLC along the lines of:<br />
<code><br />
 $posts = $wpdb-&gt;get_results("SELECT DISTINCT<br />
                comment_post_ID, post_title<br />
                FROM ($wpdb-&gt;comments LEFT JOIN $wpdb-&gt;posts ON (comment_post_ID = ID))<br />
                WHERE comment_approved = '1'<br />
                AND $wpdb-&gt;posts.post_status='publish'<br />
                $ping<br />
                ORDER BY comment_date DESC<br />
                LIMIT $num_posts;");<br />
</code></p>
<p>Second, the SELECT DISTINCT hammers the tables much harder, so add indexes for the relevant fields:</p>
<p><code><br />
CREATE INDEX post_status_index ON wp_posts(post_status);<br />
CREATE INDEX comment_date_index ON wp_comments(comment_date);<br />
CREATE INDEX comment_type_index ON wp_comments(comment_type);<br />
CREATE INDEX comment_date_approved_index ON wp_comments(comment_date_gmt);<br />
CREATE INDEX post_title_index ON wp_posts(post_title(50));<br />
</code></p>
<p><strong>Update 2:</strong> The query above doesn&#8217;t order the results according to the time of the latest comments; it winds up ordering by posting date. Not what my users want. Try this instead:</p>
<p><code>   $posts = $wpdb-&gt;get_results("SELECT comment_post_ID, post_title, max(comment_date) AS max_date<br />
                FROM ($wpdb-&gt;comments LEFT JOIN $wpdb-&gt;posts ON (comment_post_ID = ID))<br />
                WHERE comment_approved = '1'<br />
                AND $wpdb-&gt;posts.post_status='publish'<br />
                $ping<br />
                GROUP BY post_title<br />
                ORDER BY max_date DESC<br />
                LIMIT $num_posts;");<br />
</code></p>
<p><strong>Update 3:</strong> Nope. Performance is still atrocious. I&#8217;ll revisit this in a few weeks when I&#8217;ve settled in Darwin.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2010/08/06/brians-latest-comments-doesnt-scale/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>More tweaking for Ozblogistan</title>
		<link>http://chester.id.au/2010/08/06/more-tweaking-for-ozblogistan/</link>
		<comments>http://chester.id.au/2010/08/06/more-tweaking-for-ozblogistan/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 05:57:25 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=392</guid>
		<description><![CDATA[I did some minor optimisations on Ozblogistan last weekend. I have HTTP compression enabled on the webserver (gzip, to be precise). I found out through the excellent YSlow plugin that not everything was being compressed. HTML was compressed, but not &#8230; <a href="http://chester.id.au/2010/08/06/more-tweaking-for-ozblogistan/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I did some minor optimisations on Ozblogistan last weekend.</p>
<p>I have HTTP compression enabled on the webserver (gzip, to be precise). I found out through the excellent YSlow plugin that not everything was being compressed. HTML was compressed, but not style sheets or scripts.</p>
<p>The nginx settings are now as follows:</p>
<p><code>    gzip  on;<br />
    gzip_buffers 128 8k;<br />
    gzip_types text/plain application/xml text/xml application/xml+rss text/css application/x-javascript text/javascript application/xhtml+xml;<br />
    gzip_disable "MSIE [1-6].(?!.*SV1)";<br />
</code></p>
<p>This provides a large buffer to prevent unexpected hangs. I&#8217;ve added mimetypes for javascript, RSS feeds and CSS files.</p>
<p>On the MySQL front, I received some advice from MySQL expert <a href="http://blog.dbadojo.com/">Paul Moen</a> of <a href="http://www.pythian.com/">Pythian</a>.</p>
<p>Paul&#8217;s main advice upon looking over the MySQL settings was to increase the key_buffer variable from 8Mb. It&#8217;s currently at 96Mb. This has sped up some queries. He also advised enabling and watching MySQL&#8217;s slow query log. This helped me spot queries where indexes might speed up things, so I added a few to relevant columns which also reduced the number of slow queries. Thanks Paul.</p>
<p>Another change was to notice that the advice for setting connection_max in MySQL is essentially Apache/mod_php-specific. It doesn&#8217;t make sense in a FastCGI context. In FastCGI you launch a fixed number of PHP instances. The number of connections from Wordpress into MySQL is limited to the number of PHP instances; so there&#8217;s no requirement to set a higher connection_max setting. This is not an optimisation <em>per se</em>, but it tidies things up a bit.</p>
<p>I double checked my PHP opcode cacher XCache and found that it was not enabled. Annoyingly, aptitude will install but not activate this for you. To actually enable it, it&#8217;s necessary to edit the php.ini file and add a line:<br />
<code>extension=xcache.so</code><br />
Somewhere in the file.</p>
<p>Previously I have added favicons. Watching the error log, I saw requests for apple-touch-icon.png. Apple, in their infinite wisdom, have decided to add Yet Another Assumed File to the list of things webmasters need to know about. I&#8217;ve gone through and added icons for each of the Ozblogistan sites. It was, I must tell you, tedious and time-consuming. Though Apple currently scale the images to 57&#215;57, they will almost definitely upgrade this in future, so I&#8217;ve placed 128&#215;128 icons for now.</p>
<p>Finally, I adjusted the &#8216;swappiness&#8217; of the server. I configure PHP and MySQL to occupy less than the full RAM available; thus, I don&#8217;t want anything swapped out to disk. I&#8217;ve set swappiness to 0, which tells Linux to keep applications in memory at all times until it has no choice other than to swap them out.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2010/08/06/more-tweaking-for-ozblogistan/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An nginx/PHP gotcha</title>
		<link>http://chester.id.au/2010/01/20/an-nginxphp-gotcha/</link>
		<comments>http://chester.id.au/2010/01/20/an-nginxphp-gotcha/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 07:46:34 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=354</guid>
		<description><![CDATA[If you are allowing larger-than-default files to be uploaded to an nginx server with PHP FCGI, you need to alter both the php.ini and the nginx.conf. In particular, for nginx.conf, you need to use the client_max_body_size directive to set the &#8230; <a href="http://chester.id.au/2010/01/20/an-nginxphp-gotcha/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>If you are allowing larger-than-default files to be uploaded to an nginx server with PHP FCGI, you need to alter <em>both</em> the php.ini and the nginx.conf.</p>
<p>In particular, for nginx.conf, you need to use the <code>client_max_body_size</code> directive to set the permissible maximum upload size.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2010/01/20/an-nginxphp-gotcha/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automated backups for Ozblogistan</title>
		<link>http://chester.id.au/2010/01/11/automated-backups-for-ozblogistan/</link>
		<comments>http://chester.id.au/2010/01/11/automated-backups-for-ozblogistan/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 03:39:34 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=323</guid>
		<description><![CDATA[It&#8217;s a cliche amongst nerds that everyone preaches automated backups, and very few have it. Partly because it&#8217;s surprisingly fiddly to set up &#8220;right&#8221;. And even fiddlier to do restoration testing. Still, one thing at a time. I&#8217;ve just now &#8230; <a href="http://chester.id.au/2010/01/11/automated-backups-for-ozblogistan/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s a cliche amongst nerds that everyone preaches automated backups, and very few have it.</p>
<p>Partly because it&#8217;s surprisingly fiddly to set up &#8220;right&#8221;. And even fiddlier to do restoration testing.</p>
<p>Still, one thing at a time. I&#8217;ve just now finished putting together an automatic backup regime for Ozblogistan, the server I run hosting Skepticlawyer and Andrew Norton (and, at some time in the future, some other sites too).</p>
<p>Herewith my notes.</p>
<h3>Tarsnap</h3>
<p>I am using the <a href="http://tarsnap.com/">tarsnap backup service</a>. I am satisfied that it&#8217;s cheap, efficient, reliable and secure. The trickiest part to wrap my modest brain around is its snapshot-based nature. You don&#8217;t follow the full-and-incremental model with tarsnap: you simply list what you want to backup and let it sort out the details of the most efficient way to store that for you.</p>
<p>I have a very simple shell script which cron runs each day:</p>
<p><code><br />
#!/bin/bash<br />
# Quick and dirty script to backup files, settings and the database.</p>
<p>DATE=`date +%Y-%m-%d`<br />
SQL_FILE="/var/backups/mysqldump/ozblogistan-wordpressmu-$DATE.sql"<br />
BACKUP_ARCHIVE_NAME="ozblogistan-$DATE"</p>
<p># Dump SQL<br />
mysqldump --defaults-extra-file=/etc/tarsnap/mysqldump.cnf  -eltn --dump-date --default-character-set="latin1" wordpressmu &gt; $SQL_FILE</p>
<p># Perform tarsnap backup<br />
tarsnap -c -f $BACKUP_ARCHIVE_NAME --exclude *cache* --exclude *.svn* /home /etc /var/www/wordpressmu /var/backups/mysqldump</p>
<p># Delete mysqldump</p>
<p>rm -f $SQL_FILE<br />
</code></p>
<p>This script performs a mysqldump of the database. It tells tarsnap to back that up in addition to /etc, /home and /var/www/wordpressmu, ignoring cache directories and SVN directories. It creates a &#8220;new&#8221; tarsnap archive each day; in practice tarsnap will only send a delta of the SQL plus any new files uploaded to the Wordpress installation.</p>
<p>I&#8217;ve chosen not to compress the SQL, as I am unsure whether that will interfere with the delta process used by tarsnap. Compression can change the layout of a file.</p>
<h3>Mysqldump settings</h3>
<p>The mysqldump commandline in this shell script has two key features to note. Firstly, the use of the &#8211;default-character-set option, necessary to <a href="http://chester.id.au/2009/12/16/moving-a-database-from-wordpress-to-wordpress-mu/">circumvent MySQL retardation</a>. Secondly, the use of the &#8211;defaults-extra-file option to import settings from a custom configuration file.</p>
<p>The custom configuration file contains the username and password of a particular backup user. This backup user is distinct from the user supplied to Wordpress Mu to access the wordpressmu database. The backup user has very limited privileges: essentially it can read but not alter the database, which is all the permissions it needs (ie. it has SELECT, LOCK TABLES on the database and RELOAD globally).</p>
<p>Placing those identifying details in a configuration file means that they will not be visible to someone running top, ps et al. A very modest security improvement, I grant you, but still.</p>
<h3>Still to do</h3>
<p><strike>I still need to set up something similar for troppo. We have automated backups there but they&#8217;re not as efficient or reliable. Ideally I&#8217;d prefer to move troppo to the new server, but that may not be possible.</strike> This is done.</p>
<p>I also need to develop an automatic restoration testing facility. It&#8217;s common to think you have &#8220;flawless&#8221; backups, then discover after disaster that your backups were no good.</p>
<p>Finally I need to add code to delete old archives. Tarsnap&#8217;s snapshotting model is very efficient and the rate of change slow, so for now I will leave it open-ended to get a sense of how far back I can keep backups.</p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2010/01/11/automated-backups-for-ozblogistan/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Moving a database from Wordpress to Wordpress Mu</title>
		<link>http://chester.id.au/2009/12/16/moving-a-database-from-wordpress-to-wordpress-mu/</link>
		<comments>http://chester.id.au/2009/12/16/moving-a-database-from-wordpress-to-wordpress-mu/#comments</comments>
		<pubDate>Tue, 15 Dec 2009 20:13:45 +0000</pubDate>
		<dc:creator>Jacques Chester</dc:creator>
				<category><![CDATA[Technical Notes]]></category>

		<guid isPermaLink="false">http://chester.id.au/?p=288</guid>
		<description><![CDATA[Some rough notes, before I forgot what I did. mysqldump --default-character-set="latin1" -elt database_name &#124; sed 's/wp_/wp_{new blogs id}_/' &#62; database_name.sql &#8211;default-character-set is required because of MySQL&#8217;s unrestrained enthusiasm for fucking up encoding by assuming everything is encoded in utf8. Would &#8230; <a href="http://chester.id.au/2009/12/16/moving-a-database-from-wordpress-to-wordpress-mu/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Some rough notes, before I forgot what I did.</p>
<p><code>mysqldump --default-character-set="latin1" -elt database_name | sed 's/wp_/wp_{new blogs id}_/' &gt; database_name.sql</code></p>
<p>&#8211;default-character-set is required because of MySQL&#8217;s unrestrained enthusiasm for fucking up encoding by assuming everything is encoded in utf8. Would it kill them to have mysqldump simply look up the encoding first? Apparently the answer is &#8220;yes&#8221;.</p>
<p>Speaking of which, you need to change the my.cnf file to default to utf8, and tell your webserver to serve utf8, AND tell PHP to default to utf8. Then the content will come across OK.</p>
<p>Zip the dumpfile, download, upload. Then play to the mysql line program.</p>
<p><code>mysql -u root -p target_database &lt; database_name.sql</code></p>
<p>Be prepared for warts. In particular older installations of Wordpress carry _category columns on some tables which will trip up the mysql insert. The solution is to drop those columns from the source database first.</p>
<p>You also need to update the URLs. For reasons inscrutable to mere mortals, Wordpress Mu doesn&#8217;t store uploaded files in wp-content/uploads. Instead it changes that to wp-content/files. This serves no purpose, so far as I can tell, but it does force me to make updates to the database:</p>
<p></code><code>update wp_{newblogid}_posts set post_content = replace (post_content, '/wp-content/uploads/', '/wp-content/files/') where post_content like '%/uploads/%';</code></p>
<p><code>update wp_{newblogid}_posts set guid = replace (guid, '/wp-content/uploads/', '/wp-content/files/') where guid like '%/uploads/%';<br />
</code></p>
<p>Edit: commands used to move skepticlawyer.<br />
<code><br />
# Change user ids and import</p>
<p>update wp_usermeta set user_id = 6 where user_id = 4;<br />
update wp_users set ID = 6 where ID = 4;</p>
<p>mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_usermeta | sed 's/wp_/wp_3_/' &gt; sl.usermeta.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_users | sed 's/wp_/wp_3_/' &gt; sl.users.sql</p>
<p># Update user ids on posts and comments<br />
update wp_posts set post_author = 6 where post_author = 4;<br />
update wp_comments set user_id = 6 where user_id = 4;</p>
<p># Update URLs for files and attachments<br />
update wp_posts set post_content = replace (post_content, '/wp-content/uploads/', '/wp-content/files/') where post_content like '%/uploads/%';<br />
update wp_posts set guid = replace (guid, '/wp-content/uploads/', '/wp-content/files/') where guid like '%/uploads/%';</p>
<p># Dump tables<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_comments | sed 's/wp_/wp_3_/' &gt; sl.comments.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_links | sed 's/wp_/wp_3_/' &gt; sl.links.sql</p>
<p>mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_postmeta | sed 's/wp_/wp_3_/' &gt; sl.postmeta.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_posts | sed 's/wp_/wp_3_/' &gt; sl.posts.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_term_relationships | sed 's/wp_/wp_3_/' &gt; sl.term_relationships.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_term_taxonomy | sed 's/wp_/wp_3_/' &gt; sl.term_taxonomy.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_terms | sed 's/wp_/wp_3_/' &gt; sl.terms.sql<br />
mysqldump --default-character-set="latin1" --skip-opt --insert-ignore -elt skepticlawyer wp_options | sed 's/wp_/wp_3_/' &gt; sl.options.sql</p>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://chester.id.au/2009/12/16/moving-a-database-from-wordpress-to-wordpress-mu/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

