<?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>dead fish &#187; Work</title>
	<atom:link href="http://www.thomaskeller.biz/blog/category/work/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.thomaskeller.biz/blog</link>
	<description>only dead fish swim with the stream</description>
	<lastBuildDate>Tue, 27 Sep 2011 22:59:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>jazzlib &#8211; an alternative for reading ZIP files in Java</title>
		<link>http://www.thomaskeller.biz/blog/2011/05/03/jazzliban-alternative-for-reading-zip-files-in-java/</link>
		<comments>http://www.thomaskeller.biz/blog/2011/05/03/jazzliban-alternative-for-reading-zip-files-in-java/#comments</comments>
		<pubDate>Tue, 03 May 2011 14:24:17 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=1023</guid>
		<description><![CDATA[Java had zip-reading capabilities for a long time, naturally because jar files are simply compressed zip files with some meta data. The needed classes reside in the java.util.zip namespace and are ZipInputStream and ZipEntry. Recently, however, ZipInputStream gave me a huge headache. My use case was as simple as read the zip entries of a [...]]]></description>
			<content:encoded><![CDATA[<p>Java had zip-reading capabilities for a long time, naturally because <code>jar</code> files are simply compressed zip files with some meta data. The needed classes reside in the <code>java.util.zip</code> namespace and are <code>ZipInputStream</code> and <code>ZipEntry</code>.</p>

<p>Recently, however, <code>ZipInputStream</code> gave me a huge headache. My use case was as simple as</p>

<ul>
<li>read the zip entries of a list of zip files (each varying in size, but usually around 20MB)</li>
<li>skip to the zip entry that has a certain name (a single text file with only two bytes of contents)</li>
<li>read the contents of this zip entry and close the zip</li>
</ul>

<p>Doing this for about 25 files took my Pentium D (2GHz) with 3GB of RAM roughly <strong>20 seconds</strong>. Wow, 20 seconds really? I created a test case and profiled the code in question separately with <a href="http://www.yourkit.com">YourKit</a> (which is a really great tool, by the way!):</p>

<p><a href="http://www.thomaskeller.biz/blog/wp-content/uploads/2011/05/yourkit.png"><img src="http://www.thomaskeller.biz/blog/wp-content/uploads/2011/05/yourkit-450x114.png" alt="" title="yourkit" width="450" height="114" class="alignright size-medium wp-image-1024" /></a></p>

<p>It got stuck quite a bit in <code>java.util.zip.Inflater.inflateBytes</code> &#8211; but that seemed to use native code, so I couldn&#8217;t profile any further.</p>

<p>So I went on and searched for an alternative of <code>java.util.zip</code> &#8211; and luckily I found one with <a href="http://jazzlib.sourceforge.net">jazzlib</a>, which provides a pure Java implementation for ZIP compression and decompression. This library is GPL-licensed (with a small exception clause to prevent the pervasiveness of the GPL) and comes in two versions, one that duplicates the single library classes underknees <code>java.util.zip</code> (as a drop-in replacement for JDK versions where this is missing) and one that comes in its own namespace, <code>net.sf.jazzlib</code>.</p>

<p>After I went for the second version, I restarted my test and it only took about <strong>7 seconds</strong> this time. At first I thought that there must be some downside to this approach, so I checked the timings for a complete decompression of the archive, but the timings here were on par with the ones from <code>java.util.zip</code> (roughly 5 seconds for a single 20MB file).</p>

<p>I haven&#8217;t tested compression speed, because it doesn&#8217;t matter much for my use case, but the decompression speed alone is astonishing. I wonder why nobody else stumbled upon these performance problems before&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2011/05/03/jazzliban-alternative-for-reading-zip-files-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debugging Java WebStart applications with Eclipse</title>
		<link>http://www.thomaskeller.biz/blog/2011/01/07/debugging-java-webstart-applications-with-eclipse/</link>
		<comments>http://www.thomaskeller.biz/blog/2011/01/07/debugging-java-webstart-applications-with-eclipse/#comments</comments>
		<pubDate>Fri, 07 Jan 2011 13:49:43 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=947</guid>
		<description><![CDATA[The last couple of days I went through a little nightmare: I needed to debug a Java application which showed some weird behaviour only when loaded via WebStart, but not when executed within Eclipse. Multiple internet resources told me to use the JAVAWS_VM_ARGS environment variable to tell the Java VM to either create a debug [...]]]></description>
			<content:encoded><![CDATA[<p>The last couple of days I went through a little nightmare: I needed to debug a Java application which showed some weird behaviour only when loaded via WebStart, but not when executed within Eclipse.</p>

<p><a href="http://www.jacoozi.com/index.php?option=com_content&amp;task=view&amp;id=119&amp;Itemid=134">Multiple</a> <a href="http://javaswamy.blogspot.com/2008/10/debugging-jws-with-eclipse.html">internet</a> <a href="http://www.ibm.com/developerworks/java/library/os-eclipse-javadebug/index.html">resources</a> told me to use the <code>JAVAWS_VM_ARGS</code> environment variable to tell the Java VM to either create a debug socket itself or connect to some socket started from Eclipse and then simply set a breakpoint there and wait for the meal. But nothing worked out for me, until I found two very important issues nobody wrote about so far:</p>

<ul>
<li>Under Java 6 <code>JAVAWS_VM_ARGS</code> seems to be completely ignored by <code>javaws</code>, on Linux and on Windows. The way to go was to use separate <code>-J</code> options in the call to <code>javaws</code> &#8211; and given a running Eclipse debug server you finally saw the threads of your running application.</li>
<li>Wait, were these threads really belonging to <em>your</em> running application? This was the second issue. Apparently <code>javaws</code> directly forks away as soon as it started and runs your application in a separate VM, which <em>of course</em> did not get the original debug options passed. Again, some internet resources said it would be enough to load the jnlp file from the net and start locally, but actually this did not work out. The magic option to apply to <code>javaws</code> here was <code>-Xnofork</code> and finally, on the next run I saw my application&#8217;s threads in Eclipse and my breakpoints magically worked as expected.</li>
</ul>

<p>Here is the complete command line example again (given a running Eclipse debug server on port 8000):</p>

<pre><code>javaws -Xnofork -J-Xdebug -J-Xnoagent \
    -J-Xrunjdwp:transport=dt_socket,server=n,address=8000 \
    path/to/app.jnlp
</code></pre>

<p>Hope that helps someone <img src='http://www.thomaskeller.biz/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2011/01/07/debugging-java-webstart-applications-with-eclipse/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>#112123</title>
		<link>http://www.thomaskeller.biz/blog/2010/11/24/112123/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/11/24/112123/#comments</comments>
		<pubDate>Tue, 23 Nov 2010 23:40:10 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Rants]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=926</guid>
		<description><![CDATA[Today I completed my ScrumMaster certification. After the two days with Joseph Pelrine in early November they finally send me some login for their website scrumalliance.com, offered a short web-based test (which you couldn&#8217;t really fail) and tada &#8211; now I can call myself &#8220;Certified ScrumMaster&#8221;. So everything seemed to look fine, but wait, why [...]]]></description>
			<content:encoded><![CDATA[<p>Today I completed my ScrumMaster certification. After the two days with <a href="http://www.thomaskeller.biz/blog/2010/11/05/why-i-do-open-source-development/">Joseph Pelrine in early November</a> they finally send me some login for their website scrumalliance.com, offered a short web-based test (which you couldn&#8217;t really fail) and tada &#8211; now I can call myself <a href="/bewerbung/csm.pdf">&#8220;Certified ScrumMaster&#8221;</a>.</p>

<p align="center">
<img src="http://www.thomaskeller.biz/blog/wp-content/uploads/2010/11/csm-logo.png" alt="" title="csm-logo" width="250" class="size-full wp-image-927" />
</p>

<p>So everything seemed to look fine, but wait, why has this certificate an expiry date tacked on it? Uh, yes, erm, I&#8217;m supposed to renew it every year beginning from 2012 for 50 bucks. Its not like your drivers license &#8211; once you&#8217;ve learned to ride your car you cannot unlearn it, right? All the knowledge about Scrum is of course totally forgotten and buried in my head when the magic year 2012 has been reached&#8230; now thats business!</p>

<p>But hey, I got something in return. I&#8217;m listed with the other 112122 fellows on their website as CSM and are &#8220;allowed&#8221; to use the term &#8220;CSM&#8221;, the &#8220;logo&#8221; (which I had to extract via photoshop from the certificate itself, because they do not offer a download) and the nice little Scrum community they build around the website.</p>

<p>Thank god that Joseph was entertaining and educational enough for the two days in November that all the (company) money was worth the certification. The certificate itself and the &#8220;services&#8221; around it are certainly not worth it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/11/24/112123/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The fuzzy cloud</title>
		<link>http://www.thomaskeller.biz/blog/2010/11/22/the-fuzzy-cloud/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/11/22/the-fuzzy-cloud/#comments</comments>
		<pubDate>Sun, 21 Nov 2010 23:48:19 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=916</guid>
		<description><![CDATA[Until now &#8220;the cloud&#8221; (the computing paradigm) was for me more or less a fuzzy defined hype I missed to really &#8220;see&#8221; the advantages of: I delegate infrastructure resources to some external &#8220;hoster&#8221; and pay for that, but I instantly got this nagging fear that I&#8217;ll loose full control over my resources and data when [...]]]></description>
			<content:encoded><![CDATA[<p>Until now &#8220;the cloud&#8221; (the computing paradigm) was for me more or less a fuzzy defined hype I missed to really &#8220;see&#8221; the advantages of: I delegate infrastructure resources to some external &#8220;hoster&#8221; and pay for that, but I instantly got this nagging fear that I&#8217;ll loose full control over my resources and data when I&#8217;d do so.</p>

<p>While I am (unfortunately) not in the right business for experiencing and working with &#8220;the cloud&#8221; myself, I&#8217;m still very interested in learning about the possibilities and changes cloud computing introduces, especially from the development point of view.</p>

<p>Today I now stumbled upon <a href="http://perfcap.blogspot.com/">Adrian Cockcroft</a>&#8216;s outstanding <a href="http://www.slideshare.net/adrianco/netflix-on-cloud-combined-slides-for-dev-and-ops">presentation</a> in which he outlines how Netflix, the company he&#8217;s working for, introduced cloud-based services step by step as a replacement for traditional data center services and monolithic architectures. After reading through the roughly hundred slides I can say I&#8217;m much more confident to believe that all this <em>is</em> the right direction in the future, also (but not solely) because it forces us developers to write much more domain-independent, easy servicable, stripped down code.</p>

<p>So if you have 30 minutes for a very good introduction in practical cloud computing, I urge you to <a href="http://www.slideshare.net/adrianco/netflix-on-cloud-combined-slides-for-dev-and-ops">go ahead and read the slides</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/11/22/the-fuzzy-cloud/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL partitioning benchmark</title>
		<link>http://www.thomaskeller.biz/blog/2010/08/04/mysql-partitioning/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/08/04/mysql-partitioning/#comments</comments>
		<pubDate>Wed, 04 Aug 2010 12:41:49 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Free Software]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=735</guid>
		<description><![CDATA[I had a little research task today at work where I needed to evaluate which MySQL storage engine and technique would be the fastest to retrieve lots of (like millions) log data. I stumbled upon this post which explained the new horizontal partitioning features of MySQL 5.1 and what I read there made me curious [...]]]></description>
			<content:encoded><![CDATA[<p>I had a little research task today at work where I needed to evaluate which MySQL storage engine and technique would be the fastest to retrieve lots of (like millions) log data. I stumbled upon <a href="http://dev.mysql.com/tech-resources/articles/performance-partitioning.html">this post</a> which explained the new horizontal partitioning features of MySQL 5.1 and what I read there made me curious to test it out myself, also because the original author forgot to include a test with a (non-)partitioned, but indexed table.</p>

<p>This is my test setup: Linux 2.6.34, MySQL community server 5.1.46, Intel Pentium D CPU with 3.2GHz, 2GB RAM</p>

<h4>Test MyISAM tables</h4>

<p>The table definitions are copied and adapted from the aforementioned article:</p>

<div class="geshi no sql"><ol><li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> myi_no_part <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> engine<span class="sy0">=</span>MyISAM;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> myi_no_part_index <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">INDEX</span><span class="br0">&#40;</span>c3<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> engine<span class="sy0">=</span>MyISAM;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> myi_part <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> PARTITION <span class="kw1">BY</span> RANGE <span class="br0">&#40;</span>year<span class="br0">&#40;</span>c3<span class="br0">&#41;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#40;</span>PARTITION p0 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1995</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p1 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1996</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p2 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1997</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p3 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1998</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p4 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1999</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p5 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2000</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p6 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2001</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p7 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2002</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p8 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2003</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p9 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2004</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p10 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2010</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p11 <span class="kw1">VALUES</span> LESS THAN MAXVALUE<span class="br0">&#41;</span> </div></li>
<li class="li1"><div class="de1">&nbsp;engine<span class="sy0">=</span>MyISAM;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> myi_part_index <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw1">INDEX</span><span class="br0">&#40;</span>c3<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> PARTITION <span class="kw1">BY</span> RANGE <span class="br0">&#40;</span>year<span class="br0">&#40;</span>c3<span class="br0">&#41;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#40;</span>PARTITION p0 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1995</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p1 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1996</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p2 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1997</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p3 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1998</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p4 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1999</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p5 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2000</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p6 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2001</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p7 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2002</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p8 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2003</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p9 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2004</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p10 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2010</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p11 <span class="kw1">VALUES</span> LESS THAN MAXVALUE<span class="br0">&#41;</span> </div></li>
<li class="li1"><div class="de1">&nbsp;engine<span class="sy0">=</span>MyISAM;</div></li></ol></div>

<h4>Test Archive tables</h4>

<p>Since MySQL&#8217;s Archive engine does only support one index which is primarily used for identifying the primary id, I left out the indexed versions for that:</p>

<div class="geshi no sql"><ol><li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> ar_no_part <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> engine<span class="sy0">=</span>Archive;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> ar_part <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; c1 int <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c2 varchar<span class="br0">&#40;</span><span class="nu0">30</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; c3 date <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw1">INDEX</span><span class="br0">&#40;</span>c3<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span> PARTITION <span class="kw1">BY</span> RANGE <span class="br0">&#40;</span>year<span class="br0">&#40;</span>c3<span class="br0">&#41;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#40;</span>PARTITION p0 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1995</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p1 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1996</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p2 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1997</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p3 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1998</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p4 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">1999</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p5 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2000</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p6 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2001</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p7 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2002</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p8 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2003</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p9 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2004</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p10 <span class="kw1">VALUES</span> LESS THAN <span class="br0">&#40;</span><span class="nu0">2010</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp;PARTITION p11 <span class="kw1">VALUES</span> LESS THAN MAXVALUE<span class="br0">&#41;</span> </div></li>
<li class="li1"><div class="de1">&nbsp;engine<span class="sy0">=</span>Archive;</div></li></ol></div>

<h4>Test data</h4>

<p>I re-used the procedure to create about 8 million test data records spread randomly over the complete partitioned area and subsequently copied the generated data to the other tables:</p>

<div class="geshi no sql"><ol><li class="li1"><div class="de1">delimiter //</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">CREATE</span> PROCEDURE load_part_tab<span class="br0">&#40;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;begin</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; declare v int <span class="kw1">DEFAULT</span> <span class="nu0">0</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while v <span class="sy0">&lt;</span> <span class="nu0">8000000</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; do</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">INSERT</span> <span class="kw1">INTO</span> myi_no_part</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">VALUES</span> <span class="br0">&#40;</span>v,<span class="st0">&#39;testing partitions&#39;</span>,adddate<span class="br0">&#40;</span><span class="st0">&#39;1995-01-01&#39;</span>,<span class="br0">&#40;</span>rand<span class="br0">&#40;</span>v<span class="br0">&#41;</span>*<span class="nu0">36520</span><span class="br0">&#41;</span> mod <span class="nu0">3652</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw1">SET</span> v <span class="sy0">=</span> v + <span class="nu0">1</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; end while;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;end</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;//</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">delimiter ;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">call load_part_tab;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">INSERT</span> <span class="kw1">INTO</span> myi_no_part_index <span class="kw1">SELECT</span> * <span class="kw1">FROM</span> myi_no_part;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&#8230;</div></li></ol></div>

<h4>Test query and the results</h4>

<p>I used the same query to retrieve data from all of the tables:</p>

<div class="geshi no sql"><ol><li class="li1"><div class="de1"><span class="kw1">SELECT</span> count<span class="br0">&#40;</span>*<span class="br0">&#41;</span> <span class="kw1">FROM</span> TABLE_NAME </div></li>
<li class="li1"><div class="de1"><span class="kw1">WHERE</span> c3 <span class="sy0">&gt;</span> date <span class="st0">&#39;1995-01-01&#39;</span> <span class="kw1">AND</span> c3 <span class="sy0">&lt;</span> date <span class="st0">&#39;1995-12-31&#39;</span>;</div></li></ol></div>

<p>and these were the results (mean values of several executions):</p>

<table border="1">
<tr>
<th>table</th>
<th>exec time</th>
</tr>
<tr>
<td>`myi_no_part`</td>
<td>~ 6.4s</td>
</tr>
<tr>
<td>`myi_no_part_index`</td>
<td>~ 1.2s</td>
</tr>
<td>`myi_part`</td>
<td><span style="color: green">~ 0.7s</span></td>

<tr>
<td>`myi_part_index`</td>
<td>~ 1.3s</td>
</tr>
<tr>
<td>`ar_no_part`</td>
<td><span style="color: red">~ 10.2s</span></td>
</tr>
<tr>
<td>`ar_part`</td>
<td>~ 1.1s</td>
</tr>
</table>

<p>These results were actually pretty suprising to me, for various reasons:</p>

<ul>
<li>I would not have thought that intelligent partitioning would beat an index on the particular column by saving the hard disk space for the index at the same time (roughly 1/3 of the total data size in this test case).</li>
<li>The values for `myi_no_part` were actually better than expected &#8211; I would have thought that these should be much worse, also if you compare them with the values from the author of the original article.</li>
<li>The archive engine adds actually nothing to the mix, but disadvantages. Maybe my test case is flawed because I &#8220;only&#8221; tested with 8 million rows, but one can clearly see that a partitionated MyISAM table beats a partitionated Archive table by more than 40%, so the usage of the Archive engine gives you no advantages, but only disadvantages, like being not able to delete records or add additional indexes.</li>
<li>Apparently partitioning and indexing the column in question is slightly <em>slower</em> instead of faster, however if one tries to use a subset of a partitioned table (like restricting to <code>where c3 > date '1995-06-01' and c3 < date '1995-08-31'</code>) it <em>is</em> faster - ~0.3s with index vs ~0.7s without index.</code></li>
</ul>

<h4>Conclusion</h4>

<p>MySQL's partitioning is a great new feature in 5.1 and should be used complementary to subtle and wise indexing.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/08/04/mysql-partitioning/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Symfony development</title>
		<link>http://www.thomaskeller.biz/blog/2010/02/25/symfony-development/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/02/25/symfony-development/#comments</comments>
		<pubDate>Thu, 25 Feb 2010 11:14:49 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=562</guid>
		<description><![CDATA[Last week the second incarnation of Symfony Live came to an end and I just had the time to check a couple of shared slides from the event. Definitely interesting stuff going on there, especially the preview release of Symfony 2.0 whose code is available on GitHub since a couple of weeks and which makes [...]]]></description>
			<content:encoded><![CDATA[<p>Last week the second incarnation of Symfony Live came to an end and I just had the time to check <a href="http://www.symfony-project.org/blog/2010/02/17/symfony-live-day-1">a couple of shared slides</a> from the event.</p>

<p>Definitely interesting stuff going on there, especially the preview release of <a href="http://www.slideshare.net/fabpot/symfony-20-revealed">Symfony 2.0</a> whose code is available on <a href="http://github.com/symfony/symfony">GitHub</a> since a couple of weeks and which makes major changes to the &#8220;good old way&#8221; one used to write symfony applications (actions are now &#8220;controllers&#8221; and extensions &#8220;bundles&#8221; and well, a dozen of other things changed as well of course&#8230; you can read everything in detail <a href="http://symfony-reloaded.org/learn">here</a>).</p>

<p>Also, <a href="http://www.slideshare.net/jwage/doctrine-2-not-the-same-old-php-orm">Doctrine 2.0</a> seems to be the first PHP ORM which decouples the modelling approach from the actual database abstraction layer, skips the need for base classes and enables the model definition via annotations. Also, they seem to fight against the overly complex magic from Doctrine 1.x (one of my top complaints on Doctrine in comparison to, f.e. Propel) &#8211; maybe I&#8217;ll revisit Doctrine again when the next version gets stable.</p>

<p>The guys at Sensio labs do really have a fast development pace and I get more and more the impression that the Symfony ecosystem is the major competitor for the Zend framework. Community-wise I think Symfony is already much bigger than any other PHP framework.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/02/25/symfony-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Never trust doctrine:data-dump&#8230;</title>
		<link>http://www.thomaskeller.biz/blog/2010/01/29/never-trust-doctrinedata-dump/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/01/29/never-trust-doctrinedata-dump/#comments</comments>
		<pubDate>Fri, 29 Jan 2010 16:52:33 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=537</guid>
		<description><![CDATA[&#8230;and especially not if you get the impression that the dump will afterwards be readable by the doctrine:data-load command of symfony. It was a costly lesson today when I tried to reimport a dump of a couple of Sympal tables. One of them, the one which models the menu items, has a nested set behaviour, [...]]]></description>
			<content:encoded><![CDATA[<p>&#8230;and especially not if you get the impression that the dump will afterwards be readable by the <code>doctrine:data-load</code> command of <a href="http://www.symfony-project.org">symfony</a>.</p>

<p>It was a costly lesson today when I tried to reimport a dump of a couple of <a href="http://www.sympalphp.org">Sympal</a> tables. One of them, the one which models the menu items, has a nested set behaviour, and apparently this one cannot be restored properly by <a href="http://www.doctrine-project.org">doctrine</a>:</p>

<p><pre>[Doctrine_Record_UnknownPropertyException]<br />
  Unknown record property / related component "children" 
  on "sfSympalMenuItem"</pre></p>

<p>Apparently this particular issue popped up a couple of times in the past for other people as well (<a href="http://www.google.com/search?q=doctrine+data-load+%22nested+set%22">Google for it</a>) and while the help of <code>doctrine:data-dump</code> still (Doctrine 1.2) blatantly states</p>

<pre><code>The doctrine:data-dump task dumps database data:

 ./symfony doctrine:data-dump

The task dumps the database data in data/fixtures/%target%.

The dump file is in the YML format and can be reimported
by using the doctrine:data-load task.

 ./symfony doctrine:data-load
</code></pre>

<p><small>(with the emphasis of &#8220;can be reimported&#8221;)</small></p>

<p>the author of Doctrine, Jonathan Wage, told me today on Sympal&#8217;s IRC (shortened):</p>

<blockquote>
&lt;jonwage> we don&#8217;t want people to think you can dump and then restore<br />
&lt;jonwage> that is not what the data fixtures are for<br />
&lt;jonwage> b/c dumping and then loading will never work<br />
&lt;jonwage> an ORM modifies data on the way and and the way out<br />
&lt;me> I mean the least thing doctrine could do there is that if it detects the nested set behaviour it should error out clearly on dump<br />
&lt;jonwage> so you can&#8217;t dump the data through an ORM and then try and reload it<br />
&lt;jonwage> i.e. hashed passwords<br />
&lt;me> if dumping is &#8220;never&#8221; going to work &#8211; why do you support dumping into yaml at all?!<br />
&lt;jonwage> if we do that then we would have to throw errors in sooooooo many other cases too<br />
&lt;jonwage> because it is at least a little bit of a convenience<br />
&lt;me> its like a half-baked feature then<br />
&lt;jonwage> we dump the raw data<br />
&lt;jonwage> and you can tweak it<br />
&lt;jonwage> thats my point though, it will ALWAYS be a half baked feature thats why we document it that way<br />
&lt;jonwage> it can NEVER work 100% the way you want it to<br />
&lt;jonwage> so if we fix that one thing, a million other things will be reported that we cannot fix<br />
&lt;jonwage> bc an ORM is not a backup and restore tool<br />
&lt;jonwage> it is impossible<br />
</blockquote>

<p>Now I know that as well. My only problem was that I struggled &#8220;what is wrong with my fixtures&#8221; the whole time and never dared to ask &#8220;what is wrong with doctrine&#8221;&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/01/29/never-trust-doctrinedata-dump/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Tip: Logging with Symfony &gt;= 1.2</title>
		<link>http://www.thomaskeller.biz/blog/2010/01/25/tip-logging-with-symfony-1-2/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/01/25/tip-logging-with-symfony-1-2/#comments</comments>
		<pubDate>Mon, 25 Jan 2010 15:00:31 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=529</guid>
		<description><![CDATA[Imagine you have a business method in your model which needs to be accessed by two environments: once from a symfony task and once from the web. So far so good, now what if this business method should be able to log contents somewhere visibly, in case of the command line task to console and [...]]]></description>
			<content:encoded><![CDATA[<p>Imagine you have a business method in your model which needs to be accessed by two environments: once from a symfony task and once from the web. So far so good, now what if this business method should be able to log contents somewhere visibly, in case of the command line task to console and to a file and in case of the web application to the default logging mechanisms used there?</p>

<p>Getting the logger in web context is easy, all you have to do is</p>

<div class="geshi no php"><ol><li class="li1"><div class="de1"><span class="re1">$logger</span> <span class="sy0">=</span> sfContext<span class="sy0">::</span><span class="me2">getInstance</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getLogger</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li></ol></div>

<p>but its a little harder to do for the command line task.</p>

<p>By default no symfony context is created for a command line task and even if it is created, the above call returns an instance of <code>sfNoLogger</code>. Logging in command applications happens through the <code>sfTask::logSection()</code> method, which basically throws an event at the created dispatcher in <code>SYMFONYDIR/lib/command/cli.php</code>. There you can also see that an instance of <code>sfCommandLogger</code> is created, but there is no way to get your fingers at this instance, because its purely local.</p>

<p>So what can we do? Parametricizing the business method with the <code>sfTask</code> instance and using the <code>logSection()</code> is obviously no solution, because this would break in web context where no such sfTask instance exists&#8230;</p>

<p>My solution was a bit more straight forward &#8211; I simply decided to not use the task-supplied logging schema at all, but created my own logger like this:</p>

<div class="geshi no php"><ol><li class="li1"><div class="de1"><span class="re1">$dispatcher</span> <span class="sy0">=</span> <span class="kw2">new</span> sfEventDispatcher<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="re1">$logger</span> <span class="sy0">=</span> <span class="kw2">new</span> sfAggregateLogger<span class="br0">&#40;</span><span class="re1">$dispatcher</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="re1">$logger</span><span class="sy0">-&gt;</span><span class="me1">addLogger</span><span class="br0">&#40;</span><span class="kw2">new</span> sfCommandLogger<span class="br0">&#40;</span><span class="re1">$dispatcher</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// optionally add another file logger</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">if</span> <span class="br0">&#40;</span><span class="re1">$logToFile</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="re1">$logger</span><span class="sy0">-&gt;</span><span class="me1">addLogger</span><span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">new</span> sfFileLogger<span class="br0">&#40;</span><span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">dispatcher</span><span class="sy0">,</span> <span class="sy0">&#8230;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span></div></li></ol></div>

<p>Hope this helps somebody.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/01/25/tip-logging-with-symfony-1-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Doctrine Horror</title>
		<link>http://www.thomaskeller.biz/blog/2010/01/15/doctrine-horror/</link>
		<comments>http://www.thomaskeller.biz/blog/2010/01/15/doctrine-horror/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 13:47:30 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Rants]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=523</guid>
		<description><![CDATA[My latest Symfony project uses Doctrine as ORM, which is considered to be a lot better than Propel by many people&#8230; Well, not by me. Doctrine seems to have a couple of very good concepts, amongst them built-in validators, a powerful query language, and last but not least, an easy schema language. (Though to be [...]]]></description>
			<content:encoded><![CDATA[<p>My latest <a href="http://www.symfony-project.com">Symfony</a> project uses <a href="http://www.doctrine-project.org">Doctrine</a> as ORM, which is considered to be a lot better than <a href="http://propel.phpdb.org">Propel</a> by many people&#8230;</p>

<p>Well, not by me. Doctrine seems to have a couple of very good concepts, amongst them built-in validators, a powerful query language, and last but not least, an easy schema language. (Though to be fair, Propel will gain most of these useful things in the future as well or already has, f.e. with its <code>PropelQuery</code> feature.)</p>

<p>But Doctrine also fails in many areas; the massive use of overloads everywhere makes it very hard to debug and even worse, it tries to outsmart you (the developer) in many areas, which makes it even more hard to debug stuff which Doctrine doesn&#8217;t get right.</p>

<p>A simple example &#8211; consider this schema:</p>

<div class="geshi no yaml"><ol><li class="li1"><div class="de1">Foo:
</div></li>
<li class="li1"><div class="de1">&nbsp; columns:
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;id: { type: integer(5), primary: true, autoincrement: true }
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;name: { type: string }
</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">Bar:
</div></li>
<li class="li1"><div class="de1">&nbsp; columns:
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;id: { type: integer(5), primary: true, autoincrement: true }
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;name: { type: string }
</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">FooBarBaz:
</div></li>
<li class="li1"><div class="de1">&nbsp; columns:
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;foo_id: { type: integer(5), primary: true }
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;bar_id: { type: integer(5), primary: true }
</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp;name: { type: string }</div></li></ol></div>

<p>(I&#8217;ll skip the relation setup here, Doctrine should find them all with an additional <code>detect_relations: true</code>)</p>

<p>So what do you expect you see when you call this?</p>

<div class="geshi no php"><ol><li class="li1"><div class="de1"><span class="re1">$obj</span> <span class="sy0">=</span> <span class="kw2">new</span> FooBarBaz<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw3">print_r</span><span class="br0">&#40;</span><span class="re1">$obj</span><span class="sy0">-&gt;</span><span class="me1">toArray</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li></ol></div>

<p>Well, I expected to get an empty object, with a <code>NULL</code>ed <code>foo_id</code> and <code>bar_id</code>, but I didn&#8217;t! For me <code>foo_id</code> was filled with a 1. Wait, where does this come from?</p>

<p>After I digged deep enough in Doctrine_Record, I saw that this was automatically assigned in the constructor, coming from a statically incremented <code>$_index</code> variable. I could revert this by using my own constructor and call <code>assignIdentifier()</code> like this:</p>

<div class="geshi no php"><ol><li class="li1"><div class="de1"><span class="kw2">class</span> FooBarBaz <span class="kw2">extends</span> BaseFooBarBaz </div></li>
<li class="li1"><div class="de1"><span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; parent<span class="sy0">::</span>__construct<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">assignIdentifier</span><span class="br0">&#40;</span><span class="kw2">false</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span></div></li></ol></div>

<p>but now this object could no longer be added to a <code>Doctrine_Collection</code> (which is a bummer, because if you want to extend object lists with &#8220;default&#8221; empty objects, you most likely stumble upon a Doctrine_Collection, which is the default data structure returned for every SQL query).</p>

<p>So you might ask &#8220;Why the hell does all this impose a problem for you?&#8221;</p>

<p>Well, if you work with the <code>FooForm</code> created by the doctrine plugin for you in Symfony and you want to add <code>FooBarBazForm</code> via <code>sfForm::embedFormForEach</code> a couple of times (similar to the use case <a href="http://blog.barros.ws/2009/01/01/using-embedformforeach-in-symfony-part-ii/">described here</a>), you suddenly have the problem that your embedded form for the appended new <code>FooBarBaz</code> object &#8220;magically&#8221; gets a foo_id of a wrong (maybe not existing) <code>Foo</code> object and you wonder where the heck this comes from&#8230;</p>

<p>I have my lesson learned for the last one and a half days. I promise I&#8217;ll never <em>ever</em> create a table in Doctrine with a multi-key primary key again and I&#8217;m returing back to Propel for my next project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2010/01/15/doctrine-horror/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick Tip: NetworkManager and /etc/resolv.conf</title>
		<link>http://www.thomaskeller.biz/blog/2009/11/06/quick-tip-networkmanager-and-etcresolv-conf/</link>
		<comments>http://www.thomaskeller.biz/blog/2009/11/06/quick-tip-networkmanager-and-etcresolv-conf/#comments</comments>
		<pubDate>Fri, 06 Nov 2009 09:31:08 +0000</pubDate>
		<dc:creator>Thomas Keller</dc:creator>
				<category><![CDATA[Tips]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.thomaskeller.biz/blog/?p=457</guid>
		<description><![CDATA[If you have trouble with NetworkManager overwriting your search and domain configuration after every startup and you&#8217;re using DHCP, add the following line to your /etc/dhclient.conf: append domain-name " company.local other.company.local"; So whenever your DHCP server doesn&#8217;t provide these information (the one in my company does not), it&#8217;ll add this domain company.local search company.local other.company.local [...]]]></description>
			<content:encoded><![CDATA[<p>If you have trouble with NetworkManager overwriting your <code>search</code> and <code>domain</code> configuration after every startup and you&#8217;re using DHCP, add the following line to your <code>/etc/dhclient.conf</code>:</p>

<p><pre>append domain-name " company.local other.company.local";</pre></p>

<p>So whenever your DHCP server doesn&#8217;t provide these information (the one in my company does not), it&#8217;ll add this</p>

<p><pre>
domain company.local
search company.local other.company.local
</pre></p>

<p>to your <code>/etc/resolv.conf</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thomaskeller.biz/blog/2009/11/06/quick-tip-networkmanager-and-etcresolv-conf/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

